在我的项目中,我正在使用(未压缩的16位灰度)千兆像素图像,这些图像来自高分辨率扫描仪,用于测量目的。由于这些位图无法加载到内存中(主要是由于内存碎片),我使用的是瓷砖(以及光盘上的平铺TIFF)。 (见StackOverflow topic on this)
我需要以Google Maps或DeepZoom的方式实现平移/缩放。我必须在将它呈现在屏幕上之前动态应用图像处理,因此我不能使用直接访问图像文件的预先烹饪的库。对于缩放,我打算在我的文件(金字塔存储)中保留多分辨率图像。最有用的步骤似乎是+ 200%,50%并显示所有。
我的代码库目前是C#和.NET 3.5。目前我假设表格类型,除非WPF在这方面给我很大的优势。我有一个方法可以返回底层图像的任何(已处理)部分。
具体问题:
答案 0 :(得分:3)
此CodeProject article: Generate...DeepZoom Image Collection可能是一个有用的读物,因为它讨论了生成DeepZoom图像源。
此MSDN article有动态深度缩放:在运行时提供图像像素的部分,并指向此Mandelbrot Explorer的链接,其中'有点'听起来与您正在尝试的类似要做(即他按需生成mandelbrot集的特定部分;你想按需检索千兆像素图像的特定部分)。
我认为答案“可以使用DeepZoom吗?”可能是“是”,但是因为它只在Silverlight中可用,所以如果你需要一个WinForms / WPF客户端应用程序,你必须使用嵌入式Web浏览器控件做一些技巧。
抱歉,我无法提供更具体的答案 - 希望这些链接有所帮助。
P.S。我不确定Silverlight是否支持TIFF图像 - 除非您转换为其他格式,否则可能会出现问题。
答案 1 :(得分:3)
我决定亲自尝试一下。我想出了一个简单的GDI +代码,它使用了我已经获得的瓷砖。我只是过滤掉与当前剪切区域相关的部分。它就像魔法一样!请在下面找到我的代码。 (表单设置双缓冲以获得最佳效果)
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics dc = e.Graphics;
dc.ScaleTransform(1.0F, 1.0F);
Size scrollOffset = new Size(AutoScrollPosition);
int start_x = Math.Min(matrix_x_size,
(e.ClipRectangle.Left - scrollOffset.Width) / 256);
int start_y = Math.Min(matrix_y_size,
(e.ClipRectangle.Top - scrollOffset.Height) / 256);
int end_x = Math.Min(matrix_x_size,
(e.ClipRectangle.Right - scrollOffset.Width + 255) / 256);
int end_y = Math.Min(matrix_y_size,
(e.ClipRectangle.Bottom - scrollOffset.Height + 255) / 256);
// start * contain the first and last tile x/y which are on screen
// and which need to be redrawn.
// now iterate trough all tiles which need an update
for (int y = start_y; y < end_y; y++)
for (int x = start_x; x < end_x; x++)
{ // draw bitmap with gdi+ at calculated position.
dc.DrawImage(BmpMatrix[y, x],
new Point(x * 256 + scrollOffset.Width,
y * 256 + scrollOffset.Height));
}
}
为了测试它,我创建了一个80x80的256个图块(420 MPixel)的矩阵。当然,我必须在现实生活中添加一些延迟加载。如果尚未加载,我可以将瓷砖留空(空)。事实上,我已经让我的客户在他的机器上坚持使用8 GByte,所以我不必太在乎性能。加载后的瓷砖可以留在内存中。
public partial class Form1 : Form
{
bool dragging = false;
float Zoom = 1.0F;
Point lastMouse;
PointF viewPortCenter;
private readonly Brush solidYellowBrush = new SolidBrush(Color.Yellow);
private readonly Brush solidBlueBrush = new SolidBrush(Color.LightBlue);
const int matrix_x_size = 80;
const int matrix_y_size = 80;
private Bitmap[,] BmpMatrix = new Bitmap[matrix_x_size, matrix_y_size];
public Form1()
{
InitializeComponent();
Font font = new Font("Times New Roman", 10, FontStyle.Regular);
StringFormat strFormat = new StringFormat();
strFormat.Alignment = StringAlignment.Center;
strFormat.LineAlignment = StringAlignment.Center;
for (int y = 0; y < matrix_y_size; y++)
for (int x = 0; x < matrix_x_size; x++)
{
BmpMatrix[y, x] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
// BmpMatrix[y, x].Palette.Entries[0] = (x+y)%1==0?Color.Blue:Color.White;
using (Graphics g = Graphics.FromImage(BmpMatrix[y, x]))
{
g.FillRectangle(((x + y) % 2 == 0) ? solidBlueBrush : solidYellowBrush, new Rectangle(new Point(0, 0), new Size(256, 256)));
g.DrawString("hello world\n[" + x.ToString() + "," + y.ToString() + "]", new Font("Tahoma", 8), Brushes.Black,
new RectangleF(0, 0, 256, 256), strFormat);
g.DrawImage(BmpMatrix[y, x], Point.Empty);
}
}
BackColor = Color.White;
Size = new Size(300, 300);
Text = "Scroll Shapes Correct";
AutoScrollMinSize = new Size(256 * matrix_x_size, 256 * matrix_y_size);
}
原来这是容易的部分。在后台完成异步多线程i / o操作非常困难。不过,我已经按照这里描述的方式工作了。要解决的问题是与此主题相关的更多.NET / Form多线程。
在伪代码中它的工作原理如下:
after onPaint (and on Tick)
check if tiles on display need to be retrieved from disc
if so: post them to an async io queue
if not: check if tiles close to display area are already loaded
if not: post them to an async io/queue
check if bitmaps have arrived from io thread
if so: updat them on screen, and force repaint if visible
结果:我现在拥有自己的自定义控件,它使用大约50 MByte来快速访问任意大小(平铺)的TIFF文件。
答案 2 :(得分:3)
我想您可以按照以下步骤解决此问题:
图片生成:
图像可视化
最终结果与Google地图类似。