当前位置:  开发笔记 > 编程语言 > 正文

从图像中删除周围的空白

如何解决《从图像中删除周围的空白》经验,为你挑选了5个好方法。

我有一块从客户那里收到的产品图片.每个产品图像是一个东西的图片,它是用白色背景拍摄的.我想裁剪图像的所有周围部分,但只留下产品在中间.这可能吗?

举个例子:[ http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg] [1 ]

我不希望删除所有白色像素,但是我希望裁剪图像,使最顶部的像素行包含一个非白色像素,最左边的垂直像素行包含一个非白色像素,底部 - 大多数水平像素行包含一个非白色像素等.

C#或VB.net中的代码将不胜感激.



1> Darren..:

我发现我必须调整Dmitri的答案,以确保它适用于实际上不需要裁剪的图像(水平,垂直或两者)......

    public static Bitmap Crop(Bitmap bmp)
    {
        int w = bmp.Width;
        int h = bmp.Height;

        Func allWhiteRow = row =>
        {
            for (int i = 0; i < w; ++i)
                if (bmp.GetPixel(i, row).R != 255)
                    return false;
            return true;
        };

        Func allWhiteColumn = col =>
        {
            for (int i = 0; i < h; ++i)
                if (bmp.GetPixel(col, i).R != 255)
                    return false;
            return true;
        };

        int topmost = 0;
        for (int row = 0; row < h; ++row)
        {
            if (allWhiteRow(row))
                topmost = row;
            else break;
        }

        int bottommost = 0;
        for (int row = h - 1; row >= 0; --row)
        {
            if (allWhiteRow(row))
                bottommost = row;
            else break;
        }

        int leftmost = 0, rightmost = 0;
        for (int col = 0; col < w; ++col)
        {
            if (allWhiteColumn(col))
                leftmost = col;
            else
                break;
        }

        for (int col = w - 1; col >= 0; --col)
        {
            if (allWhiteColumn(col))
                rightmost = col;
            else
                break;
        }

        if (rightmost == 0) rightmost = w; // As reached left
        if (bottommost == 0) bottommost = h; // As reached top.

        int croppedWidth = rightmost - leftmost;
        int croppedHeight = bottommost - topmost;

        if (croppedWidth == 0) // No border on left or right
        {
            leftmost = 0;
            croppedWidth = w;
        }

        if (croppedHeight == 0) // No border on top or bottom
        {
            topmost = 0;
            croppedHeight = h;
        }

        try
        {
            var target = new Bitmap(croppedWidth, croppedHeight);
            using (Graphics g = Graphics.FromImage(target))
            {
                g.DrawImage(bmp,
                  new RectangleF(0, 0, croppedWidth, croppedHeight),
                  new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
                  GraphicsUnit.Pixel);
            }
            return target;
        }
        catch (Exception ex)
        {
            throw new Exception(
              string.Format("Values are topmost={0} btm={1} left={2} right={3} cropped, topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight),
              ex);
        }
    }


对于具有'透明空白'的PNG文件,我添加了对bmp.GetPixel(i,row)的检查.A!= 0和bmp.GetPixel(col,i).A!= 0(alpha级别为零).现在我的PNG工作得很好.谢谢你的代码!注意:我运行bmp.GetPixel()一次然后分析R和A属性以防止双重扫描.

2> Dmitri Neste..:

这是我(相当冗长)的解决方案:

public Bitmap Crop(Bitmap bmp)
{
  int w = bmp.Width, h = bmp.Height;

  Func allWhiteRow = row =>
  {
    for (int i = 0; i < w; ++i)
      if (bmp.GetPixel(i, row).R != 255)
        return false;
    return true;
  };

  Func allWhiteColumn = col =>
  {
    for (int i = 0; i < h; ++i)
      if (bmp.GetPixel(col, i).R != 255)
        return false;
    return true;
  };

  int topmost = 0;
  for (int row = 0; row < h; ++row)
  {
    if (allWhiteRow(row))
      topmost = row;
    else break;
  }

  int bottommost = 0;
  for (int row = h - 1; row >= 0; --row)
  {
    if (allWhiteRow(row))
      bottommost = row;
    else break;
  }

  int leftmost = 0, rightmost = 0;
  for (int col = 0; col < w; ++col)
  {
    if (allWhiteColumn(col))
      leftmost = col;
    else
      break;
  }

  for (int col = w-1; col >= 0; --col)
  {
    if (allWhiteColumn(col))
      rightmost = col;
    else
      break;
  }

  int croppedWidth = rightmost - leftmost;
  int croppedHeight = bottommost - topmost;
  try
  {
    Bitmap target = new Bitmap(croppedWidth, croppedHeight);
    using (Graphics g = Graphics.FromImage(target))
    {
      g.DrawImage(bmp,
        new RectangleF(0, 0, croppedWidth, croppedHeight),
        new RectangleF(leftmost, topmost, croppedWidth, croppedHeight),
        GraphicsUnit.Pixel);
    }
    return target;
  }
  catch (Exception ex)
  {
    throw new Exception(
      string.Format("Values are topmost={0} btm={1} left={2} right={3}", topmost, bottommost, leftmost, rightmost),
      ex);
  }
}



3> Brian Hasden..:

我需要一个适用于大图像的解决方案(GetPixel很慢),所以我在下面编写了扩展方法.它似乎在我的有限测试中运作良好.缺点是必须在项目中检查"允许不安全代码".

public static Image AutoCrop(this Bitmap bmp)
{
    if (Image.GetPixelFormatSize(bmp.PixelFormat) != 32)
        throw new InvalidOperationException("Autocrop currently only supports 32 bits per pixel images.");

    // Initialize variables
    var cropColor = Color.White;

    var bottom = 0;
    var left = bmp.Width; // Set the left crop point to the width so that the logic below will set the left value to the first non crop color pixel it comes across.
    var right = 0;
    var top = bmp.Height; // Set the top crop point to the height so that the logic below will set the top value to the first non crop color pixel it comes across.

    var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);

    unsafe
    {
        var dataPtr = (byte*)bmpData.Scan0;

        for (var y = 0; y < bmp.Height; y++)
        {
            for (var x = 0; x < bmp.Width; x++)
            {
                var rgbPtr = dataPtr + (x * 4);

                var b = rgbPtr[0];
                var g = rgbPtr[1];
                var r = rgbPtr[2];
                var a = rgbPtr[3];

                // If any of the pixel RGBA values don't match and the crop color is not transparent, or if the crop color is transparent and the pixel A value is not transparent
                if ((cropColor.A > 0 && (b != cropColor.B || g != cropColor.G || r != cropColor.R || a != cropColor.A)) || (cropColor.A == 0 && a != 0))
                {
                    if (x < left)
                        left = x;

                    if (x >= right)
                        right = x + 1;

                    if (y < top)
                        top = y;

                    if (y >= bottom)
                        bottom = y + 1;
                }
            }

            dataPtr += bmpData.Stride;
        }
    }

    bmp.UnlockBits(bmpData);

    if (left < right && top < bottom)
        return bmp.Clone(new Rectangle(left, top, right - left, bottom - top), bmp.PixelFormat);

    return null; // Entire image should be cropped, so just return null
}


如果你只想调整透明度,修改上面的"if((cropColor.A> 0 ...){"行,只需"if(a!= 0){".然后你可以摆脱cropColor变量,也是如此.到目前为止工作得很好.

4> Bevan..:

我自己编写了代码来实现这一点 - 让基础知识变得非常困难.

实质上,您需要扫描像素行/列以检查非白色像素并隔离产品图像的边界,然后创建仅包含该区域的新位图.

请注意,虽然该Bitmap.GetPixel()方法有效,但速度相对较慢.如果处理时间很重要,则需要使用Bitmap.LockBits()锁定内存中的位图,然后在unsafe { }块内使用一些简单的指针直接访问像素.

关于CodeProject的这篇文章提供了一些您可能会觉得有用的更多细节.



5> Claudiu..:

这当然是可能的.在伪代码中:

topmost = 0
for row from 0 to numRows:
    if allWhiteRow(row): 
        topmost = row
    else:
        # found first non-white row from top
        break

botmost = 0
for row from numRows-1 to 0:
    if allWhiteRow(row): 
        botmost = row
    else:
        # found first non-white row from bottom
        break

左右相似.

代码allWhiteRow将涉及查看该行中的像素并确保它们都接近 255,255,255.

推荐阅读
落单鸟人
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有