C# 获取所有像素均为白色的图像行时出现问题
我试图找出图像是否从底部剪裁,如果是,那么我将从最后一个白色像素行将其分割为两个图像。下面是我创建的简单方法,用于检查剪裁并获取空的白色像素行。此外,正如您所见,这不是一个很好的解决方案。这可能会导致较大映像的性能问题。因此,如果有人能向我建议更好的方法,那将是一个很大的帮助:C# 获取所有像素均为白色的图像行时出现问题,c#,.net,image,image-processing,system.drawing,C#,.net,Image,Image Processing,System.drawing,我试图找出图像是否从底部剪裁,如果是,那么我将从最后一个白色像素行将其分割为两个图像。下面是我创建的简单方法,用于检查剪裁并获取空的白色像素行。此外,正如您所见,这不是一个很好的解决方案。这可能会导致较大映像的性能问题。因此,如果有人能向我建议更好的方法,那将是一个很大的帮助: private static bool IsImageBottomClipping(Bitmap image) { for (int i = 0; i < image.Width; i++) {
private static bool IsImageBottomClipping(Bitmap image)
{
for (int i = 0; i < image.Width; i++)
{
var pixel = image.GetPixel(i, image.Height - 1);
if (pixel.ToArgb() != Color.White.ToArgb())
{
return true;
}
}
return false;
}
private static int GetLastWhiteLine(Bitmap image)
{
for (int i = image.Height - 1; i >= 0; i--)
{
int whitePixels = 0;
for (int j = 0; j < image.Width; j++)
{
var pixel = image.GetPixel(j, i);
if (pixel.ToArgb() == Color.White.ToArgb())
{
whitePixels = j + 1;
}
}
if (whitePixels == image.Width)
return i;
}
return -1;
}
私有静态bool IsImageBottomClipping(位图图像)
{
对于(int i=0;i=0;i--)
{
int-whitePixels=0;
对于(int j=0;j
IsImageBottomClipping
工作正常。但另一种方法是不发送正确的白色像素行。它只发送了少一行。示例图像:
在这种情况下,180附近的行应该是
GetLastWhiteLine
方法的返回值。但是它返回了192个。好吧,所以。。。我们有两个问题要解决。首先是优化,然后是bug。我将从优化开始
最快的方法是直接在内存中工作,但老实说,这有点笨拙。我通常使用的第二个最佳选择是将原始图像数据字节从图像对象中复制出来。这将使您得到四个重要的数据:
- 宽度,您可以从图像中获得
- 高度,你可以从图像中得到
- 字节数组,包含图像字节
- 步幅,它提供图像上每行使用的字节数
LockBits
,告诉它以每像素32位的ARGB数据的形式显示图像(如果需要,它将实际转换),获取行跨距,然后使用Marshal.Copy
将整个图像内容复制到字节数组中
Int32 width = image.Width;
Int32 height = image.Height;
BitmapData sourceData = image.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 stride = sourceData.Stride;
Byte[] data = new Byte[stride * height];
Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
image.UnlockBits(sourceData);
如前所述,这被强制为32位ARGB格式。如果您想使用此系统以图像中的原始格式获取数据,只需将PixelFormat.Format32bppArgb
更改为image.PixelFormat
现在,您必须意识到,锁位
是一个相当繁重的操作,它以请求的像素格式将数据复制到新内存中,在新内存中可以读取或编辑数据(如果没有像我在这里指定为只读)。这比您的方法更优化的原因很简单,GetPixel
在每次请求单个像素值时执行LockBits
操作。因此,您正在将锁位
调用的数量从数千个减少到一个
不管怎样,现在,关于你的功能 在我看来,第一种方法完全没有必要;您应该在任何图像上运行第二个。它的输出为您提供了图像的最后一条白线,因此,如果该值等于
height-1
,您就完成了,如果不是,您就立即获得了进一步处理所需的值。毕竟,第一个函数与第二个函数的作用完全相同;它检查一行上的所有像素是否为白色。唯一的区别是它只处理最后一行
那么,第二种方法。这就是问题所在。您将白色像素的数量设置为“当前像素索引加一”,而不是增加它以检查所有像素是否匹配,这意味着该方法将覆盖所有像素,但只检查行上的最后一个像素是否为白色。因为您的图像在最后一行的末尾确实有一个白色像素,所以它在一行之后中止
此外,无论何时发现不匹配的像素,都应该立即中止该行的扫描,就像第一种方法一样;在那之后再继续打那条线是没有意义的
因此,让我们修复第二个函数,并将其重写为使用“字节数组”、“跨距”、“宽度”和“高度”集合,而不是使用图像。我还添加了“白色”作为参数,以使其更易于重用,因此它从GetLastWhiteLine
更改为GetLastClearLine
一个通用的可用性注意事项:如果您在高度和宽度上迭代,请实际调用循环变量y
和x
;它使代码中的内容更加清晰
我在代码注释中解释了使用的系统
private static Int32 GetLastClearLine(Byte[] sourceData, Int32 stride, Int32 width, Int32 height, Color checkColor)
{
// Get color as UInt32 in advance.
UInt32 checkColVal = (UInt32)checkColor.ToArgb();
// Use MemoryStream with BinaryReader since it can read UInt32 from a byte array directly.
using (MemoryStream ms = new MemoryStream(sourceData))
using (BinaryReader sr = new BinaryReader(ms))
{
for (Int32 y = height - 1; y >= 0; --y)
{
// Set position in the memory stream to the start of the current row.
ms.Position = stride * y;
Int32 matchingPixels = 0;
// Read UInt32 pixels for the whole row length.
for (Int32 x = 0; x < width; ++x)
{
// Read a UInt32 for one whole 32bpp ARGB pixel.
UInt32 colorVal = sr.ReadUInt32();
// Compare with check value.
if (colorVal == checkColVal)
matchingPixels++;
else
break;
}
// Test if full line matched the given color.
if (matchingPixels == width)
return y;
}
}
return -1;
}
对于您的示例图像,这会得到值178,这在我签入Gimp时是正确的。好的,所以。。。我们有两个问题要解决。首先是优化,然后是bug。我
{
ms.Position = stride * y;
Int32 x;
for (x = 0; x < width; ++x)
if (sr.ReadUInt32() != checkColVal)
break;
if (x == width)
return y;
}