Splitting HSV mask into multiple rectangles - python-3.x

I created a HSV mask from the image. The result like following:
My goal is draw muliple rectangles that fit mask height or width, like following:
I encouter 2 problem.
I don't know how to locate the starting and ending point in mask for creating rectangle. If I use for loop to scan though mask row by row, it may split mask into 2 part.
Sometime, there also contain 2 different mask in one image. How can I draw rectangles?
Anyone can give me some suggestion?

You can search for your biggest contour (cross-like shape) with cv2.findContour(). It returns an array of coordinates of the contour. Then you can search your contour for point that has the highest X coordinate (that being your most right point), lowest X coordinat (most left point), highest Y coordinate (being most bottom point) and lowest Y coordinate (being your highest point). After you have all 4 values you can search the contour again for all points that have that values and append them in 4 different lists which you sort them later so you can get these points as drown on the bottom picture:
cv2.circle(img,(top_vertical[0]), 4, (0,0,255), -1)
cv2.circle(img,(top_vertical[-1]), 4, (0,0,255), -1)
cv2.circle(img,(bottom_vertical[0]), 4, (0,0,255), -1)
cv2.circle(img,(bottom_vertical[-1]), 4, (0,0,255), -1)
cv2.circle(img,(left_horizontal[0]), 4, (0,0,255), -1)
cv2.circle(img,(left_horizontal[-1]), 4, (0,0,255), -1)
cv2.circle(img,(right_horizontal[0]), 4, (0,0,255), -1)
cv2.circle(img,(right_horizontal[-1]), 4, (0,0,255), -1)
From this point forward I have transformed the lists into numpy arrays as it is easier for me. You can do it any other way.
Then it is just a matter of how many rectangles you want and how do you want to display them. In my example code you have to input how many same size rectangles you want and the last one is the size of what is left. I have first displayed rectangles on Y coordinate (green color) and then on X coordinate, which is divided on two segments (left and right) because they slightly vary in distance and I did not want to draw over the Y coordinate rectangles as they are not drawn on your example image. You can change the logic of writting the rectangles as you wish. Hope it helps a bit or give an idea on how to proceede. Cheers!
Example code:
import cv2
import numpy as np
# Read image and search for contours.
img = cv2.imread('cross.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, threshold = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)
_, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
# Select the biggest contour (if you wish to segmentize only the cross-like contour).
cnt = max(contours, key=cv2.contourArea)
# Create empty lists for appending key points.
top_vertical = []
bottom_vertical = []
left_horizontal = []
right_horizontal = []
# Setting the starter values for N, S, E, W.
top = 10000
bottom = 0
left = 10000
right = 0
# Loop to get highest key values of N, S, E, W.
for i in cnt:
y = int(i[:,1])
x = int(i[:, 0])
if x < left:
left = int(x)
if x > right:
right = int(x)
if y < top:
top = int(y)
if y > bottom:
bottom = int(y)
# Loop for appending all points containing key values of N, S, E, W.
for i in cnt:
if int(i[:,1]) == top:
up = (int(i[:,0]), int(i[:,1]))
top_vertical.append(up)
if int(i[:,1]) == bottom:
down = (int(i[:,0]), int(i[:,1]))
bottom_vertical.append(down)
if int(i[:,0]) == left:
l = (int(i[:,0]), int(i[:,1]))
left_horizontal.append(l)
if int(i[:,0]) == right:
r = (int(i[:,0]), int(i[:,1]))
right_horizontal.append(r)
# Sorting the lists.
top_vertical.sort(key=lambda tup: tup[0])
bottom_vertical.sort(key=lambda tup: tup[0])
left_horizontal.sort(key=lambda tup: tup[1])
right_horizontal.sort(key=lambda tup: tup[1])
# Optional drawing of key points.
'''cv2.circle(img,(top_vertical[0]), 4, (0,0,255), -1)
cv2.circle(img,(top_vertical[-1]), 4, (0,0,255), -1)
cv2.circle(img,(bottom_vertical[0]), 4, (0,0,255), -1)
cv2.circle(img,(bottom_vertical[-1]), 4, (0,0,255), -1)
cv2.circle(img,(left_horizontal[0]), 4, (0,0,255), -1)
cv2.circle(img,(left_horizontal[-1]), 4, (0,0,255), -1)
cv2.circle(img,(right_horizontal[0]), 4, (0,0,255), -1)
cv2.circle(img,(right_horizontal[-1]), 4, (0,0,255), -1)'''
# Transforming lists to arrays.
top_vertical = np.array(top_vertical)
bottom_vertical = np.array(bottom_vertical)
left_horizontal = np.array(left_horizontal)
right_horizontal = np.array(right_horizontal)
# Calculating height and weight of the contour.
distance_y = bottom - top
distance_x = right - left
# Inputs for the number of same size segments.
a = input('Input the number of same size segments in Y coordinate: ')
b = input('Input the number of same size segments in left X coordinate: ')
c = input('Input the number of same size segments in right X coordinate: ')
# Calculation of area per segment and limit for the lenght of combined segments (height and weight) .
segment_y = distance_y/int(a)
segment_x_reference = int(top_vertical[0,0]) - int(left_horizontal[0,0])
segment_x = segment_x_reference/int(b)
segment_x_right_reference = int(right_horizontal[0,0]) - int(top_vertical[-1,0])
segment_x_right = segment_x_right_reference/int(c)
# Drawing rectangles on the Y axis.
for i in range(1,20):
sq = int(segment_y)*i
if sq < distance_y:
cv2.rectangle(img,(top_vertical[0,0], top_vertical[0,1]),((top_vertical[-1,0]),top_vertical[0,1] + sq),(0,255,0),1)
else:
sq = distance_y
cv2.rectangle(img,(top_vertical[0,0], top_vertical[0,1]),((top_vertical[-1,0]),sq),(0,255,0),1)
break
# Drawing rectangles on the left side of X axis.
for i in range(1,20):
sq = int(segment_x)*i
if sq < segment_x_reference:
cv2.rectangle(img,(left_horizontal[0,0], left_horizontal[0,1]),((left_horizontal[0,0])+sq, left_horizontal[-1,1]),(255,0,0),1)
else:
sq = segment_x_reference
cv2.rectangle(img,(left_horizontal[0,0], left_horizontal[0,1]),((left_horizontal[0,0])+sq, left_horizontal[-1,1]),(255,0,0),1)
break
# Drawing rectangles on the right side of X axis.
for i in range(1,20):
sq = int(segment_x_right)*i
if sq < segment_x_right_reference:
cv2.rectangle(img,(right_horizontal[0,0], right_horizontal[0,1]),((right_horizontal[0,0])-sq, right_horizontal[-1,1]),(255,0,0),1)
else:
sq = segment_x_right_reference
cv2.rectangle(img,(right_horizontal[0,0], right_horizontal[0,1]),((right_horizontal[0,0])-sq, right_horizontal[-1,1]),(255,0,0),1)
break
# Displaying result.
cv2.imshow('img', img)
Result:
Input the number of same size segments in Y coordinate: 5
Input the number of same size segments in left X coordinate: 2
Input the number of same size segments in right X coordinate: 2

Related

How to count the vehicles if boundingRect touch the line

Below is the code i am trying:
def mouse_drawing(event, x, y, flags, params):
global point1, point2, drawing
if event == cv2.EVENT_LBUTTONDOWN:
if drawing is False:
drawing = True
point1 = (x, y)
else:
drawing = False
elif event == cv2.EVENT_MOUSEMOVE:
if drawing is True:
point2 = (x, y)
cap = cv2.VideoCapture("new2.asf")
cv2.namedWindow("App", cv2.WINDOW_FREERATIO)
cv2.setMouseCallback("App", mouse_drawing)
fgbg = cv2.createBackgroundSubtractorMOG2()
kernel = np.ones((5, 5), np.uint8)
while True:
ret, frame = cap.read()
frame = cv2.resize(frame,None,fx=scaling_factorx,fy=scaling_factory,interpolation=cv2.INTER_AREA)
imgray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
fgmask1 = cv2.GaussianBlur(imgray, (7,7), 0)
fgmask = fgbg.apply(fgmask1)
if point1 and point2:
cv2.line(frame, point1, point2, (0, 255, 0), 3)
contours, hierarchy = cv2.findContours(fgmask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
try:
hierarchy = hierarchy[0]
except:
hierarchy = []
for contour, hier in zip(contours, hierarchy):
(x, y, w, h) = cv2.boundingRect(contour)
if w > 80 and h > 80:
cv2.rectangle(frame, (x,y), (x+w,y+h), (0, 255, 0), 2)
cv2.imshow("App", frame)
How to write a image of a vehicle with cv2.imwrite which is reached the line, the line which is drawn as manually. And the vehicles are having the rectangular boxes that's fine But some vehicles having more than one box. One vehicle should be have only one rectangular box. And that should be saved if reached the line, rest of vehicles should not to be saved. Please let me know the solution.
First, you need to group intersecting rectangles into one.
You do so by checking the intersection area between each pair of rectangles.
If the intersection area is larger than a pre-defined heuristic ratio of the small rectangle area then the smaller rectangle should be removed. For example, intersection_area / smaller_rect_area > 0.75
Please check this answer for the rectangles intersection.
Second, to check that a rectangle has passed the line:
Use your points to find the parameters for the general line formula: ax + by + c = 0
For each frame, plug the rectangle center coordinates in the formula and keep track of the sign of the result.
If the sign of the result changes that means that the rectangle center has passed the line.

What is the best way to extract text contained within a table in a pdf using python?

I'm constructing a program to extract text from a pdf, put it in a structured format, and send it off to a database. I have roughly 1,400 individual pdfs that all follow a similar format, but nuances in the verbiage and plan designs that the documents summarize make it tricky.
I've played around with a couple different pdf readers in python including tabula-py and pdfminer but none of them are quite getting to what I'd like to do. Tabula reads in all of the text very well, however it pulls everything as it explicitly lays horizontally, excluding the fact that some of the text is wrapped in a box. For example, if you open up the sample SBC I have attached where it reads "What is the overall deductible?" Tabula will read in "What is the overall $500/Individual or..." skipping the fact that the word "deductible" is really part of the first sentence. (Note the files I'm working with are pdfs but I've attached a jpeg because I couldn't figure out how to attach a pdf.)
import tabula
df = tabula.read_pdf(*filepath*, pandas_options={'header': None))
print(df.iloc[0][0])
print(df)
In the end, I'd really like to be able to parse out the text within each box so that I can better identify what values belong to deductible, out-of-pocket limts, copays/coinsurance, etc. I thought possibly some sort of OCR would allow me to recognize which parts of the PDF are contained in the blue rectangles and then pull the string from there, but I really don't know where to start with that.Sample SBC
#jpnadas In this case the code you copied from my answer in this post isn't really suitable because it addresses the case when a table doesn't have surrounding grid. That algorithm looks for repeating blocks of texts and tries to find a pattern that resembles a table heuristically.
But in this particular case the table does have the grid and by taking this advantage we can achieve a lot more accurate result.
The strategy is the following:
Increase image gamma to make the grid darker
Get rid of colour and apply Otsu thresholding
Find long vertical an horizontal lines in the image and create a mask from it using erode and dilate functions
Find the cell blocks in the mask using findContours function.
Find table objects
5.1 The rest can be as in the post about finding a table without the
grid: find table structure heuristically
5.2 Alternative approach could be using hierarchy returned by the findContours function. This approach is even more accurate and
allows to find multiple tables on a single image.
Having cell coordinates it's easy to extract certain cell image from the original image:
cell_image = image[cell_y:cell_y + cell_h, cell_x:cell_x + cell_w]
Apply OCR to each cell_image.
BUT! I consider the OpenCV approach as a last resort when you're not able to read the PDF's contents: for instance in case when a PDF contains raster image inside.
If it's a vector-based PDF and its contents are readable it makes more sense to find the table inside contents and just read the text from it instead of doing heavy 'OCR lifting'.
Here's the code for reference for more accurate table recognition:
import os
import imutils
import numpy as np
import argparse
import cv2
def gamma_correction(image, gamma = 1.0):
look_up_table = np.empty((1,256), np.uint8)
for i in range(256):
look_up_table[0,i] = np.clip(pow(i / 255.0, gamma) * 255.0, 0, 255)
result = cv2.LUT(image, look_up_table)
return result
def pre_process_image(image):
# Let's get rid of color first
# Applying gamma to make the table lines darker
gamma = gamma_correction(image, 2)
# Getting rid of color
gray = cv2.cvtColor(gamma, cv2.COLOR_BGR2GRAY)
# Then apply Otsu threshold to reveal important areas
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# inverting the thresholded image
return ~thresh
def get_horizontal_lines_mask(image, horizontal_size=100):
horizontal = image.copy()
horizontal_structure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))
horizontal = cv2.erode(horizontal, horizontal_structure, anchor=(-1, -1), iterations=1)
horizontal = cv2.dilate(horizontal, horizontal_structure, anchor=(-1, -1), iterations=1)
return horizontal
def get_vertical_lines_mask(image, vertical_size=100):
vertical = image.copy()
vertical_structure = cv2.getStructuringElement(cv2.MORPH_RECT, (1, vertical_size))
vertical = cv2.erode(vertical, vertical_structure, anchor=(-1, -1), iterations=1)
vertical = cv2.dilate(vertical, vertical_structure, anchor=(-1, -1), iterations=1)
return vertical
def make_lines_mask(preprocessed, min_horizontal_line_size=100, min_vertical_line_size=100):
hor = get_horizontal_lines_mask(preprocessed, min_horizontal_line_size)
ver = get_vertical_lines_mask(preprocessed, min_vertical_line_size)
mask = np.zeros((preprocessed.shape[0], preprocessed.shape[1], 1), dtype=np.uint8)
mask = cv2.bitwise_or(mask, hor)
mask = cv2.bitwise_or(mask, ver)
return ~mask
def find_cell_boxes(mask):
# Looking for the text spots contours
# OpenCV 3
# img, contours, hierarchy = cv2.findContours(pre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# OpenCV 4
contours = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
contours = sorted(contours, key=cv2.contourArea, reverse=True)
image_width = mask.shape[1]
# Getting the texts bounding boxes based on the text size assumptions
boxes = []
for contour in contours:
box = cv2.boundingRect(contour)
w = box[2]
# Excluding the page box shape but adding smaller boxes
if w < 0.95 * image_width:
boxes.append(box)
return boxes
def find_table_in_boxes(boxes, cell_threshold=10, min_columns=2):
rows = {}
cols = {}
# Clustering the bounding boxes by their positions
for box in boxes:
(x, y, w, h) = box
col_key = x // cell_threshold
row_key = y // cell_threshold
cols[row_key] = [box] if col_key not in cols else cols[col_key] + [box]
rows[row_key] = [box] if row_key not in rows else rows[row_key] + [box]
# Filtering out the clusters having less than 2 cols
table_cells = list(filter(lambda r: len(r) >= min_columns, rows.values()))
# Sorting the row cells by x coord
table_cells = [list(sorted(tb)) for tb in table_cells]
# Sorting rows by the y coord
table_cells = list(sorted(table_cells, key=lambda r: r[0][1]))
return table_cells
def build_vertical_lines(table_cells):
if table_cells is None or len(table_cells) <= 0:
return [], []
max_last_col_width_row = max(table_cells, key=lambda b: b[-1][2])
max_x = max_last_col_width_row[-1][0] + max_last_col_width_row[-1][2]
max_last_row_height_box = max(table_cells[-1], key=lambda b: b[3])
max_y = max_last_row_height_box[1] + max_last_row_height_box[3]
hor_lines = []
ver_lines = []
for box in table_cells:
x = box[0][0]
y = box[0][1]
hor_lines.append((x, y, max_x, y))
for box in table_cells[0]:
x = box[0]
y = box[1]
ver_lines.append((x, y, x, max_y))
(x, y, w, h) = table_cells[0][-1]
ver_lines.append((max_x, y, max_x, max_y))
(x, y, w, h) = table_cells[0][0]
hor_lines.append((x, max_y, max_x, max_y))
return hor_lines, ver_lines
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to images directory")
args = vars(ap.parse_args())
in_file = args["image"]
filename_base = in_file.replace(os.path.splitext(in_file)[1], "")
img = cv2.imread(in_file)
pre_processed = pre_process_image(img)
# Visualizing pre-processed image
cv2.imwrite(filename_base + ".pre.png", pre_processed)
lines_mask = make_lines_mask(pre_processed, min_horizontal_line_size=1800, min_vertical_line_size=500)
# Visualizing table lines mask
cv2.imwrite(filename_base + ".mask.png", lines_mask)
cell_boxes = find_cell_boxes(lines_mask)
cells = find_table_in_boxes(cell_boxes)
# apply OCR to each cell rect here
# the cells array contains cell coordinates in tuples (x, y, w, h)
hor_lines, ver_lines = build_vertical_lines(cells)
# Visualize the table lines
vis = img.copy()
for line in hor_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
for line in ver_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv2.imwrite(filename_base + ".result.png", vis)
Some parameters are hard-coded:
page size threshold - 0.95
min horizontal line size - 1800 px
min vertical line size - 500 px
You can provide them as configurable parameters or make them relative to image size.
Results:
I think that the best way to do what you need is to find and isolate the cells in the file and then apply OCR to each individual cell.
There are a number of solutions in SO for that, I got the code from this answer and played around a little with the parameters to get the output below (not perfect yet, but you can tweak it a little bit yourself).
import os
import cv2
import imutils
# This only works if there's only one table on a page
# Important parameters:
# - morph_size
# - min_text_height_limit
# - max_text_height_limit
# - cell_threshold
# - min_columns
def pre_process_image(img, save_in_file, morph_size=(23, 23)):
# get rid of the color
pre = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Otsu threshold
pre = cv2.threshold(pre, 250, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# dilate the text to make it solid spot
cpy = pre.copy()
struct = cv2.getStructuringElement(cv2.MORPH_RECT, morph_size)
cpy = cv2.dilate(~cpy, struct, anchor=(-1, -1), iterations=1)
pre = ~cpy
if save_in_file is not None:
cv2.imwrite(save_in_file, pre)
return pre
def find_text_boxes(pre, min_text_height_limit=20, max_text_height_limit=120):
# Looking for the text spots contours
contours, _ = cv2.findContours(pre, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# Getting the texts bounding boxes based on the text size assumptions
boxes = []
for contour in contours:
box = cv2.boundingRect(contour)
h = box[3]
if min_text_height_limit < h < max_text_height_limit:
boxes.append(box)
return boxes
def find_table_in_boxes(boxes, cell_threshold=100, min_columns=3):
rows = {}
cols = {}
# Clustering the bounding boxes by their positions
for box in boxes:
(x, y, w, h) = box
col_key = x // cell_threshold
row_key = y // cell_threshold
cols[row_key] = [box] if col_key not in cols else cols[col_key] + [box]
rows[row_key] = [box] if row_key not in rows else rows[row_key] + [box]
# Filtering out the clusters having less than 2 cols
table_cells = list(filter(lambda r: len(r) >= min_columns, rows.values()))
# Sorting the row cells by x coord
table_cells = [list(sorted(tb)) for tb in table_cells]
# Sorting rows by the y coord
table_cells = list(sorted(table_cells, key=lambda r: r[0][1]))
return table_cells
def build_lines(table_cells):
if table_cells is None or len(table_cells) <= 0:
return [], []
max_last_col_width_row = max(table_cells, key=lambda b: b[-1][2])
max_x = max_last_col_width_row[-1][0] + max_last_col_width_row[-1][2]
max_last_row_height_box = max(table_cells[-1], key=lambda b: b[3])
max_y = max_last_row_height_box[1] + max_last_row_height_box[3]
hor_lines = []
ver_lines = []
for box in table_cells:
x = box[0][0]
y = box[0][1]
hor_lines.append((x, y, max_x, y))
for box in table_cells[0]:
x = box[0]
y = box[1]
ver_lines.append((x, y, x, max_y))
(x, y, w, h) = table_cells[0][-1]
ver_lines.append((max_x, y, max_x, max_y))
(x, y, w, h) = table_cells[0][0]
hor_lines.append((x, max_y, max_x, max_y))
return hor_lines, ver_lines
if __name__ == "__main__":
in_file = os.path.join(".", "test.jpg")
pre_file = os.path.join(".", "pre.png")
out_file = os.path.join(".", "out.png")
img = cv2.imread(os.path.join(in_file))
pre_processed = pre_process_image(img, pre_file)
text_boxes = find_text_boxes(pre_processed)
cells = find_table_in_boxes(text_boxes)
hor_lines, ver_lines = build_lines(cells)
# Visualize the result
vis = img.copy()
# for box in text_boxes:
# (x, y, w, h) = box
# cv2.rectangle(vis, (x, y), (x + w - 2, y + h - 2), (0, 255, 0), 1)
for line in hor_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
for line in ver_lines:
[x1, y1, x2, y2] = line
cv2.line(vis, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv2.imwrite(out_file, vis)

How to plot 3D voxels with given coordinates on a sphere using matplotlib

I'm currently trying to make a 3D voxels plot with know coordinates on a sphere. The x, y and z coordinates are lists filtered from a CSV. Additionally I have another list with same length as x, y and z containing the color of the voxel. With these list I want to create a 3D voxel sphere.
This I already accomplished using a 3D scatter plot (matplotlib) but the result is not very clear:
x = []
y = []
z = []
c = []
red = (1, 0, 0, 1)
green = (0, 1, 0, 1)
blue = (0, 0, 1, 1)
black = (0, 0, 0, 1)
for i in range(len(result)):
x.append(20 * math.sin(math.radians(90 - result[i][1])) * math.cos(math.radians(result[i][0])))
y.append(20 * math.sin(math.radians(90 - result[i][1])) * math.sin(math.radians(result[i][0])))
z.append(20 * math.cos(math.radians(90 - result[i][1])))
if result[i][2] == 1000:
c.append(black)
elif result[i][2] > 500:
c.append(red)
elif 200 < result[i][2] <= 500:
c.append(blue)
else:
c.append(green)
fig = pyplot.figure()
ax = Axes3D(fig)
ax.grid(True)
ax.scatter(x, y, z, c=c, s=500)
pyplot.show()
3D-Scatter

Python and OpenCV: sort list of contours according to two criteria

In Python, I have a list of contours. every contour is a numpy array.
every contour is a square as in the following image:
every contour has cx and cy - which are the moment of the contour - the center of it.
I calculated also the mean rgb of every contour and added it to the list.
How can I sort the contours as you can see in the first images from 1-24 - from top left to bottom right - row by row using ONLY (cx,cy)?
My code:
def find_contour_mean_color_value(self , img , width=None , height=None , full_square=False):
contours = []
for (i,cnt) in enumerate(self.all_detected_color_squares):
mom = cv2.moments(cnt)
(cx,cy) = int(mom['m10']/mom['m00']), int(mom['m01']/mom['m00'])
if full_square == True:
x,y,w,h = cv2.boundingRect(cnt)
roi = img[y:y+h, x:x+w]
else:
#define needed square around the center as following
center_square_width = width
center_square_height = height
x_1= int(cx-(center_square_width/2))
y_1 = int(cy-(center_square_height/2))
roi = img[y_1:y_1 + center_square_height , x_1:x_1 + center_square_width]
color = cv2.mean(roi)
(r,g,b) = (color[2] , color[1] , color[0])
contours.append((self.all_detected_color_squares , (cx ,cy) , (r,g,b) ))
self.all_detected_color_squares = np.array(contours)
How can we sort contours list as needed and described by the image and numbers?
I am sure that it is doable maybe using labmda but I am not able to do it.
For more details see:
This should return contours sorted by (cx, cy):
contours = sorted(contours, key = lambda x: x[1])
This can be done this way:
squares = sorted(detected_squares, key=lambda x: x[1][1])
for i in range(self.cols_num):
i = i*self.rows_num
j = i + self.rows_num
squares[i:j] = sorted(squares[i:j], key=lambda x: x[1][0])
detected_squares = squares

How to detect objects shape from images using python3?

i want to write a code in python3 that detects objects shapes from images.
I want to choose a pixel from an object in the given image and find the neighbours pixels.
If they have the same RGB value that means that they are part of the object.
When neighbour pixel changes the RGB value with an ajustable difference from original pixel the algorithm should stop searching for neighbours. I think that this will work unless the backgroud and object have the same color.
I have found a way to put the pixels with same color in an rectangle,but this will not help me. I want to save just the shape of the object and put it in a different image.
For example,
If i want to start my algorithm from the middle of an object, let's
say a black table with a white background,the algorithm will find
pixels with the same color in any direction. When the neighbour pixel
RGB values will change with more than 30 units in one direction,the
algorithm will stop going in that direction,and will start going in
another direction untill I have the shape of the table.
I found a code on another post that help me to determinate regions of pixels with a shared value using PIL
Thanks!
from collections import defaultdict
from PIL import Image, ImageDraw
def connected_components(edges):
"""
Given a graph represented by edges (i.e. pairs of nodes), generate its
connected components as sets of nodes.
Time complexity is linear with respect to the number of edges.
"""
neighbors = defaultdict(set)
for a, b in edges:
neighbors[a].add(b)
neighbors[b].add(a)
seen = set()
def component(node, neighbors=neighbors, seen=seen, see=seen.add):
unseen = set([node])
next_unseen = unseen.pop
while unseen:
node = next_unseen()
see(node)
unseen |= neighbors[node] - seen
yield node
return (set(component(node)) for node in neighbors if node not in seen)
def matching_pixels(image, test):
"""
Generate all pixel coordinates where pixel satisfies test.
"""
width, height = image.size
pixels = image.load()
for x in xrange(width):
for y in xrange(height):
if test(pixels[x, y]):
yield x, y
def make_edges(coordinates):
"""
Generate all pairs of neighboring pixel coordinates.
"""
coordinates = set(coordinates)
for x, y in coordinates:
if (x - 1, y - 1) in coordinates:
yield (x, y), (x - 1, y - 1)
if (x, y - 1) in coordinates:
yield (x, y), (x, y - 1)
if (x + 1, y - 1) in coordinates:
yield (x, y), (x + 1, y - 1)
if (x - 1, y) in coordinates:
yield (x, y), (x - 1, y)
yield (x, y), (x, y)
def boundingbox(coordinates):
"""
Return the bounding box of all coordinates.
"""
xs, ys = zip(*coordinates)
return min(xs), min(ys), max(xs), max(ys)
def disjoint_areas(image, test):
"""
Return the bounding boxes of all non-consecutive areas
who's pixels satisfy test.
"""
for each in connected_components(make_edges(matching_pixels(image, test))):
yield boundingbox(each)
def is_black_enough(pixel):
r, g, b = pixel
return r < 10 and g < 10 and b < 10
if __name__ == '__main__':
image = Image.open('some_image.jpg')
draw = ImageDraw.Draw(image)
for rect in disjoint_areas(image, is_black_enough):
draw.rectangle(rect, outline=(255, 0, 0))
image.show()
Try using opencv with Python.
With opencv you can make advanced image analysis and there are many tutorials to use it.
http://www.pyimagesearch.com/2014/04/21/building-pokedex-python-finding-game-boy-screen-step-4-6/

Resources