OpenCV - 如何计数照片中的对象? [英] OpenCV - how to count objects in photo?

查看:185
本文介绍了OpenCV - 如何计数照片中的对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我尝试这个代码
http://opencv-code.com/tutorials/count- and-segment-overlapping-objects-with-watershed-and-distance-transform /



这是结果



您可以在左侧图片中看到9个对象,在我运行代码后,我得到了11个对象的正确图片。
我用ncomp引入了MessageBoxW,你可以看到ncomp get 11,而不是9。



我搜索了很多,但找不到另一个代码工程带给我9的结果。



我希望有人修复此代码,我将使用它,或将链接到一个代码,我可以使用它,并得到正确的结果一些对象。



这是我使用的代码:

  cv :: Mat src = cv :: imread(c:\\pic\\test.jpg); 
if(!src.data)
return -1;

cv :: imshow(src,src);

//从源图像创建二进制图像
cv :: Mat bw;
cv :: cvtColor(src,bw,CV_BGR2GRAY);
cv :: threshold(bw,bw,40,255,CV_THRESH_BINARY);
cv :: imshow(bw,bw);

//执行距离变换算法
cv :: Mat dist;
cv :: distanceTransform(bw,dist,CV_DIST_L2,3);

//为range = {0.0,1.0}标准化距离图像
//所以我们可以可视化和阈值
cv :: normalize(dist,dist, 1,cv :: NORM_MINMAX);
cv :: imshow(dist,dist);

//获取峰值的阈值
//这将是前景对象的标记
cv :: threshold(dist,dist,.5,1.,CV_THRESH_BINARY );
cv :: imshow(dist2,dist);

//创建距离图像的CV_8U版本
//需要cv :: findContours()
cv :: Mat dist_8u;
dist.convertTo(dist_8u,CV_8U);

//找到总标记
std :: vector< std :: vector< cv :: Point> >轮廓;
cv :: findContours(dist_8u,contoururs,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
int ncomp = contoururs.size();

//为分水岭算法创建标记图像
cv :: Mat markers = cv :: Mat :: zeros(dist.size(),CV_32SC1);

//绘制前景标记
for(int i = 0; i cv :: drawContours(markers,contours,i,cv :: Scalar :: all(i + 1),-1);

//绘制背景标记
cv :: circle(markers,cv :: Point(5,5),3,CV_RGB(255,255,255),-1);
cv :: imshow(markers,markers * 10000);

//执行分水岭算法
cv :: watershed(src,markers);

//生成随机颜色
std :: vector< cv :: Vec3b>颜色;
for(int i = 0; i {
int b = cv :: theRNG()。uniform(0,255);
int g = cv :: theRNG()。uniform(0,255);
int r = cv :: theRNG()。uniform(0,255);

colors.push_back(cv :: Vec3b((uchar)b,(uchar)g,(uchar)r));
}

//创建结果图像
cv :: Mat dst = cv :: Mat :: zeros(markers.size(),CV_8UC3);

//用随机颜色填充标记的对象
for(int i = 0; i< markers.rows; i ++)
{
for(int j = 0; j< markers.cols; j ++)
{
int index = markers.at< int>(i,j)
if(index> 0&& index< = ncomp)
dst.at< cv :: Vec3b>(i,j)= colors [index-1]
else
dst.at< cv :: Vec3b>(i,j)= cv :: Vec3b(0,0,0);
}
}

cv :: imshow(dst,dst);

wchar_t buffer [256];
wsprintfW(buffer,L%d,ncomp);
MessageBoxW(nullptr,buffer,buffer,MB_OK);

cv :: waitKey(0);
return 0;

提前感谢

解决方案

你的算法计数对象,用分水岭算法标记。你给的例子有5个对象和5个分水岭标签,所以它工作正常。但你看到你的图像有9个对象,但11个分水岭标签。您应该在应用分水岭之前计算对象。实际上,您不需要应用分水岭,因为您的图像清楚地分割和计数对象。您只需将图像从彩色图像转换为二进制。之后找到并计数轮廓。
代码如下。如果你不想应用扩展,你可以使用contours_eroded.size()获得对象的数量,这将给你侵蚀后的数字。如果你不想应用任何形态

  int main(){
vector< vector< Point> >轮廓;
vector< Vec4i>层次;
Rect bounding_rect;
Mat dst,bin;

Mat src = imread(example.jpg,CV_LOAD_IMAGE_COLOR); //从文件读取图像


cvtColor(src,dst,CV_BGR2GRAY); //将图像从rgb(src)转换为灰度级(dst)
threshold(dst,bin,40,255,THRESH_BINARY); // Tresholds image level = 40 from gray level(dst)to binary(bin)
findContours(bin,contour,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE); //查找bin图像上的轮廓

标量颜色(255,255,255);
for(int i = 0; i {
if((contourArea(contoururs [i],false))> 100){//如果计数器区域> 100像素在新的图像变量ero上绘制,
drawContours (bin,轮廓,i,颜色,CV_FILLED,8,层级); //将轮廓绘制为填充
}
}

findContours(bin,contour,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);

for(int i = 0; i {
bounding_rect = boundingRect(contoururs [i]); //绑定和绘制矩形在src(原始图像)末尾检测到的每个对象
rectangle(src,bounding_rect,Scalar(0,255,0),3,8,0);
}
namedWindow(Binary,CV_WINDOW_NORMAL);
imshow(Binary,src);
cout<< contours.size();
waitKey(0);
return 0;
}


i try to count objects in photo, but can not get the right result.

i try this code http://opencv-code.com/tutorials/count-and-segment-overlapping-objects-with-watershed-and-distance-transform/

This is result

You can see in the lThe left image has 9 objects,, and after I run the code, I get the right picture of 11 objects. I introduced MessageBoxW with ncomp, and you can see that ncomp get 11, instead 9.

I searched a lot but could not find another code works brings me the result of 9.

I would like someone to repair this Code shall I use it, or will link to a code that I can use it and get the right result of a number of objects.

This is the code I use:

cv::Mat src = cv::imread("c:\\pic\\test.jpg");
if (!src.data)
    return -1;

cv::imshow("src", src);

// Create binary image from source image
cv::Mat bw;
cv::cvtColor(src, bw, CV_BGR2GRAY);
cv::threshold(bw, bw, 40, 255, CV_THRESH_BINARY);
cv::imshow("bw", bw);

// Perform the distance transform algorithm
cv::Mat dist;
cv::distanceTransform(bw, dist, CV_DIST_L2, 3);

// Normalize the distance image for range = {0.0, 1.0}
// so we can visualize and threshold it
cv::normalize(dist, dist, 0, 1., cv::NORM_MINMAX);
cv::imshow("dist", dist);

// Threshold to obtain the peaks 
// This will be the markers for the foreground objects
cv::threshold(dist, dist, .5, 1., CV_THRESH_BINARY);
cv::imshow("dist2", dist);

// Create the CV_8U version of the distance image
// It is needed for cv::findContours()
cv::Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);

// Find total markers
std::vector<std::vector<cv::Point> > contours;
cv::findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
int ncomp = contours.size();

// Create the marker image for the watershed algorithm
cv::Mat markers = cv::Mat::zeros(dist.size(), CV_32SC1);

// Draw the foreground markers
for (int i = 0; i < ncomp; i++)
    cv::drawContours(markers, contours, i, cv::Scalar::all(i+1), -1);

// Draw the background marker
cv::circle(markers, cv::Point(5,5), 3, CV_RGB(255,255,255), -1);
cv::imshow("markers", markers*10000);

// Perform the watershed algorithm
cv::watershed(src, markers);

// Generate random colors
std::vector<cv::Vec3b> colors;
for (int i = 0; i < ncomp; i++)
{
    int b = cv::theRNG().uniform(0, 255);
    int g = cv::theRNG().uniform(0, 255);
    int r = cv::theRNG().uniform(0, 255);

    colors.push_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));
}

// Create the result image
cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);

// Fill labeled objects with random colors
for (int i = 0; i < markers.rows; i++)
{
    for (int j = 0; j < markers.cols; j++)
    {
        int index = markers.at<int>(i,j);
        if (index > 0 && index <= ncomp)
            dst.at<cv::Vec3b>(i,j) = colors[index-1];
        else
            dst.at<cv::Vec3b>(i,j) = cv::Vec3b(0,0,0);
    }
}

cv::imshow("dst", dst);

wchar_t buffer[256];
wsprintfW(buffer, L"%d", ncomp);
MessageBoxW(nullptr, buffer, buffer, MB_OK);

cv::waitKey(0);
return 0;

Thanks in advance

解决方案

Your algorithm count objects which labeled by watershed algorithm.The example which you gave there are 5 objects and 5 watershed label so it works fine.But you see your image has 9 objesct but 11 watershed labels. You should count the object before apply watershed.Actually, you don't need to apply watershed because your image is so clear to segment and count objects.You only turn image to binary from color image. After that find and count the contours. The code is below.If you don't want to apply dilation you can get the number of objects with contours_eroded.size().This will give you number after erosion.If you don't want to apply any morphologic process you can apply findcontour function to binary image after that you can get size of its contours.

int main () {   
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Rect bounding_rect;
Mat dst,bin;

Mat src = imread("example.jpg", CV_LOAD_IMAGE_COLOR); // reads image from file


cvtColor(src,dst,CV_BGR2GRAY);  // converts image from rgb(src) to gray level (dst) 
threshold(dst,bin,40,255,THRESH_BINARY); // Tresholds image with level = 40 from gray level(dst) to binary (bin)
findContours(bin,contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE ); // finds contours on bin image

Scalar color( 255,255,255 );
for( int i = 0; i< contours.size(); i++ ) // iterate through each contour. 
  {
   if((contourArea(contours[i],false))>100){ // if counter area >100 pixel draw it on ero which is new image variable
    drawContours( bin, contours, i , color, CV_FILLED, 8, hierarchy ); //Draw contours on itself as filled
                                            }
   }

 findContours( bin, contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE );

 for( int i = 0; i< contours.size(); i++ ) // iterate through each contour. 
  {
      bounding_rect=boundingRect(contours[i]); //Bound and Draw rectangle each object which detected at the end on src(original image)
      rectangle(src, bounding_rect,  Scalar(0,255,0),3, 8,0);  
   }
   namedWindow("Binary",CV_WINDOW_NORMAL);
   imshow("Binary",src);
   cout<<contours.size();
   waitKey(0);
   return 0;
}

这篇关于OpenCV - 如何计数照片中的对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆