问题描述
我画一幅图片进行测试:
I paint a picture to test:
我想知道黑圈中有多少个斑点,每个斑点的大小是多少(所有斑点都是白色的).
And I want to know how much blobs I have in the black circle and what is the size of each blobs (all blobs are ~white).
例如,在这种情况下,我有12个斑点:
For example, in this case I have 12 spots:
我知道如何找到白色像素,并且很容易从左侧验证序列:
I know how to found white pixels and it easy to verify sequence from left:
int whitePixels = 0;
for (int i = 0; i < height; ++i)
{
uchar * pixel = image.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
if (j>0 && pixel[j-1]==0) // to group pixels for one spot
whitePixels++;
}
}
但是很明显,这段代码还不够好(斑点可以是对角线等).
but it's clear that this code is not good enough (blobs can be diagonally, etc.).
因此,最重要的是,我需要帮助:如何定义blob?
So, the bottom line, I need help: how can I define the blobs?
谢谢
推荐答案
以下代码查找所有白色斑点的边界矩形(斑点).
Following code finds bounding rects (blobs) for all white spots.
备注::如果我们可以假设白色斑点确实是白色的(即在灰度图像中值为255),则可以使用此代码段.考虑将其放在某个类中,以避免将不必要的参数传递给函数Traverse.虽然有效.这个想法是基于DFS的.除了灰度图像之外,我们还有id矩阵来分配并记住哪个像素属于哪个blob(具有相同id的所有像素都属于同一blob).
Remark: if we can assume white spots are really white (namely have values 255 in grayscaled image), you can use this snippet. Consider putting it in some class to avoid passing uncecessary params to function Traverse. Although it works. The idea is based on DFS. Apart from the gryscaled image, we have ids matrix to assign and remember which pixel belongs to which blob (all pixels having the same id belong to the same blob).
void Traverse(int xs, int ys, cv::Mat &ids,cv::Mat &image, int blobID, cv::Point &leftTop, cv::Point &rightBottom) {
std::stack<cv::Point> S;
S.push(cv::Point(xs,ys));
while (!S.empty()) {
cv::Point u = S.top();
S.pop();
int x = u.x;
int y = u.y;
if (image.at<unsigned char>(y,x) == 0 || ids.at<unsigned char>(y,x) > 0)
continue;
ids.at<unsigned char>(y,x) = blobID;
if (x < leftTop.x)
leftTop.x = x;
if (x > rightBottom.x)
rightBottom.x = x;
if (y < leftTop.y)
leftTop.y = y;
if (y > rightBottom.y)
rightBottom.y = y;
if (x > 0)
S.push(cv::Point(x-1,y));
if (x < ids.cols-1)
S.push(cv::Point(x+1,y));
if (y > 0)
S.push(cv::Point(x,y-1));
if (y < ids.rows-1)
S.push(cv::Point(x,y+1));
}
}
int FindBlobs(cv::Mat &image, std::vector<cv::Rect> &out, float minArea) {
cv::Mat ids = cv::Mat::zeros(image.rows, image.cols,CV_8UC1);
cv::Mat thresholded;
cv::cvtColor(image, thresholded, CV_RGB2GRAY);
const int thresholdLevel = 130;
cv::threshold(thresholded, thresholded, thresholdLevel, 255, CV_THRESH_BINARY);
int blobId = 1;
for (int x = 0;x<ids.cols;x++)
for (int y=0;y<ids.rows;y++){
if (thresholded.at<unsigned char>(y,x) > 0 && ids.at<unsigned char>(y,x) == 0) {
cv::Point leftTop(ids.cols-1, ids.rows-1), rightBottom(0,0);
Traverse(x,y,ids, thresholded,blobId++, leftTop, rightBottom);
cv::Rect r(leftTop, rightBottom);
if (r.area() > minArea)
out.push_back(r);
}
}
return blobId;
}
编辑:我修复了一个错误,降低了阈值水平,现在在下面给出了输出.我认为这是一个很好的起点.
I fixed a bug, lowered threshold level and now the output is given below. I think it is a good start point.
EDIT2 :我摆脱了Traverse()
中的递归.在较大的图像中,递归导致Stackoverflow.
I get rid of recursion in Traverse()
. In bigger images recursion caused Stackoverflow.
这篇关于如何使用OpenCV检测白色斑点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!