How to find height and width for each individual contour on an image using OpenCV
Asked Answered
A

3

10

img link

In the above image, if the entire width is specified say 30'5". How do I calculate height and width for each individual contour on that image using opencv

Arjun answered 10/2, 2020 at 11:54 Comment(0)
J
10

To obtain the height and width of a contour, you can use cv2.boundingRect. The function returns the contour information in the form of x,y,w,h. The height for a specific contour will be h and the width will be w. Here's the result with the w in pixels drawn onto the image.

enter image description here

import cv2

# Load image, convert to grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find contours, obtain bounding rect, and draw width
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    cv2.putText(image, str(w), (x,y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (36,255,12), 2)
    cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 1)

cv2.imshow('image', image)
cv2.waitKey()
Jallier answered 10/2, 2020 at 21:57 Comment(0)
C
2

My approach is using minAreaRect:

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;


int main()
{

    Mat src; Mat src_gray;
    int thresh = 100;
    RNG rng(12345);
    /// Load source image and convert it to gray
    src = imread( "/ur/img/directory/image.jpg", 1 );
    Mat original = src.clone();
    /// Convert image to gray and blur it
    cvtColor( src, src_gray, CV_BGR2GRAY );

    Mat threshold_output;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    /// Detect edges using Threshold
    threshold( src_gray, threshold_output, thresh, 255, THRESH_BINARY );
    /// Find contours
    findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

    /// Find the rotated rectangles for each contour
    vector<RotatedRect> minRect( contours.size() );

    for( int i = 0; i < contours.size(); i++ )
        minRect[i] = minAreaRect( Mat(contours[i]) );

    /// Draw contours + rotated rects
    Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
    Mat result_zero = Mat::zeros( threshold_output.size(), CV_8UC3 );

    for( int i = 0; i< contours.size(); i++ )
    {
        Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
        // detect contours
        drawContours( drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point() );
        // detect rectangle for each contour
        Point2f rect_points[4]; minRect[i].points( rect_points );

        double length_1 = cv::norm(cv::Mat(rect_points[0]),cv::Mat(rect_points[1]));
        double length_2 = cv::norm(cv::Mat(rect_points[1]),cv::Mat(rect_points[2]));


        for( int j = 0; j < 4; j++ )
        {
            int temp1 = (int)length_1;
            int temp2 = (int)length_2;

            if(length_1>length_2)
                putText(original,to_string(temp1),rect_points[0],FONT_HERSHEY_SIMPLEX,1.0,Scalar(0,255,255),2);
            else
                putText(original,to_string(temp2),rect_points[0],FONT_HERSHEY_SIMPLEX,1.0,Scalar(0,255,255),2);

            line( result_zero, rect_points[j], rect_points[(j+1)%4], color, 1, 8 );
        }

    }

    /// Show in windows
    imshow("First",original);
    imshow( "Contours", drawing );
    waitKey(0);
    return(0);
}

Source image:

enter image description here

Detected rectangles for each line:

enter image description here

Line lengths by pixel:

enter image description here

Convenience answered 10/2, 2020 at 12:48 Comment(2)
Great solution! but from next time how about using python as it is more readily testable. most people might not have a c++ environment setup!Roughage
Its not a big deal to convert it to python. There is no difficulty to convert it. My environment is just c++ based. Thats why i answered in c++Convenience
R
0
std::vector<std::vector<cv::Point2i>> vecContours;
cv::Mat mat = cv::imread("[path to image]", cv::IMREAD_GRAYSCALE);
cv::threshold(mat, mat, 200, 255, cv::THRESH_BINARY);
cv::findContours(mat, vecContours, cv::RetrievalModes::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
float inchPerPixel = 30.5f / mat.cols;
for (const std::vector<cv::Point2i>& vecContour : vecContours) {
    cv::Rect2i contourRect = cv::boundingRect(vecContour);
    printf("Contour width pixels : %d, width inches %f\n", contourRect.width, inchPerPixel*contourRect.width);
}

You can achieve this by:

  1. Creating a binary image by using the threshold method
  2. Using findContours method to find the contour of the rectangles in the image
  3. Get the size of the rectangle contour by using the boundingRect method
  4. Multiply the with of the contour by the calculated inch per pixel factor
Retro answered 10/2, 2020 at 12:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.