帮朋友处理的,检测识别上图芯片管脚的位置。大体流程,识别图片,并显示结果,对于结果位置使用红色标出。先看效果图
C#识别代码如下
PT pt = new PT();
pt.Find(bmp);
Bitmap bmp_tmp = new Bitmap(bmp);
Graphics g = Graphics.FromImage(bmp_tmp);
StringBuilder sb = new StringBuilder();
foreach (PtRec r in pt.Result)
{
g.DrawRectangle(Pens.Red, new Rectangle(r.x1, r.y1, r.x2- r.x1+1, r.y2 - r.y1+1));
sb.AppendLine(((r.x1 + r.x2) / 2).ToString() + "," + ((r.y1 + r.y2) / 2).ToString() + " Score:" + (r.v * 100).ToString("0.0"));
}
g.Dispose();
pictureBox1.Image = bmp_tmp;
textBox1.Text = sb.ToString();
识别的处理在class PT中
public void Find(Bitmap bmp)
{
Result.Clear();
Bmp2Array(bmp);
Proc();
}
Bmp2Array 将图片转化为灰度二维数组
Proc 计算管脚的位置
private void Proc()
{
x_a = new int[Width * Height];
y_a = new int[Width * Height];
for (int y = step_len + 1; y < Height - step_len - 1; y++)
for (int x = step_len + 1; x < Width - step_len - 1; x++)
{
if (array[y, x] < max_gv)
{
PtRec r = new PtRec() { x1 = x, y1 = y, x2 = x, y2 = y, cnt = 0 };
Fill(x, y, r);
r.Check();
if (r.v>0.6)
Result.Add(r);
}
}
}
使用Fill计出所有的区域,然后计算区域的符合度,v>0.6 添加到结果中。r.Check 是计算符合度的函数
public void Check()
{
int w = x2 - x1+1;
int h = y2 - y1 + 1;
int v1 = cnt * 100 / (w * h);
int v2 = w * h;
int v1_std = 80;
int v2_std = 288;
v = get_v(v1, v1_std) * get_v(v2, v2_std);
}
public float get_v(int v, int v_std)
{
if (v * v_std == 0)
return 0;
if (v < v_std)
return 1.0F *v / v_std;
else
return 1.0F * v_std / v;
}
这里有2个参数 ,v1_std = 80 和 v2_std = 288 对于不同大小的图像可以修改这里。
或者只用v1_std = 80 这个参数,就和图像大小无关了。不过另外需要加一个过滤,把特别小的和特别大的区域过滤掉,例如过滤掉
宽度 小于 图片宽度 1%的区域
高度 小于 图片宽度 1%的区域
宽度 大于 图片宽度 15%的区域
高度 大于 图片宽度 15%的区域
主要的计算在private void Fill(int x, int y, PtRec r) 中
private void Fill(int x, int y, PtRec r)
{
c_a_last = 0;
c_a = 1;
x_a[0] = x;
y_a[0] = y;
int xi, yi;
while (c_a > c_a_last)
{
int c = c_a;
for (int i = c_a_last; i < c_a; i++)
{
foreach (Point pt in step)
{
xi = x_a[i] + pt.X;
yi = y_a[i] + pt.Y;
if (xi < 0)
continue;
if (yi < 0)
continue;
if (xi >= Width)
continue;
if (yi >= Height)
continue;
if (array[yi, xi] < max_gv)
{
if (yi < r.y1)
r.y1 = yi;
if (yi > r.y2)
r.y2 = yi;
if (xi < r.x1)
r.x1 = xi;
if (xi > r.x2)
r.x2 = xi;
array[yi, xi] = 255;
r.cnt = r.cnt + 1;
x_a[c] = xi;
y_a[c] = yi;
c++;
}
}
}
c_a_last = c_a;
c_a = c;
}
}
全部代码如下:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ImgPT
{
public class PT
{
public void Find(Bitmap bmp)
{
Result.Clear();
Bmp2Array(bmp);
Proc();
}
private void Proc()
{
x_a = new int[Width * Height];
y_a = new int[Width * Height];
for (int y = step_len + 1; y < Height - step_len - 1; y++)
for (int x = step_len + 1; x < Width - step_len - 1; x++)
{
if (array[y, x] < max_gv)
{
PtRec r = new PtRec() { x1 = x, y1 = y, x2 = x, y2 = y, cnt = 0 };
Fill(x, y, r);
r.Check();
if (r.v>0.6)
Result.Add(r);
}
}
}
public int[] x_a;
public int[] y_a;
public int c_a_last;
public int c_a;
private void Fill(int x, int y, PtRec r)
{
c_a_last = 0;
c_a = 1;
x_a[0] = x;
y_a[0] = y;
int xi, yi;
while (c_a > c_a_last)
{
int c = c_a;
for (int i = c_a_last; i < c_a; i++)
{
foreach (Point pt in step)
{
xi = x_a[i] + pt.X;
yi = y_a[i] + pt.Y;
if (xi < 0)
continue;
if (yi < 0)
continue;
if (xi >= Width)
continue;
if (yi >= Height)
continue;
if (array[yi, xi] < max_gv)
{
if (yi < r.y1)
r.y1 = yi;
if (yi > r.y2)
r.y2 = yi;
if (xi < r.x1)
r.x1 = xi;
if (xi > r.x2)
r.x2 = xi;
array[yi, xi] = 255;
r.cnt = r.cnt + 1;
x_a[c] = xi;
y_a[c] = yi;
c++;
}
}
}
c_a_last = c_a;
c_a = c;
}
}
private static int max_gv = 128;
private static int step_len =2;
private static List<Point> step = make_step();
private static List<Point> make_step()
{
List<Point> list = new List<Point>();
for (int y = -step_len; y <= step_len; y++)
for (int x = -step_len; x <= step_len; x++)
{
if ((y == 0) && (x == 0))
continue;
list.Add(new Point(x, y));
}
return list;
}
private void Bmp2Array(Bitmap v)
{
Width = v.Width;
Height = v.Height;
array = new int[Height, Width];
Bitmap bmp = new Bitmap(v.Width, v.Height, PixelFormat.Format24bppRgb);
Graphics g = Graphics.FromImage(bmp);
g.DrawImage(v, 0, 0);
g.Dispose();
BitmapData bmpdata = bmp.LockBits(new Rectangle(0, 0, Width, Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int len = bmpdata.Stride;
byte[] buff = new byte[len];
int idx = 0;
byte blue, green, red, gv;
for (int y = 0; y < Height; y++)
{
IntPtr p = bmpdata.Scan0 + len * y;
System.Runtime.InteropServices.Marshal.Copy(p, buff, 0, len);
for (int x = 0; x < Width; x++)
{
idx = x * 3;
blue = buff[idx];
green = buff[idx + 1];
red = buff[idx + 2];
gv = (byte)(0.229 * red + 0.587 * green + 0.144 * blue);
array[y, x] = gv;
}
}
bmp.UnlockBits(bmpdata);
bmp.Dispose();
}
private int Width;
private int Height;
private int[,] array;
public List<PtRec> Result = new List<PtRec>();
}
public class PtRec
{
public int x1;
public int y1;
public int x2;
public int y2;
public int cnt = 0;
public float v = 0;
public void Check()
{
int w = x2 - x1+1;
int h = y2 - y1 + 1;
int v1 = cnt * 100 / (w * h);
int v2 = w * h;
int v1_std = 80;
int v2_std = 288;
v = get_v(v1, v1_std) * get_v(v2, v2_std);
}
public float get_v(int v, int v_std)
{
if (v * v_std == 0)
return 0;
if (v < v_std)
return 1.0F *v / v_std;
else
return 1.0F * v_std / v;
}
}
}
这种情况可以通过设计算法来比较范围像素相似度来做机器视觉处理,识别芯片管脚的位置。不需要上halcon等更专业的软件和算法。