Getting the X and Y Coordinates from Cv2.Contour in Open Cv Python and Store It to Different Variables

How to get x,y position of contours in Python OpenCV

You can refer here

Find Co-ordinates of Contours using OpenCV | Python

# Python code to find the co-ordinates of 
# the contours detected in an image.
import numpy as np
import cv2

# Reading image
font = cv2.FONT_HERSHEY_COMPLEX
img2 = cv2.imread('test.jpg', cv2.IMREAD_COLOR)

# Reading same image in another
# variable and converting to gray scale.
img = cv2.imread('test.jpg', cv2.IMREAD_GRAYSCALE)

# Converting image to a binary image
# ( black and white only image).
_, threshold = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY)

# Detecting contours in image.
contours, _= cv2.findContours(threshold, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)

# Going through every contours found in the image.
for cnt in contours :

approx = cv2.approxPolyDP(cnt, 0.009 * cv2.arcLength(cnt, True), True)

# draws boundary of contours.
cv2.drawContours(img2, [approx], 0, (0, 0, 255), 5)

# Used to flatted the array containing
# the co-ordinates of the vertices.
n = approx.ravel()
i = 0

for j in n :
if(i % 2 == 0):
x = n[i]
y = n[i + 1]

# String containing the co-ordinates.
string = str(x) + " " + str(y)

if(i == 0):
# text on topmost co-ordinate.
cv2.putText(img2, "Arrow tip", (x, y),
font, 0.5, (255, 0, 0))
else:
# text on remaining co-ordinates.
cv2.putText(img2, string, (x, y),
font, 0.5, (0, 255, 0))
i = i + 1

# Showing the final image.
cv2.imshow('image2', img2)

# Exiting the window if 'q' is pressed on the keyboard.
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()

Input image

Output image

OPenCV : How to get the perimeter coordinates around image

The way I approached your problem is by trying to threshold the whole perimeter you are looking for. To do that, I found the background color in the image and used it to filter the original image and erode the selection to make it slightly bigger and remove some of the artifacts.
Then, similar to your code, find the contours, remove smaller contours and approximate each one to a polygon. Once you have that approximation, you can simply draw it as a contour, which is very efficient.
To calculate the approximation I use 3% difference with the original contour perimeter, calculated as: cv2.arcLength(c, True) for each contour.

mask = cv2.inRange(img, np.array([240, 240, 240]), np.array([244, 244, 244]))
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
newmask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel, iterations=2)

contours, hierarchy = cv2.findContours(~newmask, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)

output = np.zeros_like(img[:, :, 0])
for c in [c for c in contours if cv2.contourArea(c) > 200]:
approx = cv2.approxPolyDP(c, 0.03*cv2.arcLength(c, True), True)
cv2.drawContours(output, [approx], -1, 255, 2)
plt.imshow(output, 'gray')

Result of the code above

Getting the co-ordinates of the highest point of the contour

To get the x and y coordinate, you need to use

xList = [x[0][0] for x in cnts]
yList = [y[0][1] for y in cnts]

x[0] gives you the tuple containing the x, y coordinates of the point and x[0][0] gives you the x coordinate, and x[0][1] gives you the y coordinate.

If you want the 'topmost' coordinate that you've circled, you'll need to do a lot more noise cleanup. Currently, the topmost point is going to the be the artifacts at the top edge of the image. A few morphological operations like cv2.dilate() and cv2.erode() could have helped but the target edge is quite thin, so it may distort the target edge..

enter image description here

The red lines show the contours that have been detected. Notice that the edge of the image has been selected as a contour because there are non-zero pixels on the edge of the image. You'll need to remove that noise to detect your desired top-most point. There's a lot of work to be done to get to your desired result.

Here's the code

import numpy as np
import matplotlib.pyplot as plt
import numpy as np
import cv2

font = cv2.FONT_HERSHEY_COMPLEX
img2 = cv2.imread(r"/path/to/image", cv2.IMREAD_COLOR)

# Reading same image in another
# variable and converting to gray scale.
img = cv2.imread(r"/path/to/image", cv2.IMREAD_GRAYSCALE)

# Converting image to a binary image
# ( black and white only image).
_, threshold = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY)

# Detecting contours in image.
contours, _= cv2.findContours(threshold, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)

# Going through every contours found in the image.
for cnt in contours :

approx = cv2.approxPolyDP(cnt, 0.009 * cv2.arcLength(cnt, True), True)

# draws boundary of contours.
cv2.drawContours(img2, [approx], 0, (0, 0, 255), 2)

# Used to flatted the array containing
# the co-ordinates of the vertices.
n = approx.ravel()
print(n)
i = 0

for j in n :
if(i % 2 == 0):
x = n[i]
y = n[i + 1]

# String containing the co-ordinates.
string = str(x) + " " + str(y)

# if(i == 0):
# # text on topmost co-ordinate.
# cv2.putText(img2, "Arrow tip", (x, y),
# font, 1, (255, 0, 0))
# else:
# # text on remaining co-ordinates.
# cv2.putText(img2, string, (x, y),
# font, 1, (0, 255, 0))

i = i + 1

xList = [x[0][0] for x in cnt]
yList = [y[0][1] for y in cnt]
print("x", xList)
print("y", yList)

# Showing the final image.
cv2.imwrite('image2.png', img2)

Extracting the coordinates from a single contour detected by the findcontours in opencv using python

It seems your array has an extra dimension, so you could just remove it, and then indexing should work.

x = c.squeeze()[:, 0]

How to determine between inner and outer contour with Python OpenCV?

From what I understand, you're trying to differentiate between outer and inner contours. To determine what contours are IN and OUT, you can simply use contour hierarchy to differentiate between the two. Specifically, when using cv2.findContours you can use cv2.RETR_TREE to extract outer or inner contours. See understanding contour hierarchies: how to distinguish between contours for the full explanation. From the full topology map we are given, we can filter out the contours, the idea is that if a parent contour has inner contours, it means that it's an OUT contour and the child inside is the IN contour. Another case is if a contour has no inner child then we know it is an IN contour.

Here's an example to demonstrate:

Input image

Sample Image

Result

Sample Image

Code

import cv2

# Load image, grayscale, Otsu's threshold
image = cv2.imread('2.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Filter using contour hierarchy
cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
hierarchy = hierarchy[0]
for component in zip(cnts, hierarchy):
currentContour = component[0]
currentHierarchy = component[1]
x,y,w,h = cv2.boundingRect(currentContour)
# Has inner contours which means it is IN
if currentHierarchy[2] < 0:
cv2.putText(image, 'IN', (x,y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)
# No child which means it is OUT
elif currentHierarchy[3] < 0:
cv2.putText(image, 'OUT', (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)

cv2.imshow('image', image)
cv2.waitKey()

How can I get particular count of extreme points from a static image even after resizing it in python?

Here is a simple example in Python/OpenCV where I scale the image by 0.5 and the vertices of the polygon by 0.5.

Input:

Sample Image

import cv2
import numpy as np

# set scale factor
scale = 0.5

# load image
img = cv2.imread("diamond.png")

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold to binary
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]

# get the largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# reduce number of vertices to 4
peri = cv2.arcLength(big_contour, True)
vertices = cv2.approxPolyDP(big_contour, 0.05 * peri, True)
print(len(vertices))
for vertex in vertices:
print(vertex)

# draw vertices as polyline on copy of input
polygon_full_res = img.copy()
cv2.polylines(polygon_full_res, [vertices], True, (0,0,255), 1, cv2.LINE_AA)

# resize image
img_small = cv2.resize(img, (0,0), fx=scale, fy=scale)

# resize vertices
print('')
new_vertices = []
for vertex in vertices:
xx = int(scale * vertex[0][0])
yy = int(scale * vertex[0][1])
new_vertex = [xx,yy]
new_vertices.append(new_vertex)

# convert list of points to numpy array of points
vertex_array = np.array([new_vertices])
print(vertex_array)

# draw new_vertices as polyline on copy of resized image
polygon_low_res = img_small.copy()
cv2.polylines(polygon_low_res, [vertex_array], True, (0,0,255), 1, cv2.LINE_AA)

# write result to disk
cv2.imwrite("polygon_full_res.png", polygon_full_res)
cv2.imwrite("polygon_low_res.png", polygon_low_res)

# display it
cv2.imshow("polygon_full_res", polygon_full_res)
cv2.imshow("polygon_low_res", polygon_low_res)
cv2.waitKey(0)
Input with polygon drawn:

Sample Image

Vertex Coordinates:

[[232  21]]
[[ 21 233]]
[[233 444]]
[[444 232]]

Downsized image with polygon drawn on it:

Sample Image

Downsized vertex coordinates:

[[[116  10]
[ 10 116]
[116 222]
[222 116]]]

Detecting zone with some color and text with openCV in python

Approach for the title:

  • find the buttons that sit to the left and right of it, using matchTemplate
  • title is a rectangle relative to those

Approach for the table:

  • inRange on color of table header
  • connectedComponentsWithStats
  • filter by height to find only table header cells
  • find the widest cell
  • use striped background to separate rows

entire thing: https://gist.github.com/crackwitz/54a2a8ed3fdb2d07b969ef5aeae9dfcf

utility functions:

def crop(im, x, y, w, h):
(height, width) = im.shape[:2]
assert w > 0 and h > 0
assert x >= 0 and y >= 0
assert (x+w <= width) and (y+h <= height)
return im[y:y+h, x:x+w]

def find_template(haystack, needle):
(nw, nh) = needle.shape[:2]
scores = cv.matchTemplate(haystack, needle, method=cv.TM_SQDIFF)
(minval, maxval, minloc, maxloc) = cv.minMaxLoc(scores)
#print(minval, minloc)
# minval ought to be 0... bug?
assert minval <= nw*nh*3 * 1**2, "can't find template"
(x,y) = minloc
return (x, y, nw, nh)

load:

im = cv.imread("YebIa.png")#, cv.IMREAD_GRAYSCALE)
(imh, imw) = im.shape[:2]
print("size:", imw, 'x', imh)
imshow(im)

extract button templates from hand-picked coordinates in this specific picture. best to save those and imread instead:

button1 = crop(im, 214, 88, 24, 24)
imshow(button1)
button2 = crop(im, 672, 88, 24, 24)
imshow(button2)

find buttons, get title:

button1_rect = find_template(im, button1)
button2_rect = find_template(im, button2)
b1x, b1y, b1w, b1h = button1_rect
b2x, b2y, b2w, b2h = button2_rect

top = b1y
bottom = b1y + b1h
left = b1x + b1w
right = b2x
title = crop(im, left, top, right-left, bottom-top)
imshow(title)

title

inRange:

# table header, first cell is largest
header_color = (194, 142, 93)
mask = cv.inRange(im, header_color, header_color)

connected components:

(nlabels, labels, stats, centroids) = cv.connectedComponentsWithStats(mask)
# print(stats) # x, y, w, h, area (ConnectedComponentsTypes)

filter and sort components:

comps = [(label, *stat) for label, stat in enumerate(stats)]
# (label, x, y, w, h, area)
comps = [comp for comp in comps if comp[4] == 25] # height: exactly 25 pixels
comps.sort(key=lambda comp: comp[5], reverse=True) # area, descending... or simply max(key=)
header_comp = comps[0] # largest area
header_rect = header_comp[1:5]
(hx,hy,hw,hh) = header_rect
header = crop(im, *header_rect)
imshow(header)

header

find table body and rows:

# table body
# pixel column just before the header cell (B) contains striped background but no text
# column to the left of that (A) contains only white, until the end
bx = hx
by = hy+hh + 1
bw = hw

columnA = crop(im, bx-2, by, 1, imh-by)[:,0,1]
(I,) = np.where(columnA != 255)
bh = I.min() # table body height

columnB = crop(im, bx-1, by, 1, bh)[:,0,1]
rowmask = (columnB == 255)#.astype(np.int8)
(I,) = np.where(np.diff(rowmask))
I += 1 # diff shifts things back, edge is on the second pixel, not the first

row_tops = np.concatenate(([0], I[:-1]))
row_bottoms = I
# np.vstack([row_tops, row_bottoms]).T

extract each row:

print((bx, by, bw, bh))
for i,(top,bottom) in enumerate(zip(row_tops, row_bottoms)):
print(f"row {i+1}:")
imshow(crop(im, bx, by+top, bw, bottom-top))
print()

rows

How to sort contours of a grid using OpenCV python?

Sample Image

The idea is after finding contours on the thresholded image, we utilize imutils.contours.sort_contours() to sort the contours from bottom-to-top. Next we take each row of 8 squares and sort this row from left-to-right. Here's a visualization of the sorting:

Sample Image

import cv2
from imutils import contours

# Load image, grayscale, gaussian blur, Otsu's threshold
image = cv2.imread("1.jpg")
original = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5,5), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Find all contour and sort from top-to-bottom or bottom-to-top
cnts, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2:]
(cnts, _) = contours.sort_contours(cnts, method="bottom-to-top")

# Take each row of 8 and sort from left-to-right
checkerboard_row = []
row = []
for (i, c) in enumerate(cnts, 1):
row.append(c)
if i % 8 == 0:
(cnts, _) = contours.sort_contours(row, method="left-to-right")
checkerboard_row.append(cnts)
row = []

# Draw text
number = 0
for row in checkerboard_row:
for c in row:
M = cv2.moments(c)
x = int(M['m10']/M['m00'])
y = int(M['m01']/M['m00'])
cv2.putText(original, "{}".format(number + 1), (x - 20,y), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255,50,10), 2)
number += 1

cv2.imshow('original', original)
cv2.waitKey()

Note: You could also change the sort direction such as right-to-left or top-to-bottom and so on



Related Topics



Leave a reply



Submit