Extracting data from a radar chart in a scanned document - python

I would like to convert this radar graph into numeric values for each component (dotted lines), using openCV for python. How would I best do this? I've been thinking about detecting the center of the graph as well as the intersections of the dotted lines with the gray area. By doing this I could measure the distance between the center and each of these intersections to determine a value for each component. However this seems rather complex, and I have no idea how to start. Could someone help me out here?
EDIT: The purpose is to write a piece of software that can extract data from 250 of these graphs. (I have better quality scans)

I would do something like this (sorry it's pseudo-code, if you think the idea it's good enough I'll try to write some real code):
1.find the circle center(maybe using HoughCircle function)
2.inverse thresholding to highlight your dark grey area
3.Call opencv function approx poly to get the polygon representing this area
4.for each vertex, measure its distance from the center and convert it to your desidered scale
I think it should work.
hough circle tutorial
https://docs.opencv.org/2.4/doc/tutorials/imgproc/imgtrans/hough_circle/hough_circle.html
approx poly tutorial
https://docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html
EDIT:
I've had some spare time so I wrote an initial piece of code to extract circle, radius and the poly, hope it helps you
img = cv.imread("c:\\temp\\test.jpg", cv.IMREAD_COLOR)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#thresholds for isolate circle and poly
ret, thres_poly_temp = cv.threshold(gray, 90, 255, cv.THRESH_BINARY_INV)
ret, thres_circle = cv.threshold(gray, 110, 255, cv.THRESH_BINARY_INV)
#cleanup image for detect poly
thres_poly = thres_poly_temp.copy()
thres_poly = cv.GaussianBlur(thres_poly_temp,(3,3),0)
thres_poly = cv.medianBlur( thres_poly, 5)
#bitwise_and to keep just the poly, removing the circle
cv.bitwise_and(thres_poly_temp, thres_circle, thres_poly)
kernel = np.ones((3, 3),np.uint8)
thres_poly = cv.morphologyEx(thres_poly, cv.MORPH_CLOSE, kernel)
kernel = np.ones((3, 3),np.uint8)
thres_poly = cv.morphologyEx(thres_poly, cv.MORPH_OPEN, kernel)
#find circle
circle = cv.HoughCircles(thres_circle, 3, 2, 800, minRadius = 100, maxRadius=500, param1=80, param2=100)
radius_list = []
if circle is not None:
circle = np.round(circle[0, :]).astype("int")
for (x,y,r) in circle:
cv.circle(gray, (x,y), r, (255,255,0),3)
cv.circle(gray, (x,y), 3, (255,255,0),3)
radius_list.append((x+r,y))
a = 0
#find radius
while(a < 360):
rad = math.radians(a)
x2 = int((radius_list[0][0] - x) * math.cos(rad)) - int((radius_list[0][1] - y) * math.sin(rad)) + x;
y2 = int((radius_list[0][0] - x) * math.sin(rad)) + int((radius_list[0][1] - y) * math.cos(rad)) + y;
radius_list.append((x2,y2))
a = a + 18
cv.line(gray, (x,y), (x2,y2), (255,255,0),2)
#find poly contour
contours,hierarchy = cv.findContours(thres_poly, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
#extract contour with max area
maxArea = -1
maxAreaContour = contours[0]
for contour in contours:
area = abs(cv.contourArea(contour))
if area > maxArea:
maxArea = area
maxAreaContour = contour
#approx poly to get contours
epsilon = 0.1*cv.arcLength(maxAreaContour,True)
approx = cv.approxPolyDP(maxAreaContour, 5, True)
cv.drawContours(gray, [approx],-1,(0,255,0),2)
cv.imshow("1", gray)
#now just iterate all the radius with the contour to find the intersection
# it should be pretty straight forward
output sample
EDIT 2: still missing: align image, as I said you can use the horizontal and vertical dotted lines to do that (use hough lines to get them,then determine m and q and do the inverse roto-translation)

Related

Counting curves, angles and straights in a binary image in openCV and python

I want to write a tool for finding the number of angles, curves and straight lines within each bounded object in an image.
All input images will be black on white background and all will represent characters.
As illustrated in the image, for each bounded region, each shape occurrence is noted. It would be preferable to be able to have a threshold for how curvy a curve must be to be considered a curve and not an angle etc. And the same for straight lines and angles.
I have used Hough Line Transform for detecting straight lines on other images and it might work in combination with something here I thought.
Am open to other libraries than opencv - this is just what I have some experience with.
Thanks in advance
EDIT:
So based on the answer from Markus, I made a program using the findContours() with CHAIN_APPROX_SIMPLE.
It produces a somewhat wierd result inputting a 'k' where it correctly identifies some points around the angles but then the 'leg' (the lower diagonal part) has many many points on it. I am unsure how to go about segmenting this to group into straights, angles and curves.
Code:
import numpy as np
img = cv2.imread('Helvetica-K.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
edges = cv2.Canny(blurred, 50, 150, apertureSize=3)
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#cv2.drawContours(img, contours, 0, (0,255,0), 1)
#Coordinates of each contour
for i in range(len(contours[0])):
print(contours[0][i][0][0])
print(contours[0][i][0][1])
cv2.circle(img, (contours[0][i][0][0], contours[0][i][0][1]), 2, (0,0,255), -1)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Img example:
You can use findContours with option CHAIN_APPROX_SIMPLE.
A point with an angle less than some threshold is a corner.
A point with an angle more than some threshold is on a straight line and should be removed.
Two adjacent points with a distance of more than some threshold are the ends of a straight line.
Two adjacent points that are identified to be corners are the ends of a straight line.
All other points belong to some curvy detail.
Update:
Here is some code you can start with. It shows how to smoothen the straight lines, how you can merge several corner points into one, and how to calculate distances and angles at each point. There is still some work to be done for you to achieve the required result but I hope it leads in the right direction.
import numpy as np
import numpy.linalg as la
import cv2
def get_angle(p1, p2, p3):
v1 = np.subtract(p2, p1)
v2 = np.subtract(p2, p3)
cos = np.inner(v1, v2) / la.norm(v1) / la.norm(v2)
rad = np.arccos(np.clip(cos, -1.0, 1.0))
return np.rad2deg(rad)
def get_angles(p, d):
n = len(p)
return [(p[i], get_angle(p[(i-d) % n], p[i], p[(i+d) % n])) for i in range(n)]
def remove_straight(p):
angles = get_angles(p, 2) # approximate angles at points (two steps in path)
return [p for (p, a) in angles if a < 170] # remove points with almost straight angles
def max_corner(p):
angles = get_angles(p, 1) # get angles at points
j = 0
while j < len(angles): # for each point
k = (j + 1) % len(angles) # and its successor
(pj, aj) = angles[j]
(pk, ak) = angles[k]
if la.norm(np.subtract(pj, pk)) <= 4: # if points are close
if aj > ak: # remove point with greater angle
angles.pop(j)
else:
angles.pop(k)
else:
j += 1
return [p for (p, a) in angles]
def main():
img = cv2.imread('abc.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY_INV)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours: # for each contour
pts = [v[0] for v in c] # get pts from contour
pts = remove_straight(pts) # remove almost straight angles
pts = max_corner(pts) # remove nearby points with greater angle
angles = get_angles(pts, 1) # get angles at points
# draw result
for (p, a) in angles:
if a < 120:
cv2.circle(img, p, 3, (0, 0, 255), -1)
else:
cv2.circle(img, p, 3, (0, 255, 0), -1)
cv2.imwrite('out.png', img)
cv2.destroyAllWindows()
main()

How to locate contours on a ringed shooting target

Firstly, I am new to python and opencv so my understanding is limited, however I am trying to learn more as best I can.
I am currently struggling to locate contours(bullet holes) on a simple ringed target so that I can eventually score each hole. I have managed to solve a similar problem on a different image and I am wondering what I can do to get the same method to work on the new one.
Successful attempt at scoring a target
This is the target that my problem concerns
When I use these HSV Values I am presented with only the Bullet Holes. My limited knowledge tells me that perhaps these HSV values are useful in thresholding(?) but I can not seem to find the execution.
The method used to locate the contours in the example target is shown below:
imgREDUCED = cv2.inRange(image, (60, 60, 60), (150, 150, 150))
kernel = np.ones((10,10),np.uint8)
opening = cv2.morphologyEx(imgREDUCED, cv2.MORPH_OPEN, kernel)
thresh = cv2.threshold(opening, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
if cv2.contourArea(c) > 1:
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
What steps can I take to locate the contours on this new target image?
All help is appreciated and I will try my best to answer any questions
By finding the bullseye and the outer ring we can calculate the score of each hole based on its distance from the center. Here are the steps I took to try and solve this.
First, I converted to HSV and took a look at the different channels:
Hue
Value
From the hue channel we can clearly see the holes in the target so it's a good candidate for thresholding for those. On the value channel we can clearly see the rings and the center so we'll use that channel to detect those.
Hue Mask (0, 30)
Value Mask (0, 155)
We can use findContours to outline the white parts of the mask. From that outline we can get the center of the contour and the area of the contour. Using this on the hue mask we get the center of each hole and using this on the value mask we can get the biggest ring by looking for the contour with the largest area. With the area of the biggest ring, we can estimate the radius via the circle's area formula.
To find the bullseye I thresholded the value mask again, but using the (215, 255) to search for high values. This perfectly captures just the center, but you might not always get that lucky with your pictures. Using the findContours function again I get the center are radius of the bullseye.
Now I can score each of the holes. I get the distance from the hole to the center and figure out where on the scoresheet it should land on.
Marked the outer ring, the center of each hole, the score of each hole, and the bullseye:
Here's the code:
import cv2
import math
import numpy as np
# get center of contour
def centroid(contour):
M = cv2.moments(contour);
cx = int(round(M['m10']/M['m00']));
cy = int(round(M['m01']/M['m00']));
center = (cx, cy);
return center;
# load image
img = cv2.imread("target.png");
img = img[:,:-1,:]; # there's a bit of wall or something on the right
# hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV);
h,s,v = cv2.split(hsv);
# find the outer ring
v_mask = cv2.inRange(v, 0, 155);
# contours
_, contours, _ = cv2.findContours(v_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
# find the biggest contour
biggest_cntr = None;
biggest_area = 0;
marked = img.copy();
for contour in contours:
area = cv2.contourArea(contour);
if area > biggest_area:
biggest_area = area;
biggest_cntr = contour;
cv2.drawContours(marked, [biggest_cntr], -1, (0, 255, 0), 3);
# find biggest radius
big_radius = math.sqrt(biggest_area / math.pi);
# find center
center_v_mask = cv2.inRange(v, 215, 255);
_, contours, _ = cv2.findContours(center_v_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
center = centroid(contours[0]);
# use this to calculate the middle radius
area = cv2.contourArea(contours[0]);
little_radius = math.sqrt(area / math.pi);
# draw center
marked = cv2.circle(marked, center, 2, (155,155,0), -1);
# mask holes
h_mask = cv2.inRange(h, 0, 30);
h_mask = cv2.medianBlur(h_mask, 11);
# draw contour centers
_, contours, _ = cv2.findContours(h_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE);
holes = [];
for contour in contours:
c = centroid(contour);
holes.append(c);
marked = cv2.circle(marked, c, 2, (0,0,155), -1);
# calculate approximate size of each ring
# (with foreknowledge that there are 9 + bullseye)
remaining_radius = big_radius - little_radius;
slices = remaining_radius / 9;
# calculate scores
scores = [];
for hole in holes:
# get distance from center
dx = hole[0] - center[0];
dy = hole[1] - center[1];
dist = math.sqrt(dx*dx + dy*dy);
# check score
dist -= little_radius;
if dist < 0:
scores.append(10);
else:
scores.append(9 - int(dist / slices));
# draw the scores
font = cv2.FONT_HERSHEY_SIMPLEX ;
for a in range(len(holes)):
tup = (holes[a][0], holes[a][1]);
marked = cv2.putText(marked, str(scores[a]), tup, font, 1, (0,0,155), 2, cv2.LINE_AA);
# show
cv2.imshow("marked", marked);
cv2.waitKey(0);

Convert ellipse in image to circle (warp ellipse to circle like a polygon warping to rectangle)

I have an image of an ellipse and if an image has ellipse i am finding it using findcontours() and then i want to convert this ellipse to a circle.
see the example
and i want transform each of them to like this
First I have applied canny edge detection.Then on this image findcontour() is applied.
I have found the ellipse using findcontours() to get all contours and get the required elliptical contour and then the i am using fitellipse() to get center, rotation angle and major and minor axis of ellipse.
I have then tried to rotate the image by the rotated angle and then scale height and width of image w.r.t minor and major axis(i.e. making major axis and minor axis length same) but also I am not getting the proper circular object image as above. There will be some rotation left/it will still be like an ellipse which will be near to circle or so.
_, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if len(c) >= 5:
a = cv2.fitEllipse(c)
(x, y), (MA, ma), angle = a
area = (math.pi * MA * ma)/4
if abs(x-image.shape[0]/2) <= 2 and abs(y-image.shape[1]/2) <= 2 and (area - cv2.contourArea(c)) < 50:
screenCount = c
width, height = MA, ma
centerX, centerY = x, y
ellipseAngle = angle
print(width, height, centerX, centerY, ellipseAngle)
# cv2.drawContours(img, c, -1, (0, 255, 0), 4)
cv2.ellipse(img, a, (0, 0, 255), 2, 8)
cv2.imshow("ellipse", img)
break
img = image.copy()
if ellipseAngle < 90:
rotatedImg = imutils.rotate(img, ellipseAngle)
else:
rotatedImg = imutils.rotate(img, -(ellipseAngle - 90))
Then i have scaled as per major and minor axis
after applying findcontour() i got these 2 contours for 1st image in the post
from these any contour is fine right? i am using the first contour from countour as per the code and fitellipse() gives me this ellipse
EDITED - If there are any better approach to solve this problem it would be helpful.
There are several issues I can see in the code:
You are using an edge detection algorithm, and getting the contours of the result. This is OK in principle, but it leads to an image that has two contours: one for the inner edge and one for the outer edge of the edge detection result. It is easier to just threshold the image and obtain a single edge. Though if the image gets more complex an edge detection might be relevant. Indeed, any one of the two contours you obtain should be useful.
The line if abs(x-image.shape[0]/2) <= 2 and abs(y-image.shape[1]/2) <= 2 and (area - cv2.contourArea(c)) < 50 is very restrictive, it didn't trigger for the second image for me.
Rotating by -(ellipseAngle - 90) if the angle is negative is strange. You should rotate all ellipses in the same way.
The code below produces a circular output for both ellipse images shown in the question. I think that the ellipse parameters determined this way are not very precise, but it looks like they're good enough for this application.
import cv2
import numpy as np
img = cv2.imread('im1.png',0)
_, thresh = cv2.threshold(img, 128, 255, type=cv2.THRESH_BINARY_INV)
_, contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
params = cv2.fitEllipse(contours[0])
angle = params[2]
scale = params[1]
scale = scale[0]/scale[1]
M = cv2.getRotationMatrix2D((img.shape[0]/2, img.shape[1]/2), angle, 1)
# Let's add the scaling too:
M[:,0:2] = np.array([[1,0],[0,scale]]) # M[:,0:2]
M[1,2] = M[1,2] * scale # This moves the ellipse so it doesn't end up outside the image (it's not correct to keep the ellipse in the middle of the image)
out = cv2.warpAffine(img, M, img.shape, borderValue=255)
cv2.imshow('out',out)
cv2.waitKey()
Using PyDIP (I'm an author) you can get a more precise measure in the idealized case of the OP by not thresholding, and using the grey-values around the edges of the ellipse to get a more precise fit. We compute the second order central moments of the image, and derive the ellipse parameters from those. It is important here that the background is exactly 0, and that the foreground (ellipse pixels) are uniform in intensity except at the edge, where the intermediate grey-values add information about the sub-pixel location of the edge.
import PyDIP as dip
import numpy as np
img = -dip.ImageRead('im1.png').TensorElement(0) # We use the inverted first channel
params = dip.Moments(img).secondOrder
M = np.array([[params[0],params[2]],[params[2],params[1]]])
d, V = np.linalg.eig(M)
d = np.sqrt(d)
scale = d[0]/d[1]
angle = np.arctan2(V[1,0],V[0,0])
img = dip.Rotation2D(img, -angle)
img = dip.Resampling(img, [scale, 1])
img.Show()

Completing contour for "broken" image

I have a noisy gray-scale image for which I want to segment/mask the large arc spanning the image from the rest. I intend to mask the arc and all of the pixels above the arc.
To do this, I have thresholded the image to create a binary image, and used cv2.findContours() to trace the outline of the arc.
Original Image:
Image after Otsu threshold:
Threshold + Closing:
Contours of closed image:
As you can see, the closed image does not create a solid arc. Closing further causes the arc to lose it's shape. The green line is a contour of the closed image. The blue line is created with approxpolyDP() but I can't get it to work. Are there better ways to mask the arc in the image perhaps?
Here is my code:
import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt
# read an image
img = cv2.imread('oct.png')
# get gray image and apply Gaussian blur
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# get binary image
ret, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# close image to "solidify" it
kernel = np.ones((3,3),np.uint8)
closing = cv2.morphologyEx(thresh,cv2.MORPH_CLOSE,kernel, iterations = 3)
# find contours
(_, contours, _) = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
max_area = cv2.contourArea(cnt)
for cont in contours:
if cv2.contourArea(cont) > max_area:
cnt = cont
max_area = cv2.contourArea(cont)
# define main arc contour approx. and hull
perimeter = cv2.arcLength(cnt, True)
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
# hull = cv2.convexHull(cnt)
# cv2.isContourConvex(cnt)
imgcopy = np.copy(img)
cv2.drawContours(imgcopy, [cnt], -1, (0, 255, 0), 3)
cv2.drawContours(imgcopy, [approx], -1, (0, 0, 255), 3)
# plot figures
plt.figure(1)
plt.imshow(imgcopy, cmap="gray")
plt.figure(2)
plt.imshow(thresh, cmap="gray")
plt.figure(3)
plt.imshow(closing, cmap="gray")
I would suggest to use a RANSAC method to fit 2 ellipses using edge information of the arc. Edge can be obtain simply by using canny or any other method you see fit. Of course this method can only work if the arc is elliptical. If its a straight line, you can replace the ellipse fitting part with a line fitting part.
Here is the result:
Here is the code:
import numpy as np
import cv2
import random as rp
def ransac_ellipse(iter, srcimg, x, y):
x_size = np.size(x)
best_count = x_size
for i in range(iter):
base = srcimg.copy()
# get 5 random points
r1 = int(rp.random() * x_size)
r2 = int(rp.random() * x_size)
r3 = int(rp.random() * x_size)
r4 = int(rp.random() * x_size)
r5 = int(rp.random() * x_size)
p1 = (x[r1],y[r1])
p2 = (x[r2],y[r2])
p3 = (x[r3],y[r3])
p4 = (x[r4],y[r4])
p5 = (x[r5],y[r5])
p_set = np.array((p1,p2,p3,p4,p5))
# fit ellipse
ellipse = cv2.fitEllipse(p_set)
# remove intersected ellipse
cv2.ellipse(base,ellipse,(0),1)
# count remain
local_count = cv2.countNonZero(base)
# if count is smaller than best, update
if local_count < best_count:
best_count = local_count
best_ellipse = ellipse
return best_ellipse
img = cv2.imread('arc.jpg',0)
# Speed up and remove noise
small = cv2.resize(img,(0,0),fx = 0.25,fy = 0.25)
# remove remaining noise
median = cv2.medianBlur(small,21)
# get canny edge
edge = cv2.Canny(median,180,20)
cv2.imshow("Edge",edge)
# obtain the non zero locations
y, x = np.where(edge > 0)
# ransac ellipse to get the outter circle
ellipse1 = ransac_ellipse(10000,edge,x,y)
# remove the outter circle
cv2.ellipse(edge,ellipse1,(0),2)
# ransac ellipse to get the inner circle
y, x = np.where(edge > 0)
ellipse2 = ransac_ellipse(10000,edge,x,y)
disp = cv2.cvtColor(small,cv2.COLOR_GRAY2BGR)
cv2.ellipse(disp,ellipse1,(0,0,255),1)
cv2.ellipse(disp,ellipse2,(0,0,255),1)
cv2.imshow("result",disp)
cv2.waitKey(0)
You are on the right path. Your closing will likely work better if you smooth the image a little bit first. I like to apply the thresholding at the end, after the morphological operations. In this case, it doesn't really matter which the order for closing and thresholding is, but keeping thresholding at the end helps later when refining the pre-processing. Once you threshold you loose a lot of information, you need to make sure you preserve all the information you will need, and thus filtering the image properly before thresholding is important.
Here is a quick attempt, I'm sure it can be refined:
import matplotlib.pyplot as pp
import PyDIP as dip
img = pp.imread('/Users/cris/Downloads/MipBB.jpg')
img = img[:,:,0]
smooth = dip.Gauss(img, [3]) # Gaussian smoothing with sigma=3
smooth = dip.Closing(smooth, 25) # Note! This uses a disk SE with diameter 25 pixels
out = dip.Threshold(smooth, 'triangle')[0]
pp.imsave('/Users/cris/Downloads/MipBB_out.png', out)
I used the triangle threshold method (also known as the chord method, or skewed bi-modality threshold, see P.L. Rosin, "Unimodal thresholding", Pattern Recognition 34(11):2083-2096, 2001) because it works better in this case.
The code uses PyDIP, but I'm sure you can re-create the same process using OpenCV.

How to draw a line from the centroid of a contour to the perimeter of the contour?

I'm obtaining the centroid of a contour using moments like so:
cnt = np.vstack([cnt[0]]).squeeze()
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
I want to divide the contour into 4 quadrants, so that would require two lines to be drawn, one vertical and one horizontal, both passing through the obtained centroid. How do I go about doing it?
Though this looks like a task for OpenCV, you may want to have a look at the Shapely package:
http://toblerity.org/shapely/manual.html
Shapely allows you to calculate intersections between polygons, and so the solution gets quite simple: For both the horizontal and the vertical line running through the centroid of your contour, you just calculate the intersections with the contour and draw lines to those intersections.
Lacking your original figure, I have used an ellipse to demonstrate the solution. Since you said that you have only some sample points of your contour, I have used a "coarse" ellipse which is just approximated by a few points.
Output looks like this, hope that this is what you were looking for:
Source code is lengthy due to all the visualization, but hopefully self-explaining:
import shapely.geometry as shapgeo
import numpy as np
import cv2
def make_image():
img = np.zeros((500, 500), np.uint8)
white = 255
cv2.ellipse( img, (250, 300), (100,70), 30, 0, 360, white, -1 )
return img
if __name__ == '__main__':
img = make_image()
#Create a "coarse" ellipse
_, contours0, hierarchy = cv2.findContours( img.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = [cv2.approxPolyDP(cnt, 3, True) for cnt in contours0]
h, w = img.shape[:2]
vis = np.zeros((h, w, 3), np.uint8)
cv2.drawContours( vis, contours, -1, (128,255,255), 1)
#Extract contour of ellipse
cnt = np.vstack([contours[0]]).squeeze()
#Determine centroid
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print cx, cy
#Draw full segment lines
cv2.line(vis,(cx,0),(cx,w),(150,0,0),1)
cv2.line(vis,(0,cy),(h,cy),(150,0,0),1)
# Calculate intersections using Shapely
# http://toblerity.org/shapely/manual.html
PolygonEllipse= shapgeo.asLineString(cnt)
PolygonVerticalLine=shapgeo.LineString([(cx,0),(cx,w)])
PolygonHorizontalLine=shapgeo.LineString([(0,cy),(h,cy)])
insecv= np.array(PolygonEllipse.intersection(PolygonVerticalLine)).astype(np.int)
insech= np.array(PolygonEllipse.intersection(PolygonHorizontalLine)).astype(np.int)
cv2.line(vis,(insecv[0,0], insecv[0,1]),(insecv[1,0], insecv[1,1]),(0,255,0),2)
cv2.line(vis,(insech[0,0], insech[0,1]),(insech[1,0], insech[1,1]),(0,255,0),2)
cv2.imshow('contours', vis)
0xFF & cv2.waitKey()
cv2.destroyAllWindows()

Categories