黑白二值图像周长测量--C#实现
假设是单像素线白色用1(对应RGB(255,0,0))表示,背景用0(对应RBG(0,0,0))表示。
考虑3种类型的边界 水平方向 0->1 1->0 类似垂直方向也是0->1 1->0
如果是单像素,如果水平检测时 发现 0->1 或1->0变动,那么都将 mapX[i,j] 设置成1,注意是白色像素对应的位置
类似的处理也针对垂直方向mapY[i,j]
如果以个mapY[i,j] 与mapX[i,j] 同时标记成了1,那么意味着这个像素是斜方向,那么按sqrt(2)来记录。
注意一:以直角三角型为例,构成边的线段粗细大于1个像素,那么求出的结果是翻倍的,因为边界出现了2次。
注意二:以直角三角型为例,如果直角三角型内部是填充成白色的(前景色1),那么求出的结果是一样的,因为边界只出现一次。
完整代码如下,需要传入二值图片。
/// <summary> /// 二值图片前景是白背景是黑 /// 测试白像素的边长 /// 正负 pi/4 ,3pi/4 计1.414 /// 其余按1计 /// </summary> /// <param name="bm"></param> public static double? SegmentLen(Bitmap bm) { var map = new byte[bm.Width, bm.Height]; var mapX = new byte[bm.Width, bm.Height]; var mapY = new byte[bm.Width, bm.Height]; for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { var c = bm.GetPixel(i, j); if (c.R == 255) { map[i, j] = 1; } else { map[i, j] = 0; } mapX[i, j] = 0; mapY[i, j] = 0; } } //水平边界检测 for (int i = 1; i < bm.Width - 1; i++) { for (int j = 1; j < bm.Height - 1; j++) { if (map[i, j] == 1 && map[i, j - 1] == 0) mapX[i, j] = 1; if (map[i, j] == 0 && map[i, j - 1] == 1) mapX[i, j - 1] = 1; } } //垂直边界检测 for (int i = 1; i < bm.Width - 1; i++) { for (int j = 1; j < bm.Height - 1; j++) { if (map[i, j] == 1 && map[i - 1, j] == 0) mapY[i, j] = 1; if (map[i, j] == 0 && map[i - 1, j] == 1) mapY[i - 1, j] = 1; } } var len = 0.0d; for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { if (mapY[i, j] == 1 && mapX[i, j] == 1) { len += 1.414; } else if (mapX[i, j] == 1 || mapY[i, j] == 1) { len += 1; } } } return len; }
类似的调用代码
var filename = @"C:\t2\z4_3.jpg";
pictureBox1.ImageLocation = filename;
var tImg = new Bitmap(filename);
tImg = GenBinaryImgByThreshold(tImg, 80);
tImg = BorderCheck.FullScanBoundrExtract(tImg);
var len = BorderCheck.SegmentLen(tImg);
Console.WriteLine(len);
ShowImg(tImg);
public Bitmap GenBinaryImgByThreshold(Bitmap bm, int threshold) { Color white = Color.FromArgb(255, 255, 255); Color black = Color.FromArgb(0, 0, 0); Color c = new Color(); int r; Bitmap box1 = new Bitmap(bm.Width, bm.Height); for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { c = bm.GetPixel(i, j); r = (c.R + c.G + c.B) / 3 ; if (r > threshold) { box1.SetPixel(i, j, black); } else { box1.SetPixel(i, j, white); } } } return box1; }
上文涉及到的边界抽取函数:
/// <summary> /// 黑白图片全图检测边界抽取 /// </summary> public static Bitmap FullScanBoundrExtract(Bitmap bm) { var box1 = new Bitmap(bm);//输入使用bm.Width,bm.Height,保留的点是透明的。 var b = Color.FromArgb(255,0, 0, 0); var w = Color.FromArgb(255,255, 255, 255); var map = new Int16[bm.Width, bm.Height]; var map2 = new Int16[bm.Width, bm.Height]; for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { var c = bm.GetPixel(i, j); if (c.R == 255) { map[i, j] = 1; } else { map[i, j] = 0; box1.SetPixel(i, j, b); } //map2[i, j] = 0; } } for (int i = 1; i < bm.Width - 1; i++) { for (int j = 1; j < bm.Height - 1; j++) { if (map[i, j] == 1 && map[i, j - 1] == 0) map2[i, j] = 1; if (map[i, j] == 0 && map[i, j - 1] == 1) map2[i, j - 1] = 1; } } for (int i = 1; i < bm.Width - 1; i++) { for (int j = 1; j < bm.Height - 1; j++) { if (map[i, j] == 1 && map[i - 1, j] == 0) map2[i, j] = 1; if (map[i, j] == 0 && map[i - 1, j] == 1) map2[i - 1, j] = 1; } } for (int i = 0; i < bm.Width; i++) { for (int j = 0; j < bm.Height; j++) { if (map2[i, j] == 0) { box1.SetPixel(i, j, b); } } } return box1; }