【问题标题】:Crop blank/null spaces from image从图像中裁剪空白/空空格
【发布时间】:2020-06-24 21:08:10
【问题描述】:

您好,我有一个扫描仪应用程序,我将扫描的图像返回到位图,然后显示到图片框。我有一个要求,包括自动裁剪图像并删除空白/空空格。例如,这是我在图片框上向用户展示的图像。

如您所见,扫描的图像是一张小卡片,图像是带有很多空格的全字母纸,我希望自动裁剪图像或使用按钮仅显示给用户红色边框部分。

在寻找解决方案时,我看到了一个类似的问题,我尝试使用来自 this answer 的代码,但似乎没有像我预期的那样工作。

该代码有什么问题?有没有其他方法可以让我做我想做的事?

这是我尝试过的:

public Bitmap CropImage(Bitmap bitmap)
{
    int w = bitmap.Width;
    int h = bitmap.Height;

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

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

    int leftMost = 0;
    for (int col = 0; col < w; col++)
    {
        if (IsAllWhiteColumn(col)) leftMost = col + 1;
        else break;
    }

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

    int topMost = 0;
    for (int row = 0; row < h; row++)
    {
        if (IsAllWhiteRow(row)) topMost = row + 1;
        else break;
    }

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

    if (rightMost == 0 && bottomMost == 0 && leftMost == w && topMost == h)
    {
        return bitmap;
    }

    int croppedWidth = rightMost - leftMost + 1;
    int croppedHeight = bottomMost - topMost + 1;

    try
    {
        Bitmap target = new Bitmap(croppedWidth, croppedHeight);
        using (Graphics g = Graphics.FromImage(target))
        {
            g.DrawImage(bitmap,
                    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 top={0} bottom={1} left={2} right={3}", topMost, bottomMost, leftMost, rightMost), ex);
    }
}

【问题讨论】:

  • 这里显示的图像是否完全代表您的输入?如果是这样,我可以发现一条黑色边界线,这意味着几乎没有任何行是完全白色的(好吧,或者红色,因为您只检查 R = 255)。也许您需要检查是否 > 95% 的行是白色的?
  • @SeanSkelly 你好,上图是扫描仪返回的真实尺寸,不过黑线和红线是我加的this is the original image
  • 谢谢。有趣的是,灰色的“污迹”仍然存在。这对我来说意味着您的图像来自扫描仪并不完美。你有没有遍历这些行以确保它们都是真正的白色,R 值为 255?我想知道扫描仪是否只是返回非常接近白色的像素值,例如值为 254。
  • @SeanSkelly 我知道你在说哪条灰线,但是这条线对我来说不是一个真正的问题,所以即使我没有得到完美的代码,有没有办法让这段代码工作结果,例如与我期望的结果相似 90-95%?我会试试你说的,看看有没有变化
  • 是的,例如,您可能应该修改“行或列为白色”的函数,以检查 90-95% 的像素是否在 255 的 90-95% 范围内。看来您的像素数据不是那么“纯”,您可以检查 所有 像素是否为 255。我敢打赌,它们中的大多数甚至不是 255,而是接近它。

标签: c# winforms crop wia


【解决方案1】:

如果您的图像在 R=G=B=255 的白色区域中不是真正的“纯白色”,我建议将 IsAllWhiteRow 的函数修改为更像:

int thresholdValue = 250;
double percentAllowedBelowThreshold = 0.95;

Func<int, bool> IsAllWhiteRow = row =>
{
    int numberPixelsBelowThreshold = 0;
    for (int i = 0; i < w; i++)
    {
        if (bitmap.GetPixel(i, row).R < thresholdValue)
        {
            numberPixelsBelowThreshold++;
        }
    }
    return (numberPixelsBelowThreshold / w) > percentAllowedBelowThreshold;
};

然后对列做类似的事情。 您可能需要更改阈值,具体取决于您的图像输入。例如,如果图像的真实部分中有很多白色,则可能需要 0.98 或更高的阈值!加上这段代码没有优化等等。

您还需要逐步浏览您的图片,看看我为 250 选择的值是否合理;我还没有查看位图“白色”区域中的实际 RGB 值是否存在。

【讨论】:

    猜你喜欢
    • 2018-05-15
    • 1970-01-01
    • 2010-12-12
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 2019-10-31
    • 2012-08-23
    相关资源
    最近更新 更多