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
How to find height and width for each individual contour on an image using OpenCV
Asked Answered
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.
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()
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:
Detected rectangles for each line:
Line lengths by pixel:
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
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:
- Creating a binary image by using the threshold method
- Using findContours method to find the contour of the rectangles in the image
- Get the size of the rectangle contour by using the boundingRect method
- Multiply the with of the contour by the calculated inch per pixel factor
© 2022 - 2024 — McMap. All rights reserved.