What I want :
When the user clicks on a button, the program takes the screnshot in the clipboard 'no problem with this part), detects Zone Of Interest (ZOI), cuts the zone and extracts the text of multiple zones for my programme. I try to extract information from a prescription. I know the zones of interests will always be in the same rectangle of color but I'm not sure the prescription will be in fullsize each time I need to extract information. You can see the original image and what I want the ZOI are in red.
- First time I tried making the contour of the zone using the color in the font. The program can make a zone around the grey specific zone but the isn't what I want (green box). You can see above the original image, post-process image and the code
import numpy as np
import cv2
# Read input image
img = cv2.imread('test_image.png')
gray = np.all(img == (227,227,227), 2)
# Convert logical matrix to uint8
gray = gray.astype(np.uint8)*255
# Find contours
cnts = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[-2] # Use index [-2] to be compatible to OpenCV 3 and 4
# Get contour with maximum area
c = max(cnts, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(c)
# Draw green rectangle for testing
cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 0), thickness = 2)
# Show result
cv2.imshow('gray', gray)
cv2.imwrite('niveau_gris.jpg', gray)
cv2.imshow('img', img)
cv2.imwrite('test_image_resultat.jpg', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
- Second time I tried making the contour of the zone using two parametres in the aim to select the zone with the text. The program doesn't make a zone at all. You can see the code below
import numpy as np
import cv2
frame = cv2.imread('test_image_constrasate.jpg')
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_red = np.array([189,189,189])
upper_red = np.array([204,203,204])
mask = cv2.inRange (hsv, lower_red, upper_red)
contours = cv2.findContours(mask.copy(),
cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)[-2]
if len(contours) > 0:
red_area = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(red_area)
cv2.rectangle(frame,(x, y),(x w, y h),(0, 0, 255), 2)
cv2.imshow('frame', frame)
cv2.imshow('mask', mask)
cv2.waitKey(0)
- Third time I try using Hough Line Transform but I'm not experimented enough with working with image I'm no sure of the repeatability of the process because the screenshot maybe not be fullsize and I'm not sure how to use the line to make boxes.
provide research
I sought on StackoverFlow with the following terms : "OpenCv select rectangle", "OpenCV select area base on the color", "OpenCV how to select a zone base on his color"....
Can you help me ? Thank you for your future help
CodePudding user response:
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 headerconnectedComponentsWithStats
- filter by height to find only table header cells
- find the widest cell
- use striped background to separate rows
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)
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()