4

I'm trying to count object from image. I use logs photo, and I use some steps to get a binary image. enter image description here

This is my code:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <features2d.hpp>
using namespace cv;
using namespace std;
int main(int argc, char *argv[])
{
    //load image
    Mat img = imread("kayu.jpg", CV_LOAD_IMAGE_COLOR);
    if(img.empty())
       return -1;
    //namedWindow( "kayu", CV_WINDOW_AUTOSIZE );
    imshow("kayu", img);

    //convert to b/w
    Mat bw;
    cvtColor(img, bw, CV_BGR2GRAY);
    imshow("bw1", bw);

    threshold(bw, bw, 40, 255, CV_THRESH_BINARY);
    imshow("bw", bw);

    //distance transform & normalisasi
    Mat dist;
    distanceTransform(bw, dist, CV_DIST_L2, 3);
    normalize(dist, dist, 0, 2., NORM_MINMAX);
    imshow("dist", dist);

    //threshold to draw line
    threshold(dist, dist, .5, 1., CV_THRESH_BINARY);
    imshow("dist2", dist);

    //dist = bw;
    //dilasi
    Mat dilation, erotion, element;
    int dilation_type = MORPH_ELLIPSE;
    int dilation_size = 17;

    element = getStructuringElement(dilation_type, Size(2*dilation_size + 1, 2*dilation_size+1), Point(dilation_size, dilation_size ));
    erode(dist, erotion, element);
    int erotionCount = 0;
    for(int i=0; i<erotionCount; i++){
        erode(erotion, erotion, element);
    }
    imshow("erotion", erotion);

    dilate(erotion, dilation, element);
    imshow("dilation", dilation);
    waitKey(0);
    return 0;
}

As you can see, I use Erosion and Dilation to get better circular object of log. My problem is, I'm stuck at counting the object. I tried SimpleBlobDetector but I got nothing, because when I try to convert the result of "dilation" step to CV_8U, the white object disappear. I got error too when I use findContours(). It say something about channel of image. I can't show the error here, because that's too many step and I already delete it from my code.

Btw, at the end, i got 1 channel of image. enter image description here Can i just use it to counting, or am i have to convert it and what is the best method to do it?

Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
Reza Kahfi
  • 107
  • 2
  • 12
  • Possible duplicate of [OpenCV how to find a list of connected components in a binary image](https://stackoverflow.com/questions/17565180/opencv-how-to-find-a-list-of-connected-components-in-a-binary-image) – lakshayg Nov 05 '18 at 07:50

3 Answers3

3

Two simple steps:

  1. Find contours for the binarized image.
  2. Get the count of the contours.

Code:

int count_trees(const cv::Mat& bin_image){
    cv::Mat img;
    if(bin_image.channels()>1){
        cv::cvtColor(bin_image,img,cv::COLOR_BGR2GRAY);
    }
    else{
         img=bin_image.clone();;
    }
    if(img.type()!=CV_8UC1){
        img*=255.f; //This could be stupid, but I do not have an environment to try it
        img.convertTo(img,CV_8UC1);
    }

    std::vector<std::vector<cv::Point>> contours
    std::vector<Vec4i> hierarchy;
    cv::findContours( img, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
    return contours.size();
}
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • Thank you for your response. I tried your code, but I still got an error about unsupported format. The error message kinda like this "Unsopported format or combination of formats, findContours() support only CV_8u". – Reza Kahfi May 31 '16 at 09:02
  • cvtColor(img, bw, CV_BGR2GRAY); threshold(bw, bw, 40, 255, CV_THRESH_BINARY); Those lines ensure that your image is CV_8UC1 since it was loaded as normal color. There is another code that causes that problem. you should show us to know – Humam Helfawi May 31 '16 at 09:05
  • I've shown all my code at the question. I really want to know why that's happen too. I create new function from your code and the input parameter is dilation. – Reza Kahfi May 31 '16 at 09:11
  • Oh sorry! I just notice that you are using it after the distance transform which is changing the depth. I will edit my answer – Humam Helfawi May 31 '16 at 09:13
  • Same like before. This is the error log https://www.dropbox.com/s/nt1q8e1m7f1t5fu/error.JPG?dl=0 – Reza Kahfi May 31 '16 at 09:27
  • 1
    Contour finding is unnecessary. This problem can be efficiently solved using connected component analysis. OpenCV has a “labeling” algorithm that does this. – Cris Luengo Nov 05 '18 at 00:17
0

I have the same problem, here's an idea I'm about to implement.

1) Represent your image as an array of integers; 0 = black, 1 = white.

2) set N = 2;

3) Scan your image, pixel-by-pixel. Whenever you find a white pixel, activate a flood-fill algorithm, starting at the pixel just found; paint the region with the value of N++;

4) Iterate 3 until you reach the last pixel. (N-2) is the number of regions found.

This method depends on the shape of the objects; mine are more chaotic than yours (wish me luck..). I'll make use of a recursive flood-fill recipe found somewhere (maybe Rosetta Code).

This solution also makes it easy to compute the size of each region.

qiAlex
  • 4,290
  • 2
  • 19
  • 35
A Koscianski
  • 115
  • 1
  • 5
  • This is connected component analysis. It’s the correct approach. But there are more efficient algorithms. Search the OpenCV docs for “label”. – Cris Luengo Nov 05 '18 at 00:14
-1

try to apply that on the your deleted img

// count
for (int i = 0; i< contours.size(); i = hierarchy[i][0]) // iteration sur chaque contour .
{
    Rect r = boundingRect(contours[i]);
    if (hierarchy[i][2]<0) {
        rectangle(canny_output, Point(r.x, r.y), Point(r.x + r.width, r.y + r.height), Scalar(20, 50, 255), 3, 8, 0);
        count++;
    }
}
cout << "Numeber of contour = " << count << endl;
imshow("src", src);
imshow("contour", dst);
waitKey(0);
Sasikumar Murugesan
  • 4,412
  • 10
  • 51
  • 74