C# - 裁剪透明/白色空间

5

我试图从一张图片中移除所有的白色或透明像素,只留下实际的图像(裁剪)。我尝试了几种解决方案,但似乎都不起作用。有什么建议吗?还是我要花整个晚上编写图像裁剪代码?


所以还有其他人梦想不断地重复编写同一行代码吗?我以为只有我一个人 :-) - Adrian Grigore
1
如果您详细说明了其中一种方法并解释了它为什么不起作用,那将有助于社区。 - Blair Conrad
6个回答

11

因此,您想要做的是找到左上角最顶部的非白色/透明像素和右下角最底部的非白色/透明像素。这两个坐标将为您提供一个矩形,您可以从中提取图像。

  // Load the bitmap
  Bitmap originalBitmap = Bitmap.FromFile("d:\\temp\\test.bmp") as Bitmap;

  // Find the min/max non-white/transparent pixels
  Point min = new Point(int.MaxValue, int.MaxValue);
  Point max = new Point(int.MinValue, int.MinValue);

  for (int x = 0; x < originalBitmap.Width; ++x)
  {
    for (int y = 0; y < originalBitmap.Height; ++y)
    {
      Color pixelColor = originalBitmap.GetPixel(x, y);
      if (!(pixelColor.R == 255 && pixelColor.G == 255 && pixelColor.B == 255)
        || pixelColor.A < 255)
      {
        if (x < min.X) min.X = x;
        if (y < min.Y) min.Y = y;

        if (x > max.X) max.X = x;
        if (y > max.Y) max.Y = y;
      }
    }
  }

  // Create a new bitmap from the crop rectangle
  Rectangle cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X, max.Y - min.Y);
  Bitmap newBitmap = new Bitmap(cropRectangle.Width, cropRectangle.Height);
  using (Graphics g = Graphics.FromImage(newBitmap))
  {
    g.DrawImage(originalBitmap, 0, 0, cropRectangle, GraphicsUnit.Pixel);
  }

很棒的解决方案!我移除了 || pixelColor.A < 255 以使其正常工作。 - krlzlx
类似的注释 - 在我的情况下几乎完美地工作,我只想裁剪所有且仅裁剪透明的部分。我将整个条件语句改为只有 "if (pixelColor.A > 0)"。此时,其他所有内容都按照我想要的方式工作。 - neminem

4
public Bitmap CropBitmap(Bitmap original)
{
    // determine new left
    int newLeft = -1;
    for (int x = 0; x < original.Width; x++)
    {
        for (int y = 0; y < original.Height; y++)
        {
            Color color = original.GetPixel(x, y);
            if ((color.R != 255) || (color.G != 255) || (color.B != 255) || 
                (color.A != 0))
            {
                // this pixel is either not white or not fully transparent
                newLeft = x;
                break;
            }
        }
        if (newLeft != -1)
        {
            break;
        }

        // repeat logic for new right, top and bottom

    }

    Bitmap ret = new Bitmap(newRight - newLeft, newTop - newBottom);
    using (Graphics g = Graphics.FromImage(ret)
    {
        // copy from the original onto the new, using the new coordinates as
        // source coordinates for the original
        g.DrawImage(...);
    }

    return ret
}

请注意,这个函数会非常慢。使用GetPixel()会非常缓慢,并且在循环中访问BitmapWidthHeight属性也很慢。使用LockBits是正确的方法 - 在StackOverflow上有大量的示例。

1
谢谢,这让我完成了一半。唯一的问题是,如果图像大于指定大小,我还想调整图像大小。代码比注释框中可以容纳的要多,但原始图像仅占最终图像的约一半。 代码位于http://pastebin.com/He3S8aCH。 - Echilon
我调换了两个变量:P。问题已解决,很快会发布一篇博客文章(将在此处链接)。 - Echilon
我觉得我也搞砸了。 说“color.R != 0”等的位应该实际上是说“color.R != 255”,因为白色像素的R,G和B值都为255(RGB值为0,0,0是黑色)。 如果原始版本适用于您,则可能仅裁剪了透明(和黑色)像素。 - MusiGenesis
此外,代码应该先检查A值以查看像素是否透明,然后只有在像素不透明时才检查RGB值。否则,检查将停止在任何完全透明(A = 0)的非白色像素上(尽管我怀疑这在位图中并不常见)。 - MusiGenesis

2

2

每个像素的检查应该就可以解决问题。从顶部和底部扫描每一行以找到空行,从左到右扫描每一列以找到左右约束条件(这可以在使用行或列进行一次遍历时完成)。当找到约束条件时 - 将图像的一部分复制到另一个缓冲区中。


0
我找到了一种方法,可以在大约10分钟内批量修剪几千个.jpg文件,但我没有在代码中进行操作。我使用了Snag-It编辑器的转换功能。如果您需要进行此修剪一次或您的需求是持续性的,那么我不知道这是否适用于您,但考虑到软件的价格并不高,我认为这是一个不错的解决办法。(我不工作也不代表Techsmith。)
Joey

0
此外,如果您正在使用WPF,并且图像周围有多余的空间,请检查图像的属性,并确保您的Stretch属性设置为fill。这将消除图像周围的空间。

在WPF中属性的屏幕截图


您的回答可以通过添加更多支持性信息来改进。请[编辑]以添加进一步的细节,例如引用或文档,以便他人可以确认您的答案是正确的。您可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接