C#中的大,奇比图像调整大小

Oli*_*rdt 7 c# graphics interpolation resize image

我有一个特殊的问题,我需要帮助.我正在处理复杂的蛋白质组学数据,我们的一个图表涉及原始数据的热图.这些热图我计算为原始图像,然后我调整大小以适合我的图表画布.当涉及宽度与高度时,以这种方式生成的图像文件通常非常不平衡.通常,这些图像的宽度大约为10到100像素,高度为5000到8000像素(这是我必须转换为图像的原始2D数据阵列的大小).之后的目标分辨率将是1300 x 600像素.

我通常使用此功能将图像大小调整为目标大小

public static Image Resize(Image img, int width, int height) {
   Bitmap bmp = new Bitmap(width, height);
   Graphics graphic = Graphics.FromImage((Image)bmp);
   graphic.InterpolationMode = InterpolationMode.NearestNeighbor;
   graphic.PixelOffsetMode = PixelOffsetMode.Half;


   graphic.DrawImage(img, 0, 0, width, height);
   graphic.Dispose();

   return (Image)bmp;
}
Run Code Online (Sandbox Code Playgroud)

这通常适用于上述尺寸.但现在我有了一个尺寸为6 x 54343像素的新数据集.在此图像上使用相同的代码时,已调整大小的图像为半空白.

原始图片:http: //files.biognosys.ch/FileSharing/20170427_StackOverflow/raw.png

(原始图像在大多数浏览器中无法正常显示,因此请使用"将链接另存为...")

应该如何看待(使用photoshop):http: //files.biognosys.ch/FileSharing/20170427_StackOverflow/photoshop_resize.png

当我使用上面的代码时,它的外观如何剪切 http://files.biognosys.ch/FileSharing/20170427_StackOverflow/code_resized.png

请记住,对于6 x 8000的图像,这已经工作多年没有问题,所以我想我在这里没有做任何根本性的错误.同样重要的是我有调整大小的NearestNeighbor插值,因此任何涉及其他插值的解决方案都不会导致"如何看"图像最终对我没用.

奥利

Ser*_*gGr 6

看起来你已经从16位Windows时代遇到了一些遗留限制.解决这个问题的一个显而易见的方法是使用内存操作将源映像预分割为较小的块,然后使用调整大小来应用所有这些块Graphics.此方法假定您的源图像Bitmap不仅仅是公正,Image但这似乎不是对您的限制.这是代码的草图:

[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = true)]
public static extern void CopyMemoryUnmanaged(IntPtr dest, IntPtr src, int count);

// in case you can't use P/Invoke, copy via intermediate .Net buffer        
static void CopyMemoryNet(IntPtr dst, IntPtr src, int count)
{
    byte[] buffer = new byte[count];
    Marshal.Copy(src, buffer, 0, count);
    Marshal.Copy(buffer, 0, dst, count);
}

static Image CopyImagePart(Bitmap srcImg, int startH, int endH)
{
    var width = srcImg.Width;
    var height = endH - startH;
    var srcBitmapData = srcImg.LockBits(new Rectangle(0, startH, width, height), ImageLockMode.ReadOnly, srcImg.PixelFormat);

    var dstImg = new Bitmap(width, height, srcImg.PixelFormat);
    var dstBitmapData = dstImg.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, srcImg.PixelFormat);

    int bytesCount = Math.Abs(srcBitmapData.Stride) * height;
    CopyMemoryUnmanaged(dstBitmapData.Scan0, srcBitmapData.Scan0, bytesCount);
    // in case you can't use P/Invoke, copy via intermediate .Net buffer        
    //CopyMemoryNet(dstBitmapData.Scan0, srcBitmapData.Scan0, bytesCount);

    srcImg.UnlockBits(srcBitmapData);
    dstImg.UnlockBits(dstBitmapData);

    return dstImg;
}


public static Image ResizeInParts(Bitmap srcBmp, int width, int height)
{
    int srcStep = srcBmp.Height;
    int dstStep = height;
    while (srcStep > 30000)
    {
        srcStep /= 2;
        dstStep /= 2;
    }

    var resBmp = new Bitmap(width, height);
    using (Graphics graphic = Graphics.FromImage(resBmp))
    {
        graphic.InterpolationMode = InterpolationMode.NearestNeighbor;
        graphic.PixelOffsetMode = PixelOffsetMode.Half;


        for (int srcTop = 0, dstTop = 0; srcTop < srcBmp.Height; srcTop += srcStep, dstTop += dstStep)
        {
            int srcBottom = srcTop + srcStep;
            int dstH = dstStep;
            if (srcBottom > srcBmp.Height)
            {
                srcBottom = srcBmp.Height;
                dstH = height - dstTop;
            }
            using (var imgPart = CopyImagePart(srcBmp, srcTop, srcBottom))
            {
                graphic.DrawImage(imgPart, 0, dstTop, width, dstH);
            }
        }
    }

    return resBmp;
}
Run Code Online (Sandbox Code Playgroud)

以下是我为您的示例图像获取的内容: 调整大小的图像

它与你的不一样,photoshop_resize.png但与你的相似code_resized.png

可以改进此代码以更好地处理各种"边缘",例如srcBmp.Height不均匀的情况或不同部分之间的边缘(边缘上的像素仅使用它们应该的一半像素进行插值)但是这不容易做到而不假设源和调整大小的图像的一些"好"大小或自己重新实现插值逻辑.考虑到缩放因子,这段代码可能已经足够好用于您的使用.