To find the number of circles in an image using OpenCV - python

I have an image as below :
Can anyone tell me how to detect the number of circles in it.I'm using Hough circle transform to achieve this and this is my code:
# import the necessary packages
import numpy as np
import sys
import cv2
# load the image, clone it for output, and then convert it to grayscale
image = cv2.imread(str(sys.argv[1]))
output = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# detect circles in the image
circles = cv2.HoughCircles(gray, cv2.cv.CV_HOUGH_GRADIENT, 1.2, 5)
no_of_circles = 0
# ensure at least some circles were found
if circles is not None:
# convert the (x, y) coordinates and radius of the circles to integers
circles = np.round(circles[0, :]).astype("int")
no_of_circles = len(circles)
# loop over the (x, y) coordinates and radius of the circles
for (x, y, r) in circles:
# draw the circle in the output image, then draw a rectangle
# corresponding to the center of the circle
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.rectangle(output, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)
# show the output image
cv2.imshow("output", np.hstack([image, output]))
print 'no of circles',no_of_circles
I'm getting wrong answers for this code.Can anyone tell me where I went wrong?

i tried a tricky way to detect all circles.
i found HoughCircles parameters manually
HoughCircles( src_gray, circles, HOUGH_GRADIENT, 1, 50, 40, 46, 0, 0 );
the tricky part is
flip( src, flipped, 1 );
hconcat( src,flipped, flipped );
hconcat( flipped, src, src );
flip( src, flipped, 0 );
vconcat( src,flipped, flipped );
vconcat( flipped, src, src );
flip( src, src, -1 );
will create a model like below before detection.
the result is like this
the c++ code can be easily converted to python
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char** argv)
{
Mat src, src_gray, flipped, display;
if (argc < 2)
{
std::cerr<<"No input image specified\n";
return -1;
}
// Read the image
src = imread( argv[1], 1 );
if( src.empty() )
{
std::cerr<<"Invalid input image\n";
return -1;
}
flip( src, flipped, 1 );
hconcat( src,flipped, flipped );
hconcat( flipped, src, src );
flip( src, flipped, 0 );
vconcat( src,flipped, flipped );
vconcat( flipped, src, src );
flip( src, src, -1 );
// Convert it to gray
cvtColor( src, src_gray, COLOR_BGR2GRAY );
// Reduce the noise so we avoid false circle detection
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
// will hold the results of the detection
std::vector<Vec3f> circles;
// runs the actual detection
HoughCircles( src_gray, circles, HOUGH_GRADIENT, 1, 50, 40, 46, 0, 0 );
// clone the colour, input image for displaying purposes
display = src.clone();
Rect rect_src(display.cols / 3, display.rows / 3, display.cols / 3, display.rows / 3 );
rectangle( display, rect_src, Scalar(255,0,0) );
for( size_t i = 0; i < circles.size(); i++ )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
Rect r = Rect( center.x-radius, center.y-radius, radius * 2, radius * 2 );
Rect intersection_rect = r & rect_src;
if( intersection_rect.width * intersection_rect.height > r.width * r.height / 3 )
{
// circle center
circle( display, center, 3, Scalar(0,255,0), -1, 8, 0 );
// circle outline
circle( display, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
}
// shows the results
imshow( "results", display(rect_src));
// get user key
waitKey();
return 0;
}

This SO post describes detection of semi-circles, and may be a good start for you:
Detect semi-circle in opencv
If you get stuck in OpenCV, try coding up the solution yourself. Writing a Hough circle finder parameterized for your particular application is relatively straightforward. If you write application-specific Hough algorithms a few times, you should be able to write a reasonable solution in less time than it takes to sort through a bunch of google results, decipher someone else's code, and so on.
You definitely don't need Canny edge detection for an image like this, but it won't hurt.
Other libraries (esp. commercial ones) will allow you to set more parameters for Hough circle finding. I would've expected some overload of the HoughCircle function to allow a struct of search parameters to be passed in, including the minimum percentage of circle completeness (arc length) allowed.
Although it's good to learn both RANSAC and Hough techniques--and, over time, more exotic techniques--I wouldn't necessarily recommend using RANSAC when you have circles defined so nicely and crisply. Without offering specific evidence, I'll just claim that fiddling with RANSAC parameters may be less intuitive than fiddling with Hough parameters.

HoughCircles needs some parameter tuning to work properly.
It could be that in your case the default values of Param1 and Param2 (set to 100) are not good.

You can fine tune your detection with HoughCircle, by computing the ultimate eroded. It will give you the number of circles in your image.

If there are only circles and background on the input you can count the number of connected components and ignore the component associated with background. This will be the simplest and most robust solution

Related

OpenCV HoughCircles parameters for detecting circles (microstructure spheres)

I am creating program that helps processing microstructure images. One of the function is detecting circles with the same radius. User draws one circle, my program spots others. I've already implemented distance transform method
I am trying to create method that uses HoughCircles. However, I am confused with its parameters.
My code:
def find_circles_with_radius_haugh(path, radius):
img = cv2.imread(path)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(img_gray, cv2.HOUGH_GRADIENT, int(radius),
1.5,
param1=80, param2=40,
minRadius=int(radius * 0.9),
maxRadius=int(radius * 1.1))
res = list()
if circles is not None:
for i in circles[0, :]:
res.append((i[0], i[1], i[2]))
return res
Original picture:
My result of detecting circles with radius 57 pixels (+- 10%):
Please help me with better processing images like that.
I might try findContours method, but I don't know any filters that will make borders on this picture clearer.
I tried a little.
My idea is simply using filter2D instead of Hough-Transform.
Because detection target is the circles has specific radius, if edge of circles detected clearly, the center of the circles will be able to found by convoluting circular mask to the edge image.
I checked the filter2D(=convolution) result with following code (C++).
int main()
{
//This source image "MicroSpheres.png" was copied from this question
cv::Mat Src = cv::imread( "MicroSpheres.png", cv::IMREAD_GRAYSCALE );
if( Src.empty() )return 0;
//Test with 50% Scale
cv::resize( Src, Src, cv::Size(0,0), 0.5, 0.5, cv::INTER_AREA );
cv::imshow( "Src", Src );
const int Radius = cvRound(57 * 0.5); //So, Radius is also 50% scale
//Trying to detect edge of circles
cv::Mat EdgeImg;
{
cv::Mat Test;
cv::medianBlur( Src, Test, 5 );
cv::morphologyEx( Test, Test, cv::MORPH_GRADIENT, cv::Mat() );
cv::imshow( "Test", Test );
cv::adaptiveThreshold( Test, EdgeImg, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, (Test.rows/6)|0x01, -6 );
cv::imshow( "EdgeImg", EdgeImg );
}
cv::Mat BufferFor_imwrite = EdgeImg.clone();
//filter2D
cv::Mat FilterResult;
{
const int FilterRadius = Radius + 2;
const int FilterSize = FilterRadius*2 + 1;
cv::Mat Filter = cv::Mat::zeros( FilterSize,FilterSize, CV_32F );
cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius/2, cv::Scalar(-1), -1 );
cv::circle( Filter, cv::Point(FilterRadius,FilterRadius), Radius, cv::Scalar(1), 3 );
cv::filter2D( EdgeImg, FilterResult, CV_32F, Filter );
}
{//Very lazy check of the filter2D result.
double Min, Max;
cv::minMaxLoc( FilterResult, &Min, &Max );
double scale = 255 / (Max-Min);
cv::Mat Show;
FilterResult.convertTo( Show, CV_8U, scale, -Min*scale );
cv::imshow( "Filter2D_Result", Show );
cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );
//(Estimating center of circles based onthe filter2D result.)
// Here, just only simple thresholding is implemented.
// At least non-maximum suppression must be done, I think.
cv::Mat Centers;
cv::threshold( FilterResult, Centers, (Max+Min)*0.6, 255, cv::THRESH_BINARY );
Centers.convertTo( Centers, CV_8U );
Show = Src * 0.5;
Show.setTo( cv::Scalar(255), Centers );
cv::imshow( "Centers", Show );
cv::vconcat( BufferFor_imwrite, Show, BufferFor_imwrite );
}
if( cv::waitKey() == 's' ){ cv::imwrite( "Result.png", BufferFor_imwrite ); }
return 0;
}
The following image is result. 3 images are concatenated vertically.
edge detection result
filter2D result
Circle center estimation result (very lazy. just binarized the filter2D result and overlapped it onto source image.)
I can't say this is perfect, but it looks like that the result roughly indicates some centers.
Rewrote #fana code in Python
import cv2
import numpy as np
img = cv2.imread('spheres1.bmp')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (0, 0), gray, 0.5, 0.5, cv2.INTER_AREA)
cv2.imwrite("resized.png", gray)
radius = round(57 * 0.5)
test = cv2.medianBlur(gray, 5)
struct_elem = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# might be better to use "I" matrix
# struct_elem = np.ones((3,3), np.uint8)
test = cv2.morphologyEx(test, cv2.MORPH_GRADIENT, kernel=struct_elem)
cv2.imwrite("MorphologyEx.png", test)
edge_img = cv2.adaptiveThreshold(test, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, int(len(test) / 6) | 0x01, -6)
cv2.imwrite("EdgeImg.png", edge_img );
buffer_for_imwrite = edge_img.copy()
filter_radius = radius + 2
filter_size = filter_radius * 2 + 1
img_filter = np.zeros((filter_size, filter_size))
cv2.circle(img_filter, (filter_radius, filter_radius), int(radius / 2), -1, -1)
cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 3)
# second circle better to generate with smaller width like this:
# cv2.circle(img_filter, (filter_radius, filter_radius), radius, 1, 2)
cv2.imwrite("Filter.png", img_filter)
filter_result = cv2.filter2D(edge_img, cv2.CV_32F, img_filter)
cv2.imwrite("FilterResult.png", filter_result)
min_val, max_val, _, _ = cv2.minMaxLoc(filter_result)
scale = 255 / (max_val - min_val)
show = np.uint8(filter_result * scale - min_val * scale)
cv2.imwrite("Filter2D_Result.png", show)
_, centers = cv2.threshold(filter_result, (max_val + min_val) * 0.6, 255, cv2.THRESH_BINARY)
centers = np.uint8(centers)
show = gray * 0.5
show[np.where(centers == 255)] = 255
cv2.imwrite("Centers.png", show)

Detecting small squares attached to something else on the image

I want to detect small squares circled in red on the image. But the problem is that they are on another white line. I want to know how to separate those squares from the white line and detect them.
I have used OpenCV Python to write the code. What I have done until now is that I cropped the image so that I get access only to the circular part of the image. Then I cropped the image to get the required part that is the white line. Then I used erosion so that the white line will vanish and the squares remain in the image. Then used Hough circles to detect the squares. This does work for some images but it cannot be generalized. Please help me in finding a generalized code for this. Let me know the logic and also the python code.
Also could anyone help me detect that aruco marker on the image. Its getting rejected. I dont know why.
Image is in this link. Detect small squares on an image
here's C++ code with distanceTransform.
Since I nearly only used openCV functions, you can probably easily convert it to Python code.
I removed the white stripe at the top of the image manually, hope this isn't a problem.
int main()
{
cv::Mat input = cv::imread("C:/StackOverflow/Input/SQUARES.png", cv::IMREAD_GRAYSCALE);
cv::Mat thres = input > 0; // make binary mas
cv::Mat dst;
cv::distanceTransform(thres, dst, CV_DIST_L2, 3);
double min, max;
cv::Point minPt, maxPt;
cv::minMaxLoc(dst, &min, &max, 0, 0);
double distThres = max*0.65; // a real clustering would be better. This assumes that the white circle thickness is a bout 50% of the square size, so 65% should be ok...
cv::Mat squaresMask = dst >= distThres;
cv::imwrite("C:/StackOverflow/Input/SQUARES_mask.png", squaresMask);
std::vector<std::vector<cv::Point> > contours;
cv::findContours(squaresMask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
cv::Mat output;
cv::cvtColor(input, output, cv::COLOR_GRAY2BGR);
for (int i = 0; i < contours.size(); ++i)
{
cv::Point2f center;
float radius;
cv::minEnclosingCircle(contours[i], center, radius);
cv::circle(output, center, 5, cv::Scalar(255, 0, 255), -1);
//cv::circle(output, center, radius, cv::Scalar(255, 0, 255), 1);
}
cv::imwrite("C:/StackOverflow/Input/SQUARES_output.png", output);
cv::imshow("output", output);
cv::waitKey(0);
}
this is the input:
this it the squaresMask after distance transform
and this is the result

How to measure the length of a curved contour [duplicate]

This is regarding a project that concerns detection of text in an image using OpenCV in C. The process is to detect the colors inside and outside the corresponding contours and the way to do that is to draw normals on the contours in equal spaced positions and extract the pixel colors in the corresponding positions of the normals end-points.
I am trying to implement this using the following code but it's not working. I mean, its drawing the normals but not in and equi-spaced fashion.
for( ; contours!=0 ; contours = contours->h_next )
{
CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 );
cvDrawContours( cc_color, contours, color, CV_RGB(0,0,0), -1, 1, 8, cvPoint(0,0) );
ptr = contours;
for( i=1; i<ptr->total; i++)
{
p1 = CV_GET_SEQ_ELEM( CvPoint, ptr, i );
p2 = CV_GET_SEQ_ELEM( CvPoint, ptr, i+1 );
x1 = p1->x;
y1 = p1->y;
x2 = p2->x;
y2 = p2->y;
printf("%d %d %d %d\n",x1,y1,x2,y2);
draw_normals(x1,y1,x2,y2);
}
}
So is there a way to find the length of a contour so that I can divide it by the number of normals I want to draw. Thanks in advance.
EDIT: The draw_normal function draws the normals between two points passed to it as parameters.
So is there a way to find the length of a contour?
Yes, you can find length of a contour using OpenCV standard function , cvarcLength().
Check Documentation here.

Map contour points to original image

I've performed some basic image operations and managed to isolate the object I am interested in.
As a result, I ended up with a container; get<0>(results) holding the corresponding (x, y) of the object. For visual purposes, I drew these points on a different frame named contourFrame
My question is, how do I map these points back to the original image?
I have looked into the combination of findHomography() and warpPerspective but in order to find the homography matrix, I need to provide respective destination points which is what I am looking for in the first place.
remap() seems to do what I am looking for but I think its a misunderstanding of the API on my part but after trying it out, it returns a blank white frame.
Is remap() even the way to go about this? If yes, how? If no, what other method can I use to map my contour points to another image?
A Python solution/suggestion works with me as well.
Edit I
For the current scenario, its a 1-1 mapping back to the original image. But what if I rotate the original image? Or move my camera back and forth i.e. move it closer and further away? How can I still map back the coordinates to the newly oriented original image?
Original Image
Results
Top Left: Canny Edge
Top Right: Dilated
Bottom Left: Eroded
Bottom Right: Frame with desired object/contour
tuple<vector<vector<Point>>, Mat, Mat> removeStupidIcons(Mat& edges)
{
Mat dilated, eroded;
vector<vector<Point>> contours, filteredContours;
dilate(edges, dilated, Mat(), Point(-1, -1), 5);
erode(dilated, eroded, Mat(), Point(-1, -1), 5);
findContours(eroded, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
for(vector<Point>contour: contours)
if(contourArea(contour) < 200)
filteredContours.push_back(contour);
return make_tuple(filteredContours, dilated, eroded);
}
Mat mapPoints(Mat& origin, Mat& destination)
{
Mat remapWindow, mapX, mapY;
mapX.create(origin.size(), CV_32FC1);
mapY.create(origin.size(), CV_32FC1);
remap(origin, destination, mapX, mapY, CV_INTER_LINEAR);
return destination;
}
int main(int argc, const char * argv[])
{
string image_path = "ipad.jpg";
original = imread(image_path);
blur(original, originalBlur, Size(15, 15));
cvtColor(originalBlur, originalGrey, CV_RGB2GRAY);
Canny(originalGrey, cannyEdges, 50, 130, 3);
cannyEdges.convertTo(cannyFrame, CV_8U);
tuple<vector<vector<Point>>, Mat, Mat> results = removeStupidIcons(cannyEdges);
//
// get<0>(results) -> contours
// get<1>(results) -> matrix after dilation
// get<2>(results) -> matrix after erosion
Mat contourFrame = Mat::zeros(original.size(), CV_8UC3);
Scalar colour = Scalar(rand()&255, rand()&255, rand()&255);
drawContours(contourFrame, get<0>(results), -1, colour, 3);
Mat contourCopy, originalCopy;
original.copyTo(originalCopy); contourFrame.copyTo(contourCopy);
// a white background is returned.
Mat mappedFrame = mapPoints(originalCopy, contourCopy);
imshow("Canny", cannyFrame);
imshow("Original", original);
imshow("Contour", contourFrame);
imshow("Eroded", get<2>(results));
imshow("Dilated", get<1>(results));
imshow("Original Grey", originalGrey);
imshow("Mapping Result", contourCopy);
waitKey(0);
return 0;
}

How to adapt or resize a rectangle inside an object without including (or with a few numbers) of background pixels?

After I applied thresholding and finding the contours of the object, I used the following code to get the straight rectangle around the object (or the rotated rectangle inputting its instruction):
img = cv2.imread('image.png')
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(imgray,127,255,cv2.THRESH_BINARY)
# find contours
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
# straight rectangle
x,y,w,h = cv2.boundingRect(cnt)
img= cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
see the image
Then I have calculated the number of object and background pixels inside the straight rectangle using the following code:
# rectangle area (total number of object and background pixels inside the rectangle)
area_rect = w*h
# white or object pixels (inside the rectangle)
obj = cv2.countNonZero(imgray)
# background pixels (inside the rectangle)
bac = area_rect - obj
Now I want to adapt the rectangle of the object as a function of the relationship between the background pixel and those of the object, ie to have a rectangle occupying the larger part of the object without or with less background pixel, for example:
How do I create this?
This problem can be stated as the find the largest rectangle inscribed in a non-convex polygon.
An approximate solution can be found at this link.
This problem can be formulated also as: for each angle, find the largest rectangle containing only zeros in a matrix, explored in this SO question.
My solution is based on this answer. This will find only axis aligned rectangles, so you can easily rotate the image by a given angle and apply this solution for every angle.
My solution is C++, but you can easily port it to Python, since I'm using mostly OpenCV function, or adjust the solution in the above mentioned answer accounting for rotation.
Here we are:
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
// https://stackoverflow.com/a/30418912/5008845
Rect findMinRect(const Mat1b& src)
{
Mat1f W(src.rows, src.cols, float(0));
Mat1f H(src.rows, src.cols, float(0));
Rect maxRect(0,0,0,0);
float maxArea = 0.f;
for (int r = 0; r < src.rows; ++r)
{
for (int c = 0; c < src.cols; ++c)
{
if (src(r, c) == 0)
{
H(r, c) = 1.f + ((r>0) ? H(r-1, c) : 0);
W(r, c) = 1.f + ((c>0) ? W(r, c-1) : 0);
}
float minw = W(r,c);
for (int h = 0; h < H(r, c); ++h)
{
minw = min(minw, W(r-h, c));
float area = (h+1) * minw;
if (area > maxArea)
{
maxArea = area;
maxRect = Rect(Point(c - minw + 1, r - h), Point(c+1, r+1));
}
}
}
}
return maxRect;
}
RotatedRect largestRectInNonConvexPoly(const Mat1b& src)
{
// Create a matrix big enough to not lose points during rotation
vector<Point> ptz;
findNonZero(src, ptz);
Rect bbox = boundingRect(ptz);
int maxdim = max(bbox.width, bbox.height);
Mat1b work(2*maxdim, 2*maxdim, uchar(0));
src(bbox).copyTo(work(Rect(maxdim - bbox.width/2, maxdim - bbox.height / 2, bbox.width, bbox.height)));
// Store best data
Rect bestRect;
int bestAngle = 0;
// For each angle
for (int angle = 0; angle < 90; angle += 1)
{
cout << angle << endl;
// Rotate the image
Mat R = getRotationMatrix2D(Point(maxdim,maxdim), angle, 1);
Mat1b rotated;
warpAffine(work, rotated, R, work.size());
// Keep the crop with the polygon
vector<Point> pts;
findNonZero(rotated, pts);
Rect box = boundingRect(pts);
Mat1b crop = rotated(box).clone();
// Invert colors
crop = ~crop;
// Solve the problem: "Find largest rectangle containing only zeros in an binary matrix"
// https://stackoverflow.com/questions/2478447/find-largest-rectangle-containing-only-zeros-in-an-n%C3%97n-binary-matrix
Rect r = findMinRect(crop);
// If best, save result
if (r.area() > bestRect.area())
{
bestRect = r + box.tl(); // Correct the crop displacement
bestAngle = angle;
}
}
// Apply the inverse rotation
Mat Rinv = getRotationMatrix2D(Point(maxdim, maxdim), -bestAngle, 1);
vector<Point> rectPoints{bestRect.tl(), Point(bestRect.x + bestRect.width, bestRect.y), bestRect.br(), Point(bestRect.x, bestRect.y + bestRect.height)};
vector<Point> rotatedRectPoints;
transform(rectPoints, rotatedRectPoints, Rinv);
// Apply the reverse translations
for (int i = 0; i < rotatedRectPoints.size(); ++i)
{
rotatedRectPoints[i] += bbox.tl() - Point(maxdim - bbox.width / 2, maxdim - bbox.height / 2);
}
// Get the rotated rect
RotatedRect rrect = minAreaRect(rotatedRectPoints);
return rrect;
}
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// Compute largest rect inside polygon
RotatedRect r = largestRectInNonConvexPoly(img);
// Show
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
Point2f points[4];
r.points(points);
for (int i = 0; i < 4; ++i)
{
line(res, points[i], points[(i + 1) % 4], Scalar(0, 0, 255), 2);
}
imshow("Result", res);
waitKey();
return 0;
}
The result image is:
NOTE
I'd like to point out that this code is not optimized, so it can probably perform better. For an approximized solution, see here, and the papers reported there.
This answer to a related question put me in the right direction.
There's now a python library calculating the maximum drawable rectangle inside a polygon.
Library: maxrect
Install through pip:
pip install git+https://${GITHUB_TOKEN}#github.com/planetlabs/maxrect.git
Usage:
from maxrect import get_intersection, get_maximal_rectangle, rect2poly
# For a given convex polygon
coordinates1 = [ [x0, y0], [x1, y1], ... [xn, yn] ]
coordinates2 = [ [x0, y0], [x1, y1], ... [xn, yn] ]
# find the intersection of the polygons
_, coordinates = get_intersection([coordinates1, coordinates2])
# get the maximally inscribed rectangle
ll, ur = get_maximal_rectangle(coordinates)
# casting the rectangle to a GeoJSON-friendly closed polygon
rect2poly(ll, ur)
Source: https://pypi.org/project/maxrect/
here is a python code I wrote with rotation included. I tried to speed it up, but I guess it can be improved.
For future googlers,
Since your provided sample solution allows background pixels to be within the rectangle, I suppose you wish to find the the smallest rectangle that covers perhaps 80% of the white pixels.
This can be done using a similar method of finding the error ellipse given a set of data (in this case, the data is all the white pixels, and the error ellipse needs to be modified to be a rectangle)
The following links would hence be helpful
How to get the best fit bounding box from covariance matrix and mean position?
http://www.visiondummy.com/2014/04/draw-error-ellipse-representing-covariance-matrix/

Categories

Resources