gpt4 book ai didi

c# - 使用 .tif 图像前进到下一页时提高性能

转载 作者:太空狗 更新时间:2023-10-29 21:56:54 24 4
gpt4 key购买 nike

我正在使用 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);

}

enter image description here

最佳答案

原来慢的部分是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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com