- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在使用 WinForms。在我的表单中,我有一个打开按钮和一个下一步按钮。我的应用程序将 .tif
图像打开到一个图片框中。我使用的所有 .tif
图像都有多个页面。下一个按钮用于转到 tif
图像中的下一页。我使用的这些 .tif
图像非常大。
示例:尺寸:2600 x 3300(.tif
图像)
问题:如何优化应用程序的性能?我已经阅读/研究过我可能必须直接从计算机内存和其他一些方法加载图像。我将如何解决这个问题,或者是否有更好的编码方法?
这是我目前的代码,但是当我转到下一页时我的应用程序有点滞后。
下面是一个包含多个用于测试的页面的大型 TIFF 图像的链接。
链接
http://www.filedropper.com/tiftestingdoc
FileStream _stream;
Image _myImg; // setting the selected tiff
string _fileName;
private Image _Source = null;
private int _TotalPages = 0;
private int intCurrPage = 0;
private void Clone_File()
{ // Reads file, then copys the file and loads it in the picture box as a temporary image doc. That way files are not locked in users directory when in use by this application.
try
{
if (_myImg == null)
{
try
{
_fileName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
File.Copy(@"C:\Picture_Doc\The_Image.tif", _fileName);
_stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read);
this._Source = Image.FromStream(_stream);
}
catch (Exception ex)
{
}
}
_TotalPages = _Source.GetFrameCount(System.Drawing.Imaging.FrameDimension.Page);
intCurrPage = 1;
Display_Page(intCurrPage);
}catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Show_Processing_Image_Label()
{
Application.DoEvents();
}
private void Display_Page(int PageNumber, RotateFlipType Change)
{
if (pictureBox1.Image != null && pictureBox1.Image != _Source)
{
//Release memory for old rotated image
pictureBox1.Image.Dispose();
}
// set the variable to null for easy Garbage Collection cleanup
pictureBox1.Image = null;
_Source.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, PageNumber - 1);
pictureBox1.Image = new Bitmap(_Source);
pictureBox1.Image.RotateFlip(Change);
pictureBox1.Refresh();
//Refresh() Calls Invalidate and then Update to refresh synchronously.
}
private void Display_Page(int PageNumber)
{
Show_Processing_Image_Label();
//You could adjust the PictureBox size here for each frame OR adjust the image to fit the picturebox nicely
if (pictureBox1.Image != _Source)
{
if (pictureBox1.Image != null)
{
//Release memory for old copy and set the variable to null for easy GC cleanup
pictureBox1.Image.Dispose();
pictureBox1.Image = null;
}
pictureBox1.Image = _Source;
}
pictureBox1.Image.SelectActiveFrame(System.Drawing.Imaging.FrameDimension.Page, PageNumber - 1);
pictureBox1.Refresh();
}
private void Next_btn_Click(object sender, EventArgs e)
{
intCurrPage++;
Display_Page(intCurrPage);
}
private void Open_btn_Click(object sender, EventArgs e)
{
if (_stream != null)
{
_myImg = null; //dispose the copy image
}
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
Clone_File();
}
pictureBox1.Size = new Size(850, 1100);
}
最佳答案
原来慢的部分是Image.SelectActiveFrame称呼。
像往常一样,解决方案是缓存。但是,为了不增加初始加载时间,应该在后台延迟执行。
这个想法很简单。启动工作线程并将所有图像帧作为单独的 Bitmap
加载到数组中。然后使用数组中的缓存图像代替 SelectActiveFrame
。
因为所有这些都需要一些线程同步,所以我将它封装在一个辅助类中:
class PageBuffer : IDisposable
{
public static PageBuffer Open(string path)
{
return new PageBuffer(File.OpenRead(path));
}
private PageBuffer(Stream stream)
{
this.stream = stream;
Source = Image.FromStream(stream);
PageCount = Source.GetFrameCount(FrameDimension.Page);
if (PageCount < 2) return;
pages = new Image[PageCount];
var worker = new Thread(LoadPages) { IsBackground = true };
worker.Start();
}
private void LoadPages()
{
for (int index = 0; ; index++)
{
lock (syncLock)
{
if (disposed) return;
if (index >= pages.Length)
{
// If you don't need the source image,
// uncomment the following line to free some resources
//DisposeSource();
return;
}
if (pages[index] == null)
pages[index] = LoadPage(index);
}
}
}
private Image LoadPage(int index)
{
Source.SelectActiveFrame(FrameDimension.Page, index);
return new Bitmap(Source);
}
private Stream stream;
private Image[] pages;
private object syncLock = new object();
private bool disposed;
public Image Source { get; private set; }
public int PageCount { get; private set; }
public Image GetPage(int index)
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
if (PageCount < 2) return Source;
var image = pages[index];
if (image == null)
{
lock (syncLock)
{
image = pages[index];
if (image == null)
image = pages[index] = LoadPage(index);
}
}
return image;
}
public void Dispose()
{
if (disposed) return;
lock (syncLock)
{
disposed = true;
if (pages != null)
{
foreach (var item in pages)
if (item != null) item.Dispose();
pages = null;
}
DisposeSource();
}
}
private void DisposeSource()
{
if (Source != null)
{
Source.Dispose();
Source = null;
}
if (stream != null)
{
stream.Dispose();
stream = null;
}
}
}
一个完整的工作演示:
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Forms;
namespace Demo
{
class TestForm : Form
{
public TestForm()
{
var panel = new Panel { Dock = DockStyle.Top, BorderStyle = BorderStyle.FixedSingle };
openButton = new Button { Text = "Open", Top = 8, Left = 16 };
prevButton = new Button { Text = "Prev", Top = 8, Left = 16 + openButton.Right };
nextButton = new Button { Text = "Next", Top = 8, Left = 16 + prevButton.Right };
panel.Height = 16 + openButton.Height;
panel.Controls.AddRange(new Control[] { openButton, prevButton, nextButton });
pageViewer = new PictureBox { Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom };
ClientSize = new Size(850, 1100 + panel.Height);
Controls.AddRange(new Control[] { panel, pageViewer });
openButton.Click += OnOpenButtonClick;
prevButton.Click += OnPrevButtonClick;
nextButton.Click += OnNextButtonClick;
Disposed += OnFormDisposed;
UpdatePageInfo();
}
private Button openButton;
private Button prevButton;
private Button nextButton;
private PictureBox pageViewer;
private PageBuffer pageData;
private int currentPage;
private void OnOpenButtonClick(object sender, EventArgs e)
{
using (var dialog = new OpenFileDialog())
{
if (dialog.ShowDialog(this) == DialogResult.OK)
Open(dialog.FileName);
}
}
private void OnPrevButtonClick(object sender, EventArgs e)
{
SelectPage(currentPage - 1);
}
private void OnNextButtonClick(object sender, EventArgs e)
{
SelectPage(currentPage + 1);
}
private void OnFormDisposed(object sender, EventArgs e)
{
if (pageData != null)
pageData.Dispose();
}
private void Open(string path)
{
var data = PageBuffer.Open(path);
pageViewer.Image = null;
if (pageData != null)
pageData.Dispose();
pageData = data;
SelectPage(0);
}
private void SelectPage(int index)
{
pageViewer.Image = pageData.GetPage(index);
currentPage = index;
UpdatePageInfo();
}
private void UpdatePageInfo()
{
prevButton.Enabled = pageData != null && currentPage > 0;
nextButton.Enabled = pageData != null && currentPage < pageData.PageCount - 1;
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
class PageBuffer : IDisposable
{
public static PageBuffer Open(string path)
{
return new PageBuffer(File.OpenRead(path));
}
private PageBuffer(Stream stream)
{
this.stream = stream;
Source = Image.FromStream(stream);
PageCount = Source.GetFrameCount(FrameDimension.Page);
if (PageCount < 2) return;
pages = new Image[PageCount];
var worker = new Thread(LoadPages) { IsBackground = true };
worker.Start();
}
private void LoadPages()
{
for (int index = 0; ; index++)
{
lock (syncLock)
{
if (disposed) return;
if (index >= pages.Length)
{
// If you don't need the source image,
// uncomment the following line to free some resources
//DisposeSource();
return;
}
if (pages[index] == null)
pages[index] = LoadPage(index);
}
}
}
private Image LoadPage(int index)
{
Source.SelectActiveFrame(FrameDimension.Page, index);
return new Bitmap(Source);
}
private Stream stream;
private Image[] pages;
private object syncLock = new object();
private bool disposed;
public Image Source { get; private set; }
public int PageCount { get; private set; }
public Image GetPage(int index)
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
if (PageCount < 2) return Source;
var image = pages[index];
if (image == null)
{
lock (syncLock)
{
image = pages[index];
if (image == null)
image = pages[index] = LoadPage(index);
}
}
return image;
}
public void Dispose()
{
if (disposed) return;
lock (syncLock)
{
disposed = true;
if (pages != null)
{
foreach (var item in pages)
if (item != null) item.Dispose();
pages = null;
}
DisposeSource();
}
}
private void DisposeSource()
{
if (Source != null)
{
Source.Dispose();
Source = null;
}
if (stream != null)
{
stream.Dispose();
stream = null;
}
}
}
}
更新: 正如评论中提到的,上面的实现使用了非常简单的贪心缓存策略,它使用大量内存并且不适用于大文件。
但好处是,一旦逻辑封装在类中,我们就可以更改策略而无需触及我们的应用程序代码。例如,我们可以完全删除缓存(返回到初始状态),或者通过像这样维护一小组缓存图像“窗口”来优化“上一个/下一个”导航
class PageBuffer : IDisposable
{
public const int DefaultCacheSize = 5;
public static PageBuffer Open(string path, int cacheSize = DefaultCacheSize)
{
return new PageBuffer(File.OpenRead(path), cacheSize);
}
private PageBuffer(Stream stream, int cacheSize)
{
this.stream = stream;
source = Image.FromStream(stream);
pageCount = source.GetFrameCount(FrameDimension.Page);
if (pageCount < 2) return;
pageCache = new Image[Math.Min(pageCount, Math.Max(cacheSize, 3))];
var worker = new Thread(LoadPages) { IsBackground = true };
worker.Start();
}
private void LoadPages()
{
while (true)
{
lock (syncLock)
{
if (disposed) return;
int index = Array.FindIndex(pageCache, 0, pageCacheSize, p => p == null);
if (index < 0)
Monitor.Wait(syncLock);
else
pageCache[index] = LoadPage(pageCacheStart + index);
}
}
}
private Image LoadPage(int index)
{
source.SelectActiveFrame(FrameDimension.Page, index);
return new Bitmap(source);
}
private Stream stream;
private Image source;
private int pageCount;
private Image[] pageCache;
private int pageCacheStart, pageCacheSize;
private object syncLock = new object();
private bool disposed;
public Image Source { get { return source; } }
public int PageCount { get { return pageCount; } }
public Image GetPage(int index)
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
if (PageCount < 2) return Source;
lock (syncLock)
{
AdjustPageCache(index);
int cacheIndex = index - pageCacheStart;
var image = pageCache[cacheIndex];
if (image == null)
image = pageCache[cacheIndex] = LoadPage(index);
return image;
}
}
private void AdjustPageCache(int pageIndex)
{
int start, end;
if ((start = pageIndex - pageCache.Length / 2) <= 0)
end = (start = 0) + pageCache.Length;
else if ((end = start + pageCache.Length) >= PageCount)
start = (end = PageCount) - pageCache.Length;
if (start < pageCacheStart)
{
int shift = pageCacheStart - start;
if (shift >= pageCacheSize)
ClearPageCache(0, pageCacheSize);
else
{
ClearPageCache(pageCacheSize - shift, pageCacheSize);
for (int j = pageCacheSize - 1, i = j - shift; i >= 0; j--, i--)
Exchange(ref pageCache[i], ref pageCache[j]);
}
}
else if (start > pageCacheStart)
{
int shift = start - pageCacheStart;
if (shift >= pageCacheSize)
ClearPageCache(0, pageCacheSize);
else
{
ClearPageCache(0, shift);
for (int j = 0, i = shift; i < pageCacheSize; j++, i++)
Exchange(ref pageCache[i], ref pageCache[j]);
}
}
if (pageCacheStart != start || pageCacheStart + pageCacheSize != end)
{
pageCacheStart = start;
pageCacheSize = end - start;
Monitor.Pulse(syncLock);
}
}
void ClearPageCache(int start, int end)
{
for (int i = start; i < end; i++)
Dispose(ref pageCache[i]);
}
static void Dispose<T>(ref T target) where T : class, IDisposable
{
var value = target;
if (value != null) value.Dispose();
target = null;
}
static void Exchange<T>(ref T a, ref T b) { var c = a; a = b; b = c; }
public void Dispose()
{
if (disposed) return;
lock (syncLock)
{
disposed = true;
if (pageCache != null)
{
ClearPageCache(0, pageCacheSize);
pageCache = null;
}
Dispose(ref source);
Dispose(ref stream);
if (pageCount > 2)
Monitor.Pulse(syncLock);
}
}
}
或实现其他“智能”缓存策略。我们甚至可以通过实现 Strategy pattern 来使策略可选择.
但那将是另一个故事。第二个 PageBuffer
实现应该足以满足 OP 用例。
关于c# - 使用 .tif 图像前进到下一页时提高性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35510498/
我正在尝试获取多个多页 .tif 文件并将它们组合成一个多页 tif 文件。 我在这个 question 中找到了一些代码, 但它似乎只占用每个单独的 .tif 文件的第一页,并用这些第一页创建新的多
我有很多 tif 文件,我想转换成一个。我在 Ubuntu 11.04 上,我该怎么做?我尝试将 tif 转换为单个 pdf,但是在将 pdf 转换为 tif 时卡住了,我使用 ghoscript 和
我需要分析在一个 tif 文件中选作子矩阵的图像的一部分。我想要原始格式的图像,没有多余的装饰(缩放、轴、标签等)...我该怎么做? 这是我现在使用的代码: submatrix = im[x_min
我的问题是,如何才能将 .tif 文件成功加载到 Java 中的 Image 实例中? 现在让我详细介绍一下。我已经阅读了很多关于如何在 Java 中处理/转换 TIF 图像的 stackoverfl
我需要在客户端解析 tiff 文件,因此使用一些 JQuery 库、JS 或 HTML5。我已经搜索过,但找不到我需要的东西。我无法使用任何服务器端技术或浏览器插件。我尝试过 tiff.js http
这个问题已经有答案了: Can't read and write a TIFF image file using Java ImageIO standard library (5 个回答) 已关闭 4
我可以使用以下代码片段成功打印 .GIF、.JPG 或 .PNG,但它不适用于 .TIF 文件。即使添加了 chromaticity.color 属性后,我也无法获取颜色。 public class
我有 tiff 文件,我想获取页数。我已被关注this问题,但我无法获得页数。没有错误,我尝试调试问题但找不到原因。 import java.io.File; import java.io.IOExc
Python - 将两个 TIF 文件附加到特定文件 我的要求是将两个单分页器 TIF 文件附加到具有两页的单个 TIF 文件中。我知道存在append_images参数并尝试按如下方式实现它: im
我尝试打开每像素 16 位和多波段的 tif 图像,将其转换为原始文件。我在接下来的命令 i = Image.open('image.tif') 和使用 rawData = i.tostring()
我正在尝试从 tif 文件中清除一些属性标签项。 我的测试代码是: Image sourceImg = new Bitmap("A10034.tif"); Image img = (Image)sou
我正在尝试读取一些图像文件 jpg、tif、gif、png 并且需要保存文件和创建图标。我收到 UnsupportedTypeException。 ImageIO.read(file); 如果我使用下
例如,一个 400*200 大小的 .tiff 文件,我可以在 python 中将其读取为二维数组 (400 x 200)。 我想将 tiff 大小更改为 200 x 100 或其他比例。 如何在 P
我正在尝试对与 .tif 图像关联的相关文件进行分组。从列表中可以看到,每组有 7 个相关文件。我正在寻找一种对这些文件进行分组的方法,以便我可以通过 shutil.move() 将它们移动到各个文件
我的 tif 文件的索引为 8 bpp,并与颜色图一起保存。 有什么方法可以从 C# 文件中恢复该颜色图? 默认情况下,picturebox 会自动显示这样的颜色图,最左边和最右边分别代表 0 和 2
我为 Windows 资源管理器创建了一个缩略图图像处理程序(shell 扩展),用于处理多种 TIF/TIFF 图像格式。我知道 Explorer 已经包含 TIFF 支持(由 Windows XP
我有像素数据,我想用它来创建具有多个帧的新 .tif 图像。我该怎么做呢?我已经尝试过 python PIL,但是我只发现它支持多帧读取而不是写入。请参阅下文,了解我的失败尝试。 new_Image
我正在使用 WinForms。在我的表单中,我有一个打开按钮和一个下一步按钮。我的应用程序将 .tif 图像打开到一个图片框中。我使用的所有 .tif 图像都有多个页面。下一个按钮用于转到 tif 图
在我作为研究生的工作中,我捕捉显微镜图像并使用 python 将它们保存为原始 tif。我想添加元数据,例如我正在使用的显微镜的名称、放大倍数和成像激光波长。这些细节对于我如何对图像进行后处理都很
我有一个 14406x9606 像素的 8 位 tiff,当通过 BitmapImage 加载时会抛出 System.OutOfMemoryException。作为全深度位图,其大小约为 400 兆。
我是一名优秀的程序员,十分优秀!