OpenCV C++案例實戰九《物件計數》
- 前言
- 一、影像預處理
- 1.灰度、閾值
- 2.腐蝕
- 3.距離變換
- 4.自適應閾值
- 5.膨脹
- 二、輪廓查找
- 三、效果顯示
- 四、原始碼
- 總結
前言
本文將使用OpenCV C++ 進行物件計算,
一、影像預處理

原圖如圖所示,本案例想做的是統計影像中有多少個物體,簡單來說就是通過統計有效輪廓來計數,本案例其實最重要的是如何進行影像預處理,如何才能夠將這些輪廓有效區分開,所以,具體影像要設定一個符合特定需求的演算法,接下來,我將一一演示如何一步步進行操作的,
1.灰度、閾值
首先進行影像灰度化,再進行二值化處理,這些都是很基本的影像預處理操作,由于我們的影像是單峰影像,在這是使用的是THRESH_TRIANGLE做閾值,
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//單峰影像使用THRESH_TRIANGLE做閾值
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY_INV | THRESH_TRIANGLE);
THRESH_TRIANGLE

THRESH_OTSU

結果如圖所示,如果使用THRESH_OTSU的話,效果遠沒有THRESH_TRIANGLE好,
2.腐蝕
經過二值化得到的影像明顯黏在一起的,所以得使用腐蝕操作將它們稍微分離一下,在這里,我使用的kernel稍微大了一點,這個根據影像特征自行設定,
//進行腐蝕操作,稍微斷開一些聯通區域
Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat erosion;
erode(thresh, erosion, kernel);
如圖為進行腐蝕操作后的效果,可以看出已經稍微分離了一點,但還不能完全分離,所以還得進行進一步操作,

3.距離變換
OpenCV 的distanceTransform 用于計算影像中每一個非零點距離自己最近零點的距離,影像上越亮的點,代表離零點距離越遠,進行距離變換,可以查找出亮包,找出物體的中心,
//進行距離變換,查找出亮包 找出物體的中心
Mat dist;
distanceTransform(erosion, dist, DIST_L2, 3);
normalize(dist, dist, 0, 1, NORM_MINMAX);

4.自適應閾值
//使用自適應閾值
Mat dist_8U;
dist.convertTo(dist_8U, CV_8UC1);
adaptiveThreshold(dist_8U, dist_8U, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 81,0);
通過使用自適應閾值,已經可以將物體完全分離開了,不過,有些輪廓是斷開的,所以還得通過膨脹操作將它們有效連接起來作為一個整體,

5.膨脹
//進行膨脹處理,將有效輪廓聯通
Mat dilation;
dilate(dist_8U, dilation, kernel);

至此,我們的影像預處理已經完成了,可以看出,我們已經將物體完全的分離開作為一個整體,接下來就是通過輪廓查找來進行計數了,
二、輪廓查找
vector<vector<Point>>contours;
vector<vector<Point>>EffectiveContours;
findContours(dilation, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//計算有效聯通區域
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 500)
{
EffectiveContours.push_back(contours[i]);
}
}
我們通過統計有效輪廓就可以統計出來到底有多少個物體了,接下來就是一些顯示效果操作了,
三、效果顯示
for (int i = 0; i < EffectiveContours.size(); i++)
{
Rect rect = boundingRect(EffectiveContours[i]);
putText(src, to_string(i+1), Point(rect.x+10, rect.y+30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
//rectangle(src, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2);
}
char text[10];
sprintf_s(text, "%s%d","Total:",EffectiveContours.size());
putText(src, text, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
結果如圖所示:


四、原始碼
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat src = imread("test-2.jpg");
if (src.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
}
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
//單峰影像使用THRESH_TRIANGLE做閾值
Mat thresh;
threshold(gray, thresh, 0, 255, THRESH_BINARY| THRESH_TRIANGLE);
//imshow("thresh", thresh);
//進行腐蝕操作,稍微斷開一些聯通區域
Mat kernel = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat erosion;
erode(thresh, erosion, kernel);
//imshow("erosion", erosion);
//進行距離變換,查找出亮包 找出物體的中心
Mat dist;
distanceTransform(erosion, dist, DIST_L2, 3);
normalize(dist, dist, 0, 1, NORM_MINMAX);
//imshow("dist", dist);
//使用自適應閾值
Mat dist_8U;
dist.convertTo(dist_8U, CV_8UC1);
adaptiveThreshold(dist_8U, dist_8U, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 81,0);
//imshow("dist_8U", dist_8U);
//進行膨脹處理,將有效輪廓聯通
Mat dilation;
dilate(dist_8U, dilation, kernel);
//imshow("close", dilation);
vector<vector<Point>>contours;
vector<vector<Point>>EffectiveContours;
findContours(dilation, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//計算有效聯通區域
for (int i = 0; i < contours.size(); i++)
{
double area = contourArea(contours[i]);
if (area > 500)
{
EffectiveContours.push_back(contours[i]);
}
}
for (int i = 0; i < EffectiveContours.size(); i++)
{
Rect rect = boundingRect(EffectiveContours[i]);
putText(src, to_string(i+1), Point(rect.x+10, rect.y+30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
//rectangle(src, Rect(rect.x, rect.y, rect.width, rect.height), Scalar(0, 255, 0), 2);
}
char text[10];
sprintf_s(text, "%s%d","Total:",EffectiveContours.size());
putText(src, text, Point(10, 30), FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2);
imshow("src", src);
waitKey(0);
destroyAllWindows();
system("pause");
return 0;
}
總結
本文使用OpenCV C++進行物件計數,關鍵步驟有以下幾點,
1、使用灰度、閾值、形態學操作、距離變換等預處理操作將物件有效分離開成為一個整體,
2、通過統計有效輪廓數量從而統計出實際物體數量,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/385438.html
標籤:其他
上一篇:重讀經典:《Momentum Contrast for Unsupervised Visual Representation Learning》
