建立測試程序
通過NuGet工具爲工程添加OpenCvSharp3-AnyCPU:
- 工具–NuGet包管理器—管理解決方案的NuGet程序包
測試程序
打開位於\bin\Debug\netcoreapp2.1和dill文件同目錄下的lenna.jpg。
先添加引用:using OpenCvSharp
;
static void Main(string[] args)
{
Mat src = new Mat("lenna.jpg", ImreadModes.Grayscale);//小寫的scale,不然報錯
Mat dst = new Mat();
Mat dst1 = new Mat();
Cv2.Canny(src, dst, 50, 200);
Cv2.Add(src, dst, dst1);
using (new Window("src image", src))
using (new Window("dst image", dst))
using (new Window("dst1 image", dst1))
{
Cv2.WaitKey();
}
}
測試成功之後進行函數理解和麪向搜索引擎的編程
關於OpenCv的模板匹配函數matchTemplate,這些地方都講得都比較清楚,但我第一天看,還沒沒看到在C#上實現的,所以只能一步步寫,函數定義,模仿,解決報錯。
友情鏈接:
C#模板匹配代碼
static void Main(string[] args)
{
//模板圖片
Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);
//被匹配圖
Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);
//匹配結果
Mat result=new Mat();
//模板匹配
Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配爲1,值越小匹配越差
//數組位置下x,y
Point minLoc =new Point(0,0);
Point maxLoc = new Point(0, 0);
Point matchLoc = new Point(0, 0);
Cv2.MinMaxLoc(result, out minLoc, out maxLoc);
matchLoc = maxLoc;
Mat mask = wafer.Clone();
//畫框顯示
Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);
//新建窗體顯示圖片
using (new Window("temp image", temp))
using (new Window("wafer image", wafer))
using (new Window("mask image", mask))
{
Cv2.WaitKey();
}
}
結果
詳解和補習
- Point是個結構體,Point newPoint=new Point(int x,int y);,用於得到位置。
- Cv2.Rectangle():對角線畫框,起點和終點,都用Point,線寬
Cv2.Rectangle(img, (x1, y1), (x2, y2), (255,0,0), 2)//
x1,y1 ------
| |
| |
| |
--------x2,y2
重載是怎麼回事呢?overload是重載,一般是用於在一個類內實現若干重載的方法,這些方法的名稱相同而參數形式不同。
根據一定度量值畫框標識出多個匹配結果
我們知道保存結果的矩陣是result,查找矩陣中在某一範圍座標,就可以標識出多個結果。參數說明:
把result打印出來如下圖,CV_32FC1就代表數據是32位單通道浮點數
通過At函數可以獲取某座標的值
//
// 摘要:
// Returns a value to the specified array element.
//
// 參數:
// i0:
// Index along the dimension 0
//
// i1:
// Index along the dimension 1
//
// 類型參數:
// T:
//
// 返回結果:
// A value to the specified array element.
public T At<T>(int i0, int i1) where T : struct;
通過Set函數可以設置某座標的值
//
// 摘要:
// Set a value to the specified array element.
//
// 參數:
// i0:
// Index along the dimension 0
//
// i1:
// Index along the dimension 1
//
// value:
//
// 類型參數:
// T:
public void Set<T>(int i0, int i1, T value) where T : struct;
注意行和列的區分,matchLoc是和習慣相反的。Y是行座標,X是列座標
那麼我們就可以通過遍歷一遍result裏面每個座標點的值,如果大於閾值,那麼就進行畫框顯示。一般還要先經過歸一化。還有看到一個人是每次都把最大值設爲0,再用一次MinMaxLoc查找。
我們還可以畫出座標
void cv::putText(
cv::Mat& img, // 待繪製的圖像
const string& text, // 待繪製的文字
cv::Point origin, // 文本框的左下角
int fontFace, // 字體 (如cv::FONT_HERSHEY_PLAIN)
double fontScale, // 尺寸因子,值越大文字越大
cv::Scalar color, // 線條的顏色(RGB)
int thickness = 1, // 線條寬度
int lineType = 8, // 線型(4鄰域或8鄰域,默認8鄰域)
bool bottomLeftOrigin = false // true='origin at lower left'
);
static void Main(string[] args)
{
//讀取圖片
Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);//模板圖片
//Cv2.ImWrite("temp_write.jpg",temp);//寫入圖片
Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);//被匹配圖
Mat result=new Mat(); //匹配結果
//模板匹配
Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配爲1,值越小匹配越差
Double minVul;
Double maxVul;
Point minLoc =new Point(0,0);
Point maxLoc = new Point(0, 0);
Point matchLoc = new Point(0, 0);
Cv2.Normalize(result, result,0, 1,NormTypes.MinMax,-1);//歸一化
Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找極值
matchLoc = maxLoc;//最大值座標
//result.Set(matchLoc.Y, matchLoc.X, 0);//改變最大值爲最小值
Mat mask = wafer.Clone();//複製整個矩陣
//畫框顯示
Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);
Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
Console.WriteLine("At獲取最大值(Y,X):{0}", result.At<float>(matchLoc.Y, matchLoc.X));
Console.WriteLine("result的類型:{0}", result.GetType());
//循環查找畫框顯示
Double threshold = 0.91;
Mat maskMulti = wafer.Clone();//複製整個矩陣
for (int i=1; i<result.Rows; i++)//行遍歷
{
for (int j = 1; j < result.Cols; j++)//列遍歷
{
// Console.WriteLine("({0},{1}):{2}",i,j, result.At<float>(j, i));
if (result.At<float>(i, j) > threshold)
{
Cv2.Rectangle(maskMulti, new Point(j, i), new Point(j + temp.Cols, i + temp.Rows), Scalar.Green, 2);
//畫出座標
string axis = '(' + Convert.ToString(i) + ',' + Convert.ToString(j) + ')';
Cv2.PutText(maskMulti, axis, new Point(j, i), HersheyFonts.HersheyPlain, 1, Scalar.Red, 1, LineTypes.Link4);
}
}
}
//新建窗體顯示圖片
using (new Window("temp image", temp))
using (new Window("wafer image", wafer))
using (new Window("mask image", mask))
using (new Window("maskMulti image", maskMulti))
{
Cv2.WaitKey();
}
}
模板匹配可分爲兩類
- 基於圖像灰度相關的模板匹配
- 基於幾何特徵的模板匹配
第一種即爲現在所用到的,其缺點
- 由於其算法特性,無法匹配超過一定角度的
- 對於篩選閾值獲取多處匹配時,降低閾值時,在一處周圍會出現重複匹配的效果
但是我們也可以通過建立感興區,在裏面找最大,再判斷是否大於閾值,再移動感興趣,可解決重複匹配問題。
static void Main(string[] args)
{
//讀取圖片
Mat temp = new Mat("temp.jpg",ImreadModes.AnyColor);//模板圖片
//Cv2.ImWrite("temp_write.jpg",temp);//寫入圖片
Mat wafer = new Mat("wafer.jpg", ImreadModes.AnyColor);//被匹配圖
Mat result=new Mat(); //匹配結果
//模板匹配
Cv2.MatchTemplate(wafer,temp,result,TemplateMatchModes.CCoeffNormed);//最好匹配爲1,值越小匹配越差
Double minVul;
Double maxVul;
Point minLoc =new Point(0,0);
Point maxLoc = new Point(0, 0);
Point matchLoc = new Point(0, 0);
Cv2.Normalize(result, result,0, 1,NormTypes.MinMax,-1);//歸一化
Cv2.MinMaxLoc(result, out minVul, out maxVul, out minLoc, out maxLoc);//查找極值
matchLoc = maxLoc;//最大值座標
//result.Set(matchLoc.Y, matchLoc.X, 0);//改變最大值爲最小值
Mat mask = wafer.Clone();//複製整個矩陣
//畫框顯示
Cv2.Rectangle(mask, matchLoc, new Point(matchLoc.X + temp.Cols, matchLoc.Y + temp.Rows),Scalar.Green, 2);
Console.WriteLine("最大值:{0},X:{1},Y:{2}", maxVul, matchLoc.Y, matchLoc.X);
Console.WriteLine("At獲取最大值(Y,X):{0}", result.At<float>(matchLoc.Y, matchLoc.X));
Console.WriteLine("result的類型:{0}", result.GetType());
//循環查找畫框顯示
Double threshold = 0.91;
Mat maskMulti = wafer.Clone();//複製整個矩陣
for (int i=1; i<result.Rows- temp.Rows; i+= temp.Rows)//行遍歷
{
for (int j = 1; j < result.Cols- temp.Cols; j+= temp.Cols)//列遍歷
{
Rect roi = new Rect(j,i, temp.Cols, temp.Rows); //建立感興趣
Mat RoiResult = new Mat(result,roi);
Cv2.MinMaxLoc(RoiResult, out minVul, out maxVul, out minLoc, out maxLoc);//查找極值
matchLoc = maxLoc;//最大值座標
if (maxVul > threshold)
{
//畫框顯示
Cv2.Rectangle(maskMulti, new Point(j+maxLoc.X,i+maxLoc.Y), new Point(j + maxLoc.X + temp.Cols, i + maxLoc.Y + temp.Rows), Scalar.Green, 2);
string axis = '(' + Convert.ToString(i + maxLoc.Y) + ',' + Convert.ToString(j + maxLoc.X) + ')';
Cv2.PutText(maskMulti, axis, new Point(j + maxLoc.X, i + maxLoc.Y), HersheyFonts.HersheyPlain, 1, Scalar.Red, 1, LineTypes.Link4);
}
}
}
//新建窗體顯示圖片
using (new Window("temp image", temp))
using (new Window("wafer image", wafer))
using (new Window("mask image", mask))
using (new Window("maskMulti image", maskMulti))
{
Cv2.WaitKey();
}
}
在這種芯片定位任務中或簡單的缺陷識別任務中最好不要使用這種方法,魯棒性太差。
參考文獻:
[1]徐亞坤. 基於機器視覺的LED芯片識別與產品檢測系統[D].南昌大學,2018.