How can I get information from circular ROI using dm script? - image-processing

After making a circular ROI in an image, how can I get the information (average, standar deviation, variance) from that image region using script?
Can I link the position in the ciruclar ROI with original image?

This task is unfortunately not as straight forward and easy as one would hope.
While scripting supports a convenient shortcut to restrict image operations to rectangular ROIs ( using the img[] notation ), there is nothing like that for irregular ROIs.
In such a case, one has to manually create a binary mask of a ROI and perform the wanted operations manually. The example script at the bottom of this post shows how the average value of an irregular ROI may be computed.
CreateImageWithROI() Creates a test image with two ROIs on it
GetFirstIrregularROIOfImage() just returns the first found, irregular ROI of an image
GetROIMean() is the actual example
The command ROIAddToMask() is used to create the mask. Note, that there is also a similar command which would perform the action with all ROIs of an image display at once: ImageDisplayAccumulateROIsToMask()
So far, so good.
However, it turns out that the newly introduced Circular ROIs do not yet support the mask-creation commands correctly (Tested with GMS 3.1).
Instead, they always use the bounding rectangle of the ROI:
It is therefore necessary to go even one step back and read the ROI's coordinates to create a mask from it manually. Get the ROI's bounding-box and create a mask using an icol and irow expression for an ellipse. In the example below:
GetFirstOvalROIOfImage() just returns the first found, oval ROI of an image
MyAddOvalROIToMask() is the manual mask creation for oval ROIs
Example code:
image CreateImageWithROI()
{
// Create and show image
number sx = 256, sy = 256
image img := RealImage( "Image", 4, sx, sy )
img = sin( 0.1 * iradius ) * cos( 7 * itheta )
img.ShowImage()
// Create an irregular, closed ROI
ROI myIrRoi = NewROI()
myIrRoi.ROIAddVertex( 0.3 * sx, 0.1 * sy )
myIrRoi.ROIAddVertex( 0.7 * sx, 0.2 * sy )
myIrRoi.ROIAddVertex( 0.5 * sx, 0.6 * sy )
myIrRoi.ROIAddVertex( 0.1 * sx, 0.8 * sy )
myIrRoi.ROISetIsClosed(1)
myIRRoi.ROISetVolatile(0)
// Create an oval ROI
ROI myOvalROI = NewROI()
myOvalROI.ROISetOval( 0.7 * sy, 0.7 * sx, 0.9 * sy, 0.8 * sx )
myOvalROI.ROISetVolatile(0)
// AddROIs
imageDisplay disp = img.ImageGetImageDisplay( 0 )
disp.ImageDisplayAddROI( myIRRoi )
disp.ImageDisplayAddROI( myOvalROI )
return img
}
ROI GetFirstIrregularROIOfImage( image img )
{
if ( img.ImageIsValid() )
{
if ( 0 != img.ImageCountImageDisplays() )
{
imageDisplay disp = img.ImageGetImageDisplay( 0 )
number nRois = disp.ImageDisplayCountROIs()
for ( number i = 0; i < nRois; i++ )
{
ROI testROI = disp.ImageDisplayGetRoi( i )
number isIrregularClosed = 1
isIrregularClosed *= testROI.ROIIsClosed();
isIrregularClosed *= !testROI.ROIIsOval();
isIrregularClosed *= !testROI.ROIIsRectangle();
isIrregularClosed *= ( 2 < testROI.ROICountVertices());
if ( isIrregularClosed )
return testROI
}
}
}
Throw( "No irregular ROI found" )
}
ROI GetFirstOvalROIOfImage( image img )
{
if ( img.ImageIsValid() )
{
if ( 0 != img.ImageCountImageDisplays() )
{
imageDisplay disp = img.ImageGetImageDisplay( 0 )
number nRois = disp.ImageDisplayCountROIs()
for ( number i = 0; i < nRois; i++ )
{
ROI testROI = disp.ImageDisplayGetRoi( i )
if ( testROI.ROIIsOval() )
return testROI
}
}
}
Throw( "No oval ROI found" )
}
void MyAddOvalROIToMask( image img, ROI ovalROI )
{
number top, left, bottom, right
ovalROI.ROIGetOval( top, left, bottom, right )
number sx = ( right - left )
number sy = ( bottom - top )
number cx = sx/2 // Used as both center x coordiante and x radius!
number cy = sy/2 // Used as both center y coordiante and y radius!
// Create mask of just the rect area
image maskCut := RealImage( "", 4, sx, sy )
maskCut = ( ((cx-icol)/cx)**2 + ((cy-irow)/cy)**2 <= 1 ) ? 1 : 0
// Apply mask to image
img[top, left, bottom, right] = maskCut
}
number GetROIMean( image img, ROI theRoi )
{
if ( !img.ImageIsValid() ) Throw( "Invalid image in GetROIMean()" )
if ( !theRoi.ROIIsValid() ) Throw( "Invalid roi in GetROIMean()" )
// Create a binary mask of "img" size using the ROI's coordinates
image mask = img * 0; // image of same size as "img" with 0 values
number sx, sy
img.GetSize( sx, sy )
// Oval ROIs are not supported by the command correctly
// Hence check and compute mask manually..
if ( theROI.ROIIsOval() )
MyAddOvalROIToMask( mask, theROI )
else
theROI.ROIAddToMask( mask, 0, 0, sx, sy )
if ( TwoButtonDialog( "Show mask?", "Yes", "No" ) )
mask.ShowImage()
// Do meanValue as sums of masked points
number maskedPoints = sum( mask )
number maskedSum
if ( 0 < maskedPoints )
maskedSum = sum( mask * img ) / maskedPoints
else
maskedSum = sum( img )
return maskedSum
}
Result( "\n Testing irregular and oval ROIs on image.\n" )
image testImg := CreateImageWithROI()
ROI testROIir = GetFirstIrregularROIOfImage( testImg )
number ROIirMean = GetROIMean( testImg, testROIir )
Result( "\n Mean value (irregular ROI): "+ ROIirMean )
ROI testROIoval = GetFirstOvalROIOfImage( testImg )
number ROIovalMean = GetROIMean( testImg, testROIoval )
Result( "\n Mean value (oval ROI) : "+ ROIovalMean )

Related

displaying text above the frame

please help with displaying information above the contour frame, how to display the size of the frame, if it is known that the size of the product is 670 pixels, length, physical 4.1 mm, and how to draw the center of the axis on the object? opencv version 4.7.0.68 , please edit the code.
import numpy as np
import cv2
cap = cv2.VideoCapture ( 1 )
kernel = np.ones ( (2 , 2) , np.uint8 )
while (True):
# Capture frame-by-frame
ret , frame = cap.read ()
# Our operations on the frame come here
gray = cv2.cvtColor ( frame , cv2.COLOR_BGR2GRAY )
gray = cv2.GaussianBlur ( gray , (7 , 7) , 0 )
gray = cv2.medianBlur ( gray , 5 ) # to remove salt and paper noise
# to binary
ret , thresh = cv2.threshold ( gray , 128 , 128 , 128 ) # to detect white objects
# to get outer boundery only
thresh = cv2.morphologyEx ( thresh , cv2.MORPH_GRADIENT , kernel )
# to strength week pixels
thresh = cv2.dilate ( thresh , kernel , iterations = 1 )
im2 = contours , hierarchy = cv2.findContours ( thresh , cv2.RETR_EXTERNAL , cv2.CHAIN_APPROX_SIMPLE )
if len ( contours ) > 0:
cv2.drawContours ( frame , contours , -1 , (0 , 255 , 0) , 3 )
# find the biggest countour (c) by the area
c = max ( contours , key = cv2.contourArea )
x , y , w , h = cv2.boundingRect ( c )
# draw the biggest contour (c) in green
cv2.rectangle ( frame , (x , y) , (x + w , y + h) , (255 , 0 , 0) , 2 )
# Display the resulting frame
cv2.imshow ( 'frame' , frame )
if cv2.waitKey ( 27 ) & 0xFF == ord ( 'q' ):
break
# When everything done, release the capture
cap.release ()
cv2.destroyAllWindows ()
Print(frameWidth)
Print(FrameHeight)

Is there cumulative sum function in DM-scripting?

I would like to do cumulative sum along x or y direction of the image data.
Is there any function in DM-scripting like "cumsum" in Matlib?
Thanks!
for example an image of 4x4 pixels the pixel values are
1 2 3 4
2 3 4 5
3 4 5 6
4 5 6 7
cumulative sum along x direction will result in:
1 1+2=3 1+2+3=6 1+2+3+4=10
2 5 9 14
3 7 12 18
4 9 15 22
There are differnt ways to achieve this, but potentially the fastest and easiest is to create a "fully binned" version of the image.
image img := GetFrontImage()
number sizeX, sizeY
img.GetSize( sizeX, sizeY )
image vSum = Rebin( img, 1, sizeY )
image hSum = Rebin( img, sizeX, 1 )
vSum.SetName( "vertical sum" )
vSum.ShowImage()
hSum.SetName( "horizontal sum" )
hSum.ShowImage()
If you want a 2D image as a result, where each pixel holds the sum of all its pixels to the left, you can do this by adding up offset images:
image img := GetFrontImage()
number sizeX, sizeY
img.GetSize( sizeX, sizeY )
image vCumSum := img.ImageClone()
for( number x = 1; x<sizeX ; x++ )
{
hCumSum += offset( img, -x, 0 )
}
hCumSum.SetName( "horizontal sum (cumulative)" )
hCumSum.ShowImage()
Alternatively, you can create an expression using intrinsic variables as in
image img := GetFrontImage()
image hCumSum := 0 * img.ImageClone()
hCumSum += img[icol,irow] + hCumSum[ icol - 1, irow ]
hCumSum.SetName( "horizontal sum (cumulative)" )
hCumSum.ShowImage()
GMS 3.4 also offers a dedicated, speed optimized command:
RealImage Project( BasicImage img, Number axis )
RealImage Project( BasicImage img, Number axis, Boolean rescale )
void Project( BasicImage img, BasicImage dst, Number axis )
void Project( BasicImage img, BasicImage dst, Number axis, Boolean rescale )
Another way to do projection is by matrix multiplication. multiply a 2-D image by a 1-D matrix of 1's will project the image onto 1-D accumulation.
number d0, d1
image HProject, VProject, ones, img
img:=getfrontImage()
img.getSize(d0,d1)
ones:=exprSize(1,d0,1)
HProject=MatrixMultiply(img,ones)
HProject.rotateLeft()
HProject.showImage()
ones:=exprSize(d1,1,1)
VProject=MatrixMultiply(ones,img)
VProject.showImage()
I also have one
image cumsum(image img)
// computes the cumulative sum along x direction
{
number sx, sy
img.GetSize(sx,sy)
for(number i=1; i<sx; i++)
{
img[0,i,sy,i+1]=img[0,i-1,sy,i]+img[0,i,sy,i+1]
}
return img
}
image im=getfrontimage()
im=im.cumsum()
im.showimage()

Detect semicircle in OpenCV

I am trying to detect full circles and semicircles in an image.
I am following the below mentioned procedure:
Process image (including Canny edge detection).
Find contours and draw them on an empty image, so that I can eliminate unwanted components
(The processed image is exactly what I want).
Detect circles using HoughCircles. And, this is what I get:
I tried varying the parameters in HoughCircles but the results are not consistent as it varies based on lighting and the position of circles in the image.
I accept or reject a circle based on its size. So, the result is not acceptable. Also, I have a long list of "acceptable" circles. So, I need some allowance in the HoughCircle params.
As for the full circles, it's easy - I can simply find the "roundness" of the contour. The problem is semicircles!
Please find the edited image before Hough transform
Use houghCircle directly on your image, don't extract edges first.
Then test for each detected circle, how much percentage is really present in the image:
int main()
{
cv::Mat color = cv::imread("../houghCircles.png");
cv::namedWindow("input"); cv::imshow("input", color);
cv::Mat canny;
cv::Mat gray;
/// Convert it to gray
cv::cvtColor( color, gray, CV_BGR2GRAY );
// compute canny (don't blur with that image quality!!)
cv::Canny(gray, canny, 200,20);
cv::namedWindow("canny2"); cv::imshow("canny2", canny>0);
std::vector<cv::Vec3f> circles;
/// Apply the Hough Transform to find the circles
cv::HoughCircles( gray, circles, CV_HOUGH_GRADIENT, 1, 60, 200, 20, 0, 0 );
/// Draw the circles detected
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]);
cv::circle( color, center, 3, Scalar(0,255,255), -1);
cv::circle( color, center, radius, Scalar(0,0,255), 1 );
}
//compute distance transform:
cv::Mat dt;
cv::distanceTransform(255-(canny>0), dt, CV_DIST_L2 ,3);
cv::namedWindow("distance transform"); cv::imshow("distance transform", dt/255.0f);
// test for semi-circles:
float minInlierDist = 2.0f;
for( size_t i = 0; i < circles.size(); i++ )
{
// test inlier percentage:
// sample the circle and check for distance to the next edge
unsigned int counter = 0;
unsigned int inlier = 0;
cv::Point2f center((circles[i][0]), (circles[i][1]));
float radius = (circles[i][2]);
// maximal distance of inlier might depend on the size of the circle
float maxInlierDist = radius/25.0f;
if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
//TODO: maybe paramter incrementation might depend on circle size!
for(float t =0; t<2*3.14159265359f; t+= 0.1f)
{
counter++;
float cX = radius*cos(t) + circles[i][0];
float cY = radius*sin(t) + circles[i][1];
if(dt.at<float>(cY,cX) < maxInlierDist)
{
inlier++;
cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(0,255,0));
}
else
cv::circle(color, cv::Point2i(cX,cY),3, cv::Scalar(255,0,0));
}
std::cout << 100.0f*(float)inlier/(float)counter << " % of a circle with radius " << radius << " detected" << std::endl;
}
cv::namedWindow("output"); cv::imshow("output", color);
cv::imwrite("houghLinesComputed.png", color);
cv::waitKey(-1);
return 0;
}
For this input:
It gives this output:
The red circles are Hough results.
The green sampled dots on the circle are inliers.
The blue dots are outliers.
Console output:
100 % of a circle with radius 27.5045 detected
100 % of a circle with radius 25.3476 detected
58.7302 % of a circle with radius 194.639 detected
50.7937 % of a circle with radius 23.1625 detected
79.3651 % of a circle with radius 7.64853 detected
If you want to test RANSAC instead of Hough, have a look at this.
Here is another way to do it, a simple RANSAC version (much optimization to be done to improve speed), that works on the Edge Image.
the method loops these steps until it is cancelled
choose randomly 3 edge pixel
estimate circle from them (3 points are enough to identify a circle)
verify or falsify that it's really a circle: count how much percentage of the circle is represented by the given edges
if a circle is verified, remove the circle from input/egdes
int main()
{
//RANSAC
//load edge image
cv::Mat color = cv::imread("../circleDetectionEdges.png");
// convert to grayscale
cv::Mat gray;
cv::cvtColor(color, gray, CV_RGB2GRAY);
// get binary image
cv::Mat mask = gray > 0;
//erode the edges to obtain sharp/thin edges (undo the blur?)
cv::erode(mask, mask, cv::Mat());
std::vector<cv::Point2f> edgePositions;
edgePositions = getPointPositions(mask);
// create distance transform to efficiently evaluate distance to nearest edge
cv::Mat dt;
cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
//TODO: maybe seed random variable for real random numbers.
unsigned int nIterations = 0;
char quitKey = 'q';
std::cout << "press " << quitKey << " to stop" << std::endl;
while(cv::waitKey(-1) != quitKey)
{
//RANSAC: randomly choose 3 point and create a circle:
//TODO: choose randomly but more intelligent,
//so that it is more likely to choose three points of a circle.
//For example if there are many small circles, it is unlikely to randomly choose 3 points of the same circle.
unsigned int idx1 = rand()%edgePositions.size();
unsigned int idx2 = rand()%edgePositions.size();
unsigned int idx3 = rand()%edgePositions.size();
// we need 3 different samples:
if(idx1 == idx2) continue;
if(idx1 == idx3) continue;
if(idx3 == idx2) continue;
// create circle from 3 points:
cv::Point2f center; float radius;
getCircle(edgePositions[idx1],edgePositions[idx2],edgePositions[idx3],center,radius);
float minCirclePercentage = 0.4f;
// inlier set unused at the moment but could be used to approximate a (more robust) circle from alle inlier
std::vector<cv::Point2f> inlierSet;
//verify or falsify the circle by inlier counting:
float cPerc = verifyCircle(dt,center,radius, inlierSet);
if(cPerc >= minCirclePercentage)
{
std::cout << "accepted circle with " << cPerc*100.0f << " % inlier" << std::endl;
// first step would be to approximate the circle iteratively from ALL INLIER to obtain a better circle center
// but that's a TODO
std::cout << "circle: " << "center: " << center << " radius: " << radius << std::endl;
cv::circle(color, center,radius, cv::Scalar(255,255,0),1);
// accept circle => remove it from the edge list
cv::circle(mask,center,radius,cv::Scalar(0),10);
//update edge positions and distance transform
edgePositions = getPointPositions(mask);
cv::distanceTransform(255-mask, dt,CV_DIST_L1, 3);
}
cv::Mat tmp;
mask.copyTo(tmp);
// prevent cases where no fircle could be extracted (because three points collinear or sth.)
// filter NaN values
if((center.x == center.x)&&(center.y == center.y)&&(radius == radius))
{
cv::circle(tmp,center,radius,cv::Scalar(255));
}
else
{
std::cout << "circle illegal" << std::endl;
}
++nIterations;
cv::namedWindow("RANSAC"); cv::imshow("RANSAC", tmp);
}
std::cout << nIterations << " iterations performed" << std::endl;
cv::namedWindow("edges"); cv::imshow("edges", mask);
cv::namedWindow("color"); cv::imshow("color", color);
cv::imwrite("detectedCircles.png", color);
cv::waitKey(-1);
return 0;
}
float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
unsigned int counter = 0;
unsigned int inlier = 0;
float minInlierDist = 2.0f;
float maxInlierDistMax = 100.0f;
float maxInlierDist = radius/25.0f;
if(maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
if(maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;
// choose samples along the circle and count inlier percentage
for(float t =0; t<2*3.14159265359f; t+= 0.05f)
{
counter++;
float cX = radius*cos(t) + center.x;
float cY = radius*sin(t) + center.y;
if(cX < dt.cols)
if(cX >= 0)
if(cY < dt.rows)
if(cY >= 0)
if(dt.at<float>(cY,cX) < maxInlierDist)
{
inlier++;
inlierSet.push_back(cv::Point2f(cX,cY));
}
}
return (float)inlier/float(counter);
}
inline void getCircle(cv::Point2f& p1,cv::Point2f& p2,cv::Point2f& p3, cv::Point2f& center, float& radius)
{
float x1 = p1.x;
float x2 = p2.x;
float x3 = p3.x;
float y1 = p1.y;
float y2 = p2.y;
float y3 = p3.y;
// PLEASE CHECK FOR TYPOS IN THE FORMULA :)
center.x = (x1*x1+y1*y1)*(y2-y3) + (x2*x2+y2*y2)*(y3-y1) + (x3*x3+y3*y3)*(y1-y2);
center.x /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
center.y = (x1*x1 + y1*y1)*(x3-x2) + (x2*x2+y2*y2)*(x1-x3) + (x3*x3 + y3*y3)*(x2-x1);
center.y /= ( 2*(x1*(y2-y3) - y1*(x2-x3) + x2*y3 - x3*y2) );
radius = sqrt((center.x-x1)*(center.x-x1) + (center.y-y1)*(center.y-y1));
}
std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
std::vector<cv::Point2f> pointPositions;
for(unsigned int y=0; y<binaryImage.rows; ++y)
{
//unsigned char* rowPtr = binaryImage.ptr<unsigned char>(y);
for(unsigned int x=0; x<binaryImage.cols; ++x)
{
//if(rowPtr[x] > 0) pointPositions.push_back(cv::Point2i(x,y));
if(binaryImage.at<unsigned char>(y,x) > 0) pointPositions.push_back(cv::Point2f(x,y));
}
}
return pointPositions;
}
input:
output:
console output:
press q to stop
accepted circle with 50 % inlier
circle: center: [358.511, 211.163] radius: 193.849
accepted circle with 85.7143 % inlier
circle: center: [45.2273, 171.591] radius: 24.6215
accepted circle with 100 % inlier
circle: center: [257.066, 197.066] radius: 27.819
circle illegal
30 iterations performed`
optimization should include:
use all inlier to fit a better circle
dont compute distance transform after each detected circles (it's quite expensive). compute inlier from point/edge set directly and remove the inlier edges from that list.
if there are many small circles in the image (and/or a lot of noise), it's unlikely to hit randomly 3 edge pixels or a circle. => try contour detection first and detect circles for each contour. after that try to detect all "other" circles left in the image.
a lot of other stuff
#Micka's answer is great, here it is roughly translated into python
The method takes a thresholded image mask as an argument, leaving that part as an exercise for the reader
def get_circle_percentages(image):
#compute distance transform of image
dist = cv2.distanceTransform(image, cv2.DIST_L2, 0)
rows = image.shape[0]
circles = cv2.HoughCircles(image, cv2.HOUGH_GRADIENT, 1, rows / 8, 50, param1=50, param2=10, minRadius=40, maxRadius=90)
minInlierDist = 2.0
for c in circles[0, :]:
counter = 0
inlier = 0
center = (c[0], c[1])
radius = c[2]
maxInlierDist = radius/25.0
if maxInlierDist < minInlierDist: maxInlierDist = minInlierDist
for i in np.arange(0, 2*np.pi, 0.1):
counter += 1
x = center[0] + radius * np.cos(i)
y = center[1] + radius * np.sin(i)
if dist.item(int(y), int(x)) < maxInlierDist:
inlier += 1
print(str(100.0*inlier/counter) + ' percent of a circle with radius ' + str(radius) + " detected")
I know that it's little bit late, but I used different approach which is much easier.
From the cv2.HoughCircles(...) you get centre of the circle and the diameter (x,y,r). So I simply go through all centre points of the circles and I check if they are further away from the edge of the image than their diameter.
Here is my code:
height, width = img.shape[:2]
#test top edge
up = (circles[0, :, 0] - circles[0, :, 2]) >= 0
#test left edge
left = (circles[0, :, 1] - circles[0, :, 2]) >= 0
#test right edge
right = (circles[0, :, 0] + circles[0, :, 2]) <= width
#test bottom edge
down = (circles[0, :, 1] + circles[0, :, 2]) <= height
circles = circles[:, (up & down & right & left), :]
The semicircle detected by the hough algorithm is most probably correct. The issue here might be that unless you strictly control the geometry of the scene, i.e. exact position of the camera relative to the target, so that the image axis is normal to the target plane, you will get ellipsis rather than circles projected on the image plane. Not to mention the distortions caused by the optical system, which further degenerate the geometric figure. If you rely on precision here, I would recommend camera calibration.
You better try with different kernel for gaussian blur.That will help you
GaussianBlur( src_gray, src_gray, Size(11, 11), 5,5);
so change size(i,i),j,j)

How to find points with specific degree on the contour (Opencv)

I have the points of the contour and i want to get the (x,y) of the points that are on specific degree. From the attached image, i want to get the yellow points.
I used the hint that #Aleksander and #Zaw gave me.
The centroid of the contour was used as one point to draw the lines and i used the line equations to get the second point of each line and draw it.
y - y1 = m (x - x1)
to get m we used the following equations
arctan( **y** of the centroid / **x** of the centroid) - angle in radiant = Phi
tan(Phi) = m
then i used
y = m (x - x1) + y1
when x1 and y1 is the centroid point, and x is the assumed coordinate of the second point needed to draw the line.
void MyDraw (Mat Draw, vector<Point> hull,Scalar color,int thickness){
std::vector<std::vector<cv::Point> > T = std::vector<std::vector<cv::Point> >(1,hull);
drawContours(Draw, T, -1, color, thickness);}
Point GetCentroidOfConvexHull(vector<Point> Hull){
// Get the moments
Moments mu;
mu= moments(Hull, false );
// Get the mass centers:
return Point( mu.m10/mu.m00 , mu.m01/mu.m00 );}
void _tmain(int argc, _TCHAR* argv[]){
vector<vector<Point>> contours;
vector<Point> hull;
Mat testContour = Mat::zeros(width, height,CV_8UC3);
Mat testLine = Mat::zeros(width, height,CV_8UC3);
Mat andResult;
Point centroid;
findContours(Dilated,contour,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE,Point (0,0));
convexHull(contour,hull,false);
centroid = GetCentroidOfConvexHull(hull);
MyDraw(testContour,hull,White,1);
Rect myRect = boundingRect(hull);
imshow("test contour",testContour);
waitKey(0);
float degrees[8] = {0,45,90,135,180,225,270,315};
Point point;
// for each degree
for (int d = 0 ; d < 8 ; d++){
//Get line equation from the slope and the center point of the contour
float m = std::tan( std::atan2((double)centroid.y,(double)centroid.x) - (degrees[d]*3.14/180));
// using the upper left and lower right points to get the x of the other point
if ((d >= 0 && d <= 2) || d == 7) point.x = myRect.tl().x;
else point.x = myRect.br().x;
point.y = (int)(m * (float)(point.x - centroid.x) + centroid.y);
line(testLine,centroid,point,White,1,8);}
imshow("test Line",testLine);
waitKey(0);
// and operation
andResult = testContour & testLine;
imshow("Anding Result",andResult);
waitKey(0);}
After drawing the contour and the lines, i did a bit wise AND operation to get the intersection between the lines and the contour.

Extracting text from an image with tesseract [duplicate]

I asked a similar question here but that is focused more on tesseract.
I have a sample image as below. I would like to make the white square my Region of Interest and then crop out that part (square) and create a new image with it. I will be working with different images so the square won't always be at the same location in all images. So I will need to somehow detect the edges of the square.
What are some pre-processing methods I can perform to achieve the result?
Using your test image I was able to remove all the noises with a simple erosion operation.
After that, a simple iteration on the Mat to find for the corner pixels is trivial, and I talked about that on this answer. For testing purposes we can draw green lines between those points to display the area we are interested at in the original image:
At the end, I set the ROI in the original image and crop out that part.
The final result is displayed on the image below:
I wrote a sample code that performs this task using the C++ interface of OpenCV. I'm confident in your skills to translate this code to Python. If you can't do it, forget the code and stick with the roadmap I shared on this answer.
#include <cv.h>
#include <highgui.h>
int main(int argc, char* argv[])
{
cv::Mat img = cv::imread(argv[1]);
std::cout << "Original image size: " << img.size() << std::endl;
// Convert RGB Mat to GRAY
cv::Mat gray;
cv::cvtColor(img, gray, CV_BGR2GRAY);
std::cout << "Gray image size: " << gray.size() << std::endl;
// Erode image to remove unwanted noises
int erosion_size = 5;
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size) );
cv::erode(gray, gray, element);
// Scan the image searching for points and store them in a vector
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = gray.begin<uchar>();
cv::Mat_<uchar>::iterator end = gray.end<uchar>();
for (; it != end; it++)
{
if (*it)
points.push_back(it.pos());
}
// From the points, figure out the size of the ROI
int left, right, top, bottom;
for (int i = 0; i < points.size(); i++)
{
if (i == 0) // initialize corner values
{
left = right = points[i].x;
top = bottom = points[i].y;
}
if (points[i].x < left)
left = points[i].x;
if (points[i].x > right)
right = points[i].x;
if (points[i].y < top)
top = points[i].y;
if (points[i].y > bottom)
bottom = points[i].y;
}
std::vector<cv::Point> box_points;
box_points.push_back(cv::Point(left, top));
box_points.push_back(cv::Point(left, bottom));
box_points.push_back(cv::Point(right, bottom));
box_points.push_back(cv::Point(right, top));
// Compute minimal bounding box for the ROI
// Note: for some unknown reason, width/height of the box are switched.
cv::RotatedRect box = cv::minAreaRect(cv::Mat(box_points));
std::cout << "box w:" << box.size.width << " h:" << box.size.height << std::endl;
// Draw bounding box in the original image (debugging purposes)
//cv::Point2f vertices[4];
//box.points(vertices);
//for (int i = 0; i < 4; ++i)
//{
// cv::line(img, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 1, CV_AA);
//}
//cv::imshow("Original", img);
//cv::waitKey(0);
// Set the ROI to the area defined by the box
// Note: because the width/height of the box are switched,
// they were switched manually in the code below:
cv::Rect roi;
roi.x = box.center.x - (box.size.height / 2);
roi.y = box.center.y - (box.size.width / 2);
roi.width = box.size.height;
roi.height = box.size.width;
std::cout << "roi # " << roi.x << "," << roi.y << " " << roi.width << "x" << roi.height << std::endl;
// Crop the original image to the defined ROI
cv::Mat crop = img(roi);
// Display cropped ROI
cv::imshow("Cropped ROI", crop);
cv::waitKey(0);
return 0;
}
Seeing that the text is the only large blob, and everything else is barely larger than a pixel, a simple morphological opening should suffice
You can do this in opencv
or with imagemagic
Afterwards the white rectangle should be the only thing left in the image. You can find it with opencvs findcontours, with the CvBlobs library for opencv or with the imagemagick -crop function
Here is your image with 2 steps of erosion followed by 2 steps of dilation applied:
You can simply plug this image into the opencv findContours function as in the Squares tutorial example to get the position
input
#objective:
#1)compress large images to less than 1000x1000
#2)identify region of interests
#3)save rois in top to bottom order
import cv2
import os
def get_contour_precedence(contour, cols):
tolerance_factor = 10
origin = cv2.boundingRect(contour)
return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]
# Load image, grayscale, Gaussian blur, adaptive threshold
image = cv2.imread('./images/sample_0.jpg')
#compress the image if image size is >than 1000x1000
height, width, color = image.shape #unpacking tuple (height, width, colour) returned by image.shape
while(width > 1000):
height = height/2
width = width/2
print(int(height), int(width))
height = int(height)
width = int(width)
image = cv2.resize(image, (width, height))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (9,9), 0)
thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)
# Dilate to combine adjacent text contours
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
ret,thresh3 = cv2.threshold(image,127,255,cv2.THRESH_BINARY_INV)
dilate = cv2.dilate(thresh, kernel, iterations=4)
# Find contours, highlight text areas, and extract ROIs
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#cnts = cv2.findContours(thresh3, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
#ORDER CONTOURS top to bottom
cnts.sort(key=lambda x:get_contour_precedence(x, image.shape[1]))
#delete previous roi images in folder roi to avoid
dir = './roi/'
for f in os.listdir(dir):
os.remove(os.path.join(dir, f))
ROI_number = 0
for c in cnts:
area = cv2.contourArea(c)
if area > 10000:
x,y,w,h = cv2.boundingRect(c)
#cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 3)
cv2.rectangle(image, (x, y), (x + w, y + h), (100,100,100), 1)
#use below code to write roi when results are good
ROI = image[y:y+h, x:x+w]
cv2.imwrite('roi/ROI_{}.jpg'.format(ROI_number), ROI)
ROI_number += 1
cv2.imshow('thresh', thresh)
cv2.imshow('dilate', dilate)
cv2.imshow('image', image)
cv2.waitKey()
roi detection
output

Resources