如何解决检测图像中的正方形
在一所学校,我们正在准备扫描的艺术品,并希望将其自动裁剪为正确的尺寸。孩子们(尝试)在矩形内绘制:
我想检测内部矩形边框,所以我在Accord.net中应用了一些过滤器:
main()
但是blobcounter的结果并不是非常准确,紫色线表示BC已检测到的内容。
在Accord.net中,var newImage = new Bitmap(@"C:\Temp\temp.jpg");
var g = Graphics.FromImage(newImage);
var pen = new Pen(Color.Purple,10);
var grayScaleFilter = new Grayscale(1,0);
var image = grayScaleFilter.Apply(newImage);
image.Save(@"C:\temp\grey.jpg");
var skewChecker = new DocumentSkewChecker();
var angle = skewChecker.GetSkewAngle(image);
var rotationFilter = new RotateBilinear(-angle);
rotationFilter.FillColor = Color.White;
var rotatedImage = rotationFilter.Apply(image);
rotatedImage.Save(@"C:\Temp\rotated.jpg");
var thresholdFilter = new IterativeThreshold(10,128);
thresholdFilter.ApplyInPlace(rotatedImage);
rotatedImage.Save(@"C:\temp\threshold.jpg");
var invertFilter = new Invert();
invertFilter.ApplyInPlace(rotatedImage);
rotatedImage.Save(@"C:\temp\inverted.jpg");
var bc = new BlobCounter
{
BackgroundThreshold = Color.Black,FilterBlobs = true,MinWidth = 1000,MinHeight = 1000
};
bc.ProcessImage(rotatedImage);
foreach (var rect in bc.GetobjectsRectangles())
{
g.DrawRectangle(pen,rect);
}
newImage.Save(@"C:\Temp\test.jpg");
会有更好的替代方法吗?还是其他C#库更适合这种计算机视觉?
解决方法
这是一个简单的解决方案,当我无聊的午休时。
基本上,它只是从外部到内部扫描所有尺寸的颜色阈值(黑色),然后得到最突出的结果。
给予
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsValid(int* scan0Ptr,int x,int y,int stride,double thresh)
{
var c = *(scan0Ptr + x + y * stride);
var r = ((c >> 16) & 255);
var g = ((c >> 8) & 255);
var b = ((c >> 0) & 255);
// compare it against the threshold
return r * r + g * g + b * b < thresh;
}
private static int GetBest(IEnumerable<int> array)
=> array.Where(x => x != 0)
.GroupBy(i => i)
.OrderByDescending(grp => grp.Count())
.Select(grp => grp.Key)
.First();
示例
private static unsafe Rectangle ConvertImage(string path,Color source,double threshold)
{
var thresh = threshold * threshold;
using var bmp = new Bitmap(path);
// lock the array for direct access
var bitmapData = bmp.LockBits(new Rectangle(0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppPArgb);
int left,top,bottom,right;
try
{
// get the pointer
var scan0Ptr = (int*)bitmapData.Scan0;
// get the stride
var stride = bitmapData.Stride / 4;
var array = new int[bmp.Height];
for (var y = 0; y < bmp.Height; y++)
for (var x = 0; x < bmp.Width; x++)
if (IsValid(scan0Ptr,x,y,stride,thresh))
{
array[y] = x;
break;
}
left = GetBest(array);
array = new int[bmp.Height];
for (var y = 0; y < bmp.Height; y++)
for (var x = bmp.Width-1; x > 0; x--)
if (IsValid(scan0Ptr,thresh))
{
array[y] = x;
break;
}
right = GetBest(array);
array = new int[bmp.Width];
for (var x = 0; x < bmp.Width; x++)
for (var y = 0; y < bmp.Height; y++)
if (IsValid(scan0Ptr,thresh))
{
array[x] = y;
break;
}
top = GetBest(array);
array = new int[bmp.Width];
for (var x = 0; x < bmp.Width; x++)
for (var y = bmp.Height-1; y > 0; y--)
if (IsValid(scan0Ptr,thresh))
{
array[x] = y;
break;
}
bottom = GetBest(array);
}
finally
{
// unlock the bitmap
bmp.UnlockBits(bitmapData);
}
return new Rectangle(left,right-left,bottom-top);
}
用法
var fileName = @"D:\7548p.jpg";
var rect = ConvertImage(fileName,Color.Black,50);
using var src = new Bitmap(fileName);
using var target = new Bitmap(rect.Width,rect.Height);
using var g = Graphics.FromImage(target);
g.DrawImage(src,new Rectangle(0,target.Width,target.Height),rect,GraphicsUnit.Pixel);
target.Save(@"D:\Test.Bmp");
输出
注意:
- 这并不意味着防弹或最佳解决方案。只是一个简单的快速操作。
- 有很多方法可以做到这一点,即使是机器学习方法也可能更好,更强大。
- 这里有很多代码重复,基本上我只是复制,粘贴和调整了每一面
- 我刚刚选择了一个似乎可行的任意阈值。玩
- 获取最常见的一方可能不是最佳方法,也许您想对结果进行分类。
- 您可能会理智地限制一面需要扫描的数量。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。