我们仍然讲学习。
昨天已经把80万像素1024*768的图像变成256*192图像了,并且使用iir高斯平滑保留了特征。
下面做的就是用roi把特征图扣出来,也就是所谓的模板,你在原图中的roi假定是200*200,那么在256*192中,就变成50*50了.要注意,我们昨天调用了一个函数非极大值抑制,实质是canny结果:
public byte[] 非极大抑制黑中白202210261519(byte[] gaosbuffer, int chang, int gao)//这个buffer已经gaos过了
{//这个版本非常精彩,自己又一次突破自己202210172008
int hh = gao;
int ww = chang;
double[] 梯度幅值;
梯度幅值 = new double[ww * hh];
double[] gy = new double[ww * hh];
double[] gx = new double[ww * hh];
double[] 梯度角 = new double[ww * hh];
byte[] 输出图像 = new byte[ww * hh];
double maxM = 0;
for (int i = 1; i < (hh - 1); i++)
{
for (int j = 1; j < (ww - 1); j++)
{
int fangbian1 = i * ww + j;
// 3,此处改为 sobelx,sobely20220327
double Grady = gaosbuffer[fangbian1 - 1 - ww] + 2 * gaosbuffer[fangbian1 - ww] + gaosbuffer[fangbian1 - ww + 1]
- gaosbuffer[fangbian1 + ww - 1] - 2 * gaosbuffer[fangbian1 + ww] - gaosbuffer[fangbian1 + ww + 1];
double Gradx = gaosbuffer[fangbian1 - ww + 1] + 2 * gaosbuffer[fangbian1 + 1] + gaosbuffer[fangbian1 + ww + 1]
- gaosbuffer[fangbian1 - ww - 1] - 2 * gaosbuffer[fangbian1 - 1] - gaosbuffer[fangbian1 + ww - 1];
梯度幅值[fangbian1] = Math.Sqrt(Gradx * Gradx + Grady * Grady);
if (梯度幅值[fangbian1] > maxM) maxM = 梯度幅值[fangbian1];
// 4,这个函数博客里边有jiaoduAndxiangxian
梯度角[fangbian1] = jiaoduAndxiangxian(Gradx, Grady) * 180 / Math.PI;//角度,用弧度的话,速度效率高20221018
gx[fangbian1] = Gradx;
gy[fangbian1] = Grady;
}
}
globmaxM = maxM;
//5,非极大值抑制
double g1 = 0, g2 = 0, g3 = 0, g4 = 0;
double dTmp1 = 0.0, dTmp2 = 0.0;
double dWeight = 0.0;
double[] 非极大值抑制后图像 = new double[ww * hh];
for (int i = 1; i < (ww - 1); i++)
{
for (int j = 1; j < (hh - 1); j++)
{
int fangbian = j * ww + i;
if (梯度幅值[fangbian] == 0)
{
非极大值抑制后图像[fangbian] = 0;
}
else
{
//材///
/// g1 g2 /
/// C /
/// g4 g3 /
///
if (((梯度角[fangbian] >= 45) && (梯度角[fangbian] < 90)) ||
((梯度角[fangbian] >= 225) && (梯度角[fangbian] < 270)))//
{
g1 = 梯度幅值[fangbian - ww - 1];
g2 = 梯度幅值[fangbian - ww];
g4 = 梯度幅值[fangbian + ww];
g3 = 梯度幅值[fangbian + ww + 1];
// if (gy[fangbian] != 0)//为什么这个是多余的?
// {
dWeight = Math.Abs(gx[fangbian] / (gy[fangbian])); // p->Gradx,q->Grady
dTmp1 = g1 * dWeight + g2 * (1 - dWeight);
dTmp2 = g3 * dWeight + g4 * (1 - dWeight);
// }
// else { }
}
//材///
g1 /
// / g4 C g2 /
// / g3 /
// /
// else
if (((梯度角[fangbian] >= 135) && (梯度角[fangbian] < 180)) ||//shuipingfangxiang
((梯度角[fangbian] >= 315) && (梯度角[fangbian] < 360)))//20220817
{ //int nPointIdx = i+j*w;
g3 = 梯度幅值[fangbian + ww - 1];
g2 = 梯度幅值[fangbian + 1];
g1 = 梯度幅值[fangbian - ww + 1];
g4 = 梯度幅值[fangbian - 1];
// if (gx[fangbian] != 0)//为什么这个是多余的?
// {
dWeight = Math.Abs(gy[fangbian] / (gx[fangbian])); // p->Gradx,q->Grady
dTmp1 = g1 * dWeight + g2 * (1 - dWeight);
dTmp2 = g3 * dWeight + g4 * (1 - dWeight);
// }
// else
// {
//先不处理20220817
// }
}
// 材///
/ g2 g1 /
/ C /
/ g3 g4 /
/
//else
if (((梯度角[fangbian] >= 90) && (梯度角[fangbian] < 135)) ||
((梯度角[fangbian] >= 270) && (梯度角[fangbian] < 315))) //20220817
{ //int nPointIdx = i+j*w;
g2 = 梯度幅值[fangbian - ww];
g1 = 梯度幅值[fangbian - ww + 1];
g4 = 梯度幅值[fangbian + ww];
g3 = 梯度幅值[fangbian + ww - 1];
// if (gy[fangbian] != 0)//为什么这个是多余的?
// {
dWeight = Math.Abs(gx[fangbian] / (gy[fangbian])); // p->Gradx,q->Grady
dTmp1 = g1 * dWeight + g2 * (1 - dWeight);
dTmp2 = g3 * dWeight + g4 * (1 - dWeight);
// }
// else { }
}
//材///
g3 /
g4 C g2 /
g1 /
//else
if (((梯度角[fangbian] >= 0) && (梯度角[fangbian] < 45)) ||
((梯度角[fangbian] >= 180) && (梯度角[fangbian] < 225)))//这个是45度方向
{ //一共四个方向,已经判断了3个方向,这个可以不必判断了
g1 = 梯度幅值[fangbian + ww + 1];
g2 = 梯度幅值[fangbian + 1];
g3 = 梯度幅值[fangbian - ww - 1];
g4 = 梯度幅值[fangbian - 1];
// if (gx[fangbian] != 0)//为什么这个是多余的?
// {
dWeight = Math.Abs(gy[fangbian] / (gx[fangbian])); //
dTmp1 = g1 * dWeight + g2 * (1 - dWeight);
dTmp2 = g3 * dWeight + g4 * (1 - dWeight);
// }
// else { }
}
}
if ((梯度幅值[fangbian] > dTmp1) && (梯度幅值[fangbian] > dTmp2))
{
非极大值抑制后图像[fangbian] = 梯度幅值[fangbian];//非极大值抑制后图像
}
else
{
非极大值抑制后图像[fangbian] = 0;
}
}
}
for (int i = 0; i < hh; i++)
{
for (int j = 0; j < ww; j++)
{
int fangbian = i * ww + j;
//if ((非极大值抑制后图像[fangbian] > dThrHigh))//这个值太低,有干扰20221026,启用128
// if ((非极大值抑制后图像[fangbian] > 180))//这个值太低,有干扰20221026,启用128,可能180效果更好,要匹配,有著特征就好
if ((非极大值抑制后图像[fangbian] > maxM * 0.618f))//黄金分割了一下
{//在精细中,可以尝试0.667,202211041121
输出图像[fangbian] = 255;
//if (checkBox幅值默认.Checked)
//{ TraceEdge(i, j, dThrLow, ref 梯度幅值, ref 输出图像, ww); }
//else
//{ TraceEdge(i, j, dThrLow, ref 非极大值抑制后图像, ref 输出图像, ww); }
TraceEdge(i, j, dThrLow, ref 梯度幅值, ref 输出图像,ww); //梯度幅值版本20221017第一版本
TraceEdge(i, j, dThrLow, ref 非极大值抑制后图像, ref 输出图像, ww); //第二版本20221018,这两个版本效果都可以
}
}
}
//for (int w2b = 0; w2b < hh * ww; w2b++)
// 输出图像[w2b] = (byte)(255 - 输出图像[w2b]);
return 输出图像;
}
你可以看到,我很多值限定死了,这是很有意义的,首先他是尝试过后定下来的,即经验值,另外,不变就很稳定。我们抠出的图像,即学习图像就来自 return 输出图像;
下面就是抠图代码,并且记录了学习到的边(注意和线条要区别,便可以组成线条,但不是线条),也就是我们提炼了canny图像,不要整体,只要特征,这样匹配中数据量就会很小:
// showbuffer2pict(globgaospydoutimgN, 128, 96, pictureBox加速gaos);
//
pictureBoxroi.Size = new Size(256, 192);
if (globgaospydoutimgN == null) return;
// roi内所有llp
float top = m_cutImageRoi.m_RoiBase1.rcWin.Top/4f;
float left = m_cutImageRoi.m_RoiBase1.rcWin.Left/4f;
//float right = m_cutImageRoi.m_RoiBase1.rcWin.Right;
//float bottom = m_cutImageRoi.m_RoiBase1.rcWin.Bottom;
int temproiw = (int)(m_cutImageRoi.m_RoiBase1.rcWin.Width/4f+3);
int temproih = (int)(m_cutImageRoi.m_RoiBase1.rcWin.Height/4f+3);//si`she*wu*ru`dao*zhi`dejia-san-+3
int mod = temproiw % 4;//解决四位对齐问题20150716
temproiw = temproiw + (4 - mod) % 4;
// _RoiH = Convert.ToInt32(textBoxH.Text);
int temproiX = (int)left;
int temproiY = (int)top;
byte[] roiImage = new byte[temproiw * temproih];
// unsafe
{
for (int i = 0; i < temproih; i++)
for (int j = 0; j < temproiw; j++)
{
roiImage[i * temproiw + j] = globgaospydoutimgN[((i + temproiY) * 128 + j + temproiX)];
}
}
List<List<Point>> llpgaos12896roi = new List<List<Point>>();
// llpgaos12896.Clear();
int ww = temproiw; int hh = temproih;
// ww = temproiw; hh = temproih;
for (int i = 0; i < hh; i++)//****染色算法必须202209091600染色算法也隐含了白底黑斑
{//所以这里必须反色处理,负责没有结果//202209091606
roiImage[i * ww + 0] = 0;
roiImage[i * ww + ww - 1] = 0;
}
for (int j = 0; j < ww; j++)
{
roiImage[0 * ww + j] = 0;
roiImage[(hh - 1) * ww + j] = 0;
}
染色算法黑中白(ref llpgaos12896roi, ref roiImage, ww, hh);//在边界崩溃了20220831
if (llpgaos12896roi.Count == 0) return;
showbuffer2pict(roiImage, ww, hh, pictureBoxroi);//roi内线条被染色为240
/
//质心还是要找,可能会用到,llp可以合并在一起用。202210261700
hebingllp.Clear();
for (int i = 0; i < llpgaos12896roi.Count; i++)
{
for (int j = 0; j < llpgaos12896roi[i].Count; j++)
{
hebingllp.Add(llpgaos12896roi[i][j]);//找出x,y中最大,最小,差值加一来确认宽和高202210261709
}
}
//0度匹配
List<float> arr = new List<float>();
for (int i = 0; i < hebingllp.Count; i++)
{
arr.Add(hebingllp[i].X);
}
// 以上四个x,找出最大和最小,画出两条线;
float min = arr[0]; float max = arr[0];
for (int i = 0; i < arr.Count; i++)
{
if (min > arr[i])
{
min = arr[i];
}
if (max < arr[i])
{
max = arr[i];
}
}
arr = new List<float>();
// 同样四个y,找出最大和最小,画出两条线;
for (int i = 0; i < hebingllp.Count; i++)
{
arr.Add(hebingllp[i].Y);
}
float min1 = arr[0]; float max1 = arr[0];
for (int i = 0; i < arr.Count; i++)
{
if (min1 > arr[i])
{
min1 = arr[i];
}
if (max1 < arr[i])
{
max1 = arr[i];
}
}
匹配www = (int)Math.Abs(max - min);//两个x值
//匹配www = (int)Math.Abs(max - min)+1;//两个x值
匹配www = (int)Math.Abs(max - min) + 2 + 1 + 1;//两个x值//202210270838
匹配qidianX = (int)min - 2;
匹配hhh = (int)Math.Abs(max1 - min1);//两个y值//左顶(min,min1)及右底(max,max1)
//匹配hhh = (int)Math.Abs(max1 - min1)+1;
匹配hhh = (int)Math.Abs(max1 - min1) + 2 + 1 + 1;
匹配qidianY = (int)min1 - 2;//已经改进,可以接受0,0;不能接受-1000//202211040955
min和min1有都等于0的情形,所以不能赋值为零,为-1来判断吧,0,0值有可能是ok状况,匹配qidianY的条件要放宽
hebingllp1.Clear();
for (int t = 0; t < hebingllp.Count; t++)
{
hebingllp1.Add(new Point(hebingllp[t].X - 匹配qidianX, hebingllp[t].Y - 匹配qidianY));//这样处理的好处是与匹配www* 匹配hhh一致。202210261924
}
//
buttonmatchlearning.Enabled = false;
要注意的是,我们找了特征的最小外接矩形,显然他比抠图的roi要小,这也是为匹配减少数据量,匹配中,就用这个矩形,遍历整个256*192图像。
其实,roi矩形也是可以的,矩形大,搜索次数减少,矩形小,搜索次数多,你要衡量一下,我这里没用roi矩形。
学习就算完了,下面讲匹配,待续。。。