to detect patches in binary images using opencv python - python

I want to detect all the patches in the enter image description hereimage, I attached the code used to detect them:
import cv2
import numpy as np
import matplotlib.pyplot as plt
image=cv2.imread("bw2.jpg",0)
# convert to RGB
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# create a binary thresholded image
_, binary = cv2.threshold(gray, 0, 500, cv2.THRESH_BINARY_INV)
# show it
plt.imshow(gray, cmap="gray")
plt.show()
# find the contours from the thresholded image
contours, hierarchy = cv2.findContours(gray, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
print("contours:",contours)
# draw all contours
for c in contours:
if cv2.contourArea(c) < 3000:
continue
(x, y, w, h) = cv2.boundingRect(c)
#cv2.rectangle(image, (x,y), (x+w,y+h), (0, 255, 0), 2)
## BEGIN - draw rotated rectangle
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(image,[box],0,(255,51,255),2)
# show the image with the drawn contours
plt.imshow(image)
#plt.imshow(im3)
cv2.imwrite("detectImg2.png",image)
plt.show()
I get output image as hereenter image description here
I want to detect all of them, can anyone tell me how to achieve this I new to image processing

Here is how I would extract and rotate each blob in your image using Python OpenCV.
Read the input
Convert to gray
Threshold
Apply morphology open and close to clean small spots
Get all the external contours
Loop over each contour and do the following:
Draw the contour on a copy of the input image
Get the rotated rectangle of the contour and extract its center, dimensions and rotation angle
Get the corners of the rotated rectangle
Draw the rotated rectangle on another copy of the input
Correct the rotation angle for image unrotation
Generate a mask image with the filled rotated rectangle
Apply the mask image to the morphology cleaned image to remove near-by other white regions
Get the affine warp matrix using the center and corrected rotation angle
Unrotated the the masked image using warpAffine
Get the contour of the one blob in the unrotated image
Get the contours bounding box
Crop the masked image (or alternately crop the input image)
Save the cropped image
Exit the loop
Save the contour and rotrect images
Input:
import cv2
import numpy as np
image = cv2.imread("bw2.jpg")
hh, ww = image.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# create a binary thresholded image
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# apply morphology
kernel = np.ones((7,7), np.uint8)
clean = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = np.ones((13,13), np.uint8)
clean = cv2.morphologyEx(clean, cv2.MORPH_CLOSE, kernel)
# get external contours
contours = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
contour_img = image.copy()
rotrect_img = image.copy()
i = 1
for c in contours:
# draw contour on input
cv2.drawContours(contour_img,[c],0,(0,0,255),2)
# get rotated rectangle from contour
# get its dimensions
# get angle relative to horizontal from rotated rectangle
rotrect = cv2.minAreaRect(c)
(center), (width,height), angle = rotrect
box = cv2.boxPoints(rotrect)
boxpts = np.int0(box)
# draw rotated rectangle on copy of image
cv2.drawContours(rotrect_img,[boxpts],0,(0,255,0),2)
# from https://www.pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/
# the `cv2.minAreaRect` function returns values in the
# range [-90, 0); as the rectangle rotates clockwise the
# returned angle tends to 0 -- in this special case we
# need to add 90 degrees to the angle
if angle < -45:
angle = -(90 + angle)
# otherwise, check width vs height
else:
if width > height:
angle = -(90 + angle)
else:
angle = -angle
# negate the angle for deskewing
neg_angle = -angle
# draw mask as filled rotated rectangle on black background the size of the input
mask = np.zeros_like(clean)
cv2.drawContours(mask,[boxpts],0,255,-1)
# apply mask to cleaned image
blob_img = cv2.bitwise_and(clean, mask)
# Get rotation matrix
#center = (width // 2, height // 2)
M = cv2.getRotationMatrix2D(center, neg_angle, scale=1.0)
#print('m: ',M)
# deskew (unrotate) the rotated rectangle
deskewed = cv2.warpAffine(blob_img, M, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
# threshold it again
deskewed = cv2.threshold(deskewed, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# get bounding box of contour of deskewed rectangle
cntrs = cv2.findContours(deskewed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
cntr = cntrs[0]
x,y,w,h = cv2.boundingRect(cntr)
# crop to white region
crop = deskewed[y:y+h, x:x+w]
# alternately crop the input
#crop = image[y:y+h, x:x+w]
# save deskewed image
cv2.imwrite("bw2_deskewed_{0}.png".format(i),crop)
print("")
i = i + 1
# save contour and rot rect images
cv2.imwrite("bw2_contours.png",contour_img)
cv2.imwrite("bw2_rotrects.png",rotrect_img)
# display result, though it won't show transparency
cv2.imshow("thresh", thresh)
cv2.imshow("clean", clean)
cv2.imshow("contours", contour_img)
cv2.imshow("rectangles", rotrect_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Contour image:
Rotated rectangles images:
First 3 unrotated images:
Affine warp rotation angles:
13.916877746582031
-42.87890625
18.8118896484375
-44.333797454833984
-38.65980911254883
-37.25965881347656
8.806793212890625
14.931419372558594
-37.405357360839844
-34.99202346801758
35.537681579589844
-35.350345611572266
-42.3245735168457
50.12316131591797
-42.969085693359375
52.750038146972656
45.0

your code is correct for detecting those patches, only a minor mistake is here
if cv2.contourArea(c) < 3000:
continue
reduce 3000 to 100 or below values, because your are giving a condition as contours below 3000 to be neglect

Related

How to crop text from a scanned image using python?

I need to extract the bounding box of text and save it as sub-images of the main image. I am not getting the right code documentation for this task.
Please can anyone provide me code documentation or help links or any python modules which can help to crop text from scanned images.
Below I have attached a scanned image and expected output.
below image scanned copy need to crop text from image.
import cv2
import pytesseract
pytesseract.pytesseract.tesseract_cmd ='C:\\Program Files (x86)\\Tesseract-OCR\\tesseract'
img = cv2.imread("test.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU | cv2.THRESH_BINARY_INV)
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (18, 18))
dilation = cv2.dilate(thresh1, rect_kernel, iterations = 1)
contours, hierarchy = cv2.findContours(dilation, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_NONE)
im2 = img.copy()
file = open("recognized.txt", "w+")
file.write("")
file.close()
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
rect = cv2.rectangle(im2, (x, y), (x + w, y + h), (0, 255, 0), 2)
cropped = im2[y:y + h, x:x + w]
file = open("recognized.txt", "a")
text = pytesseract.image_to_string(cropped)
file.write(text)
file.write("\n")
crop_img = img[y:y+h, x:x+w] # just the region you are interested
file.close
second image expected croped image:
Here is one approach in Python/OpenCV.
Read the input
Get the Canny edges
Get the outer contours of the edges
Filter the contours to remove small extraneous spots
Get the convex hull of the main cluster of edges
Draw the convex hull as white filled on a black background as a mask
Mask to black the outside region of the input
Get the rotated rectangle from the convex hull
From the negative angle and center of the rotated rectangle rectify the orientation using perspective warping
Save the results
Input:
import cv2
import numpy as np
# Read image
img = cv2.imread('receipt.jpg')
hh, ww = img.shape[:2]
# get edges
canny = cv2.Canny(img, 50, 200)
# get contours
contours = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# filter out small regions
cimg = np.zeros_like(canny)
for cntr in contours:
area = cv2.contourArea(cntr)
if area > 20:
cv2.drawContours(cimg, [cntr], 0, 255, 1)
# get convex hull and draw on input
points = np.column_stack(np.where(cimg.transpose() > 0))
hull = cv2.convexHull(points)
himg = img.copy()
cv2.polylines(himg, [hull], True, (0,0,255), 1)
# draw convex hull as filled mask
mask = np.zeros_like(cimg, dtype=np.uint8)
cv2.fillPoly(mask, [hull], 255)
# blacken out input using mask
mimg = img.copy()
mimg = cv2.bitwise_and(mimg, mimg, mask=mask)
# get rotate rectangle
rotrect = cv2.minAreaRect(hull)
(center), (width,height), angle = rotrect
box = cv2.boxPoints(rotrect)
boxpts = np.int0(box)
# draw rotated rectangle on copy of input
rimg = img.copy()
cv2.drawContours(rimg, [boxpts], 0, (0,0,255), 1)
# from https://www.pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/
# the `cv2.minAreaRect` function returns values in the
# range [-90, 0); as the rectangle rotates clockwise the
# returned angle tends to 0 -- in this special case we
# need to add 90 degrees to the angle
if angle < -45:
angle = -(90 + angle)
# otherwise, check width vs height
else:
if width > height:
angle = -(90 + angle)
else:
angle = -angle
# negate the angle to unrotate
neg_angle = -angle
print('unrotation angle:', neg_angle)
print('')
# Get rotation matrix
# center = (width // 2, height // 2)
M = cv2.getRotationMatrix2D(center, neg_angle, scale=1.0)
# unrotate to rectify
result = cv2.warpAffine(mimg, M, (ww, hh), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0))
# save results
cv2.imwrite('receipt_mask.jpg', mask)
cv2.imwrite('receipt_edges.jpg', canny)
cv2.imwrite('receipt_filtered_edges.jpg', cimg)
cv2.imwrite('receipt_hull.jpg', himg)
cv2.imwrite('receipt_rotrect.jpg', rimg)
cv2.imwrite('receipt_masked_result.jpg', result)
cv2.imshow('canny', canny)
cv2.imshow('cimg', cimg)
cv2.imshow('himg', himg)
cv2.imshow('mask', mask)
cv2.imshow('rimg', rimg)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Canny Edges:
Filtered Edges from Contours:
Convex Hull:
Mask:
Rotated Rectangle:
Rectified Result:
In OpenCV you can use cv2.findContours to draw the bounding boxes. See this article which explains how to do that: https://www.geeksforgeeks.org/text-detection-and-extraction-using-opencv-and-ocr/
Then after you have your bounding box locations (your region of interest where text is located, and you want to crop) you can use use slicing to crop the image:
import cv2
img = cv2.imread("lenna.png")
crop_img = img[y:y+h, x:x+w] # just the region you are interested
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)
If you want to extract the text directly, I think you can use tesseract ocr a python package (How to get started: https://pypi.org/project/pytesseract/) . You can also make use of OpenCV built in OCR functions. Read more: https://nanonets.com/blog/ocr-with-tesseract/
from PIL import image
original_image = Image.open(".nameofimage.jpg")
rotate_image = Original_image.rotate(330)
rotate_image.show()
x = 100
y = 80
h = 200
w = 200
cropped_image = rotate_image[y:y+h, x:x+w]
cropped_image.show()

OpenCV Smoother contour

I am trying to solve the issue of creating paths around logos with OpenCV.
I have attached two images, tekst.png and tekst2.png. I have also attached an image comparison.png that shows the wanted result (created manually) and the result I currently am getting with my program.
If anyone has any tips for me, I'd appreciate it a lot!
Short description of wanted solution:
Returns one outer contour that is as close as possible to the logo.
I can use the contour mentioned in the last sentence to scale it up to make padding in between the logo and the contour.
Some kind of algorithm to smooth out the finished contour
The code I currently have:
def current_milli_time():
return round(time.time() * 1000)
def time_calculation_start():
timing.append(current_milli_time())
def time_calculation_end(string):
timing.append(current_milli_time())
print(str(string) + ": ", timing[1] - timing[0], "ms")
timing.clear()
def render_png(filename):
print(filename)
time_calculation_start()
original_image = cv2.imread(str(filename), cv2.IMREAD_UNCHANGED)
copy = original_image.copy() # Saved for imagecreation
time_calculation_end("Setup")
time_calculation_start()
if(original_image.shape[2] == 4):
b,g,r,mask = cv2.split(original_image)
time_calculation_end("Mask")
# Reduce outer turdss
time_calculation_start()
kernel = np.ones((3,3), np.uint8)
dilation = cv2.dilate(mask,kernel,iterations = 2)
dilation = cv2.erode(dilation,kernel,iterations = 1)
time_calculation_end("Dialtion")
time_calculation_start()
gaublur = cv2.GaussianBlur(dilation,(16,16),0)
time_calculation_end("Gaussian blur")
#Find contours
time_calculation_start()
contours, hierarchy = cv2.findContours(gaublur, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
time_calculation_end("Find contours")
print("\tContour layers: ", len(contours))
# Draw contours
time_calculation_start()
cv2.drawContours(copy, contours, -1, (0, 255, 0, 255),1)
time_calculation_end("Draw contours")
print("\n")
cv2.imwrite(str(render_path) + str(filename), copy)
Here is one way to do that in Python/OpenCV. Note that I reduced the size of your input.
Read the input
Extract the BGR channels
Extract the alpha channel
Get the largest contour from the alpha channel to remove small regions
Reduce the number of vertices to make it smoother
Draw a white filled contour on black background
Dilate the contour image
Make an edge image and thicken it
Make a white background image
Invert the dilated contour and blur it for the shadow
Overlay the blurred dilated area on the background
Overlay the dilated white region
Overlay the bgr image
Overlay the edge
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('hjemsokt_small.png', cv2.IMREAD_UNCHANGED)
# extract bgr image
bgr = img[:,:,0:3]
# extract alpha channel
alpha = img[:,:,3]
# get largest contours
contours = cv2.findContours(alpha, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# smooth contour
peri = cv2.arcLength(big_contour, True)
big_contour = cv2.approxPolyDP(big_contour, 0.001 * peri, True)
# draw white filled contour on black background
contour_img = np.zeros_like(alpha)
cv2.drawContours(contour_img, [big_contour], 0, 255, -1)
# apply dilate to connect the white areas in the alpha channel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (40,40))
dilate = cv2.morphologyEx(contour_img, cv2.MORPH_DILATE, kernel)
# make edge outline
edge = cv2.Canny(dilate, 0, 200)
# thicken edge
edge = cv2.GaussianBlur(edge, (0,0), sigmaX=0.3, sigmaY=0.3)
# make background
result = np.full_like(bgr, (255,255,255))
# invert dilated image and blur
dilate_inv = 255 - dilate
dilate_inv = cv2.GaussianBlur(dilate_inv, (0,0), sigmaX=21, sigmaY=21)
dilate_inv = cv2.merge([dilate_inv,dilate_inv,dilate_inv])
# overlay blurred dilated area on background
result[dilate_inv>0] = dilate_inv[dilate_inv>0]
# overlay dilated white region
result[dilate==255] = (255,255,255)
# overlay bgr image
result[contour_img==255] = bgr[contour_img==255]
# overlay edge
result[edge!=0] = (96,96,96)
# save resulting images
cv2.imwrite('hjemsokt_small_alpha.jpg',alpha)
cv2.imwrite('hjemsokt_small_contour.jpg',contour_img)
cv2.imwrite('hjemsokt_small_alpha_dilated.jpg',dilate)
cv2.imwrite('hjemsokt_small_alpha_dilated_inv.jpg',dilate_inv)
cv2.imwrite('hjemsokt_small_alpha_dilated_edge.jpg',edge)
cv2.imwrite('hjemsokt_small_result.jpg',result)
# show thresh and result
cv2.imshow("bgr", bgr)
cv2.imshow("alpha", alpha)
cv2.imshow("contour_img", contour_img)
cv2.imshow("dilate", dilate)
cv2.imshow("dilate_inv", dilate_inv)
cv2.imshow("edge", edge)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Alpha channel:
Contour Image:
Smoothed Dilated contour image:
Inverted contour blurred:
Edge image:
Result:

How to rotate bounding box in open CV and crop it (python)

I have the coordinates of a rectangle (XMIN,YMIN,XMAX & YMAX) on a particular image. I wish to rotate the rectangle at a particular angle and then crop it from the image.
How do I do that??
For example this image.i have got the output bounding box appearing on the left side (plotted it using XMIN,YMIN,XMAX & YMAX). I want to rotate it as per the image on the right side and then crop it.
Can someone provide the way to get this output with a sample code or point me to a link with the explanation
Here is one way in Python/OpenCV.
Read the input
Convert to HSV
Do color thresholding on the green box
Get the outer contour
Print the bounding box
Rotate the image by 10 deg clocwise
Convert that image to HSV
Do color thresholding on the rotated green box
Get the outer contour
Create a black image with the white filled contour
Get the white pixel coordinates
Get the minAreaRect from the coordinates
Get the vertices of the rotated rectangle
Draw the rotated rectangle outline on the rotated image
Input:
import cv2
import numpy as np
from scipy import ndimage
# load image
img = cv2.imread("berry.png")
# convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# threshold using inRange or green
range1 = (20,200,170)
range2 = (80,255,255)
thresh = cv2.inRange(hsv,range1,range2)
# get bounding box coordinates from the one outer contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
x,y,w,h = cv2.boundingRect(contours[0])
print("bounding_box(x,y,w,h):",x,y,w,h)
# rotate image by 10 degree clockwise
rotated = img.copy()
rotated = ndimage.rotate(img, -10, cval=255)
# convert rotated to hsv
hsv_rotated = cv2.cvtColor(rotated, cv2.COLOR_BGR2HSV)
# threshold using inRange or green
range1 = (20,200,170)
range2 = (80,255,255)
thresh_rotated = cv2.inRange(hsv_rotated,range1,range2)
# get bounding box coordinates from the one outer contour
contours = cv2.findContours(thresh_rotated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# draw white filled contour on black background
mask = np.zeros_like(thresh_rotated)
cv2.drawContours(mask, [contours[0]], -1, (255), -1)
# get coordinates of white pixels in mask
coords = np.column_stack(np.where(mask.transpose() > 0))
# get rotated rectangle
rotrect = cv2.minAreaRect(coords)
# rotated rectangle box points
box = np.int0(cv2.boxPoints(rotrect))
print("rotate_box_corners:\n",box)
# draw rotated rectangle on rotated image
result = rotated.copy()
cv2.polylines(result, [box], True, (0,0,255), 1)
# write result to disk
cv2.imwrite("berry_thresh.png", thresh)
cv2.imwrite("berry_rotated.png", rotated)
cv2.imwrite("berry_thresh_rotated.png", thresh_rotated)
cv2.imwrite("berry_mask.png", mask)
cv2.imwrite("berry_rotated_box.png", result)
# display results
cv2.imshow("THRESH", thresh)
cv2.imshow("ROTATED", rotated)
cv2.imshow("THRESH_ROT", thresh_rotated)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold of green lines in input:
Rotated input:
Threshold of green lines in rotated image:
Filled threshold:
Result showing rotated rectangle on rotated image:
Input Bounding Box:
bounding_box(x,y,w,h): 12 13 212 124
Output Vertices:
rotate_box_corners:
[[222 172]
[ 14 136]
[ 35 14]
[243 51]]
You can use a rotation matrix to rotate both the images and the bounding boxes.
Steps:
Generate a rotation matrix
Use OpenCV warpAffine to rotate the
image
Rotate the 4 corners of the bounding box using the same
rotation matrix
Read about getRotationMatrix2D and warpAffine
img_path = '' #The image path
bb = [] #XMIN,YMIN,XMAX,YMAX
angle = 3 #Rotation angle in degrees, +ve is counter-clockwise
bb = np.array(((bb[0],bb[1]),(bb[2],bb[1]),(bb[2],bb[3]),(bb[0],bb[3]))) #Get all 4 coordinates of the box
img = cv2.imread(img_path) #Read the img
center = (img.shape[0]//2,img.shape[1]//2) #Get the center of the image
rotMat = cv2.getRotationMatrix2D(center,angle,1.0) #Get the rotation matrix, its of shape 2x3
img_rotated = cv2.warpAffine(img,rotMat,img.shape[1::-1]) #Rotate the image
bb_rotated = np.vstack((bb.T,np.array((1,1,1,1)))) #Convert the array to [x,y,1] format to dot it with the rotMat
bb_rotated = np.dot(rotMat,bb_rotated).T #Perform Dot product and get back the points in shape of (4,2)
#Plot the original image and bb
plt.imshow(img)
plt.plot(
np.append(bb[:,0],bb[0,0]),
np.append(bb[:,1],bb[0,1])
)
plt.show()
#Plot the rotated image and bb
plt.imshow(img_rotated)
plt.plot(
np.append(bb_rotated[:,0],bb_rotated[0,0]),
np.append(bb_rotated[:,1],bb_rotated[0,1])
)
plt.show()

How to use OpenCV to crop an image based on a certain criteria?

I would like to crop the images like the one below using python's OpenCV library. The area of interest is inside the squiggly lines on the top and bottom, and the lines on the side. The problem is that every image is slightly different. This means that I need some automated way of cropping for the area of interest. I guess the top and the sides would be easy since you could just crop it by 10 pixels or so. But how can I crop out the bottom half of the image where the line is not straight? I have included this example image. The image that follows highlights in pink the area of the image that I am interested in keeping.
Here is one way using Python/OpenCV.
Read input
Get center point (assume it is inside the desired region)
Convert image to grayscale
Floodfill the gray image and set background to black
Get the largest contour and its bounding box
Draw the largest contour as filled on black background as mask
Apply the mask to the input image
Crop the masked input image
Input:
import cv2
import numpy as np
# load image and get dimensions
img = cv2.imread("odd_region.png")
hh, ww, cc = img.shape
# compute center of image (as integer)
wc = ww//2
hc = hh//2
# create grayscale copy of input as basis of mask
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# create zeros mask 2 pixels larger in each dimension
zeros = np.zeros([hh + 2, ww + 2], np.uint8)
# do floodfill at center of image as seed point
ffimg = cv2.floodFill(gray, zeros, (wc,hc), (255), (0), (0), flags=8)[1]
# set rest of ffimg to black
ffimg[ffimg!=255] = 0
# get contours, find largest and its bounding box
contours = cv2.findContours(ffimg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
area_thresh = 0
for cntr in contours:
area = cv2.contourArea(cntr)
if area > area_thresh:
area = area_thresh
outer_contour = cntr
x,y,w,h = cv2.boundingRect(outer_contour)
# draw the filled contour on a black image
mask = np.full([hh,ww,cc], (0,0,0), np.uint8)
cv2.drawContours(mask,[outer_contour],0,(255,255,255),thickness=cv2.FILLED)
# mask the input
masked_img = img.copy()
masked_img[mask == 0] = 0
#masked_img[mask != 0] = img[mask != 0]
# crop the bounding box region of the masked img
result = masked_img[y:y+h, x:x+w]
# draw the contour outline on a copy of result
result_outline = result.copy()
cv2.drawContours(result_outline,[outer_contour],0,(0,0,255),thickness=1,offset=(-x,-y))
# display it
cv2.imshow("img", img)
cv2.imshow("ffimg", ffimg)
cv2.imshow("mask", mask)
cv2.imshow("masked_img", masked_img)
cv2.imshow("result", result)
cv2.imshow("result_outline", result_outline)
cv2.waitKey(0)
cv2.destroyAllWindows()
# write result to disk
cv2.imwrite("odd_region_cropped.png", result)
cv2.imwrite("odd_region_cropped_outline.png", result_outline)
Result:
Result With Contour Drawn:

Insert an image onto a substrate

please help me, I need to insert an image on the substrate.
substrate:
It png, and in the area that is blank with cities, you must insert the image from edge to edge of the frame.
The problem is that I can't find an example of how to insert an image to the known coordinate points of the corners of a given substrate.
Pls help))
My test image
import cv2
import numpy as np
from skimage import io
frame = cv2.cvtColor(io.imread('as.png'), cv2.COLOR_RGB2BGR)
image = cv2.cvtColor(io.imread("Vw5Rc.jpg"), cv2.COLOR_RGB2BGR)
mask = 255 * np.uint8(np.all(frame == [0, 0, 0], axis=2))
contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnt = min(contours, key=cv2.contourArea)
(x, y, w, h) = cv2.boundingRect(cnt)
# Copy appropriately resized image to frame
frame[y:y+h, x:x+w] = cv2.resize(image, (w, h))
cv2.imwrite('frame.png', frame)
I'm trying to find the area where to insert the image by color, the red color of the area I can find, and if there is no color?
The static frame has a constant size.
Here is one way to do it in Python/OpenCV, if I understand what you want.
Read the substrate and trees images
Extract the alpha channel from the substrate
Extract the substrate image without the alpha channel
Use the alpha channel to color the base substrate image white where the alpha channel is black to correct a flaw in the base image
Threshold the alpha channel and invert it
Use morphology to remove the grid lines so that there is only one "outer" contour.
Extract the contour and its bounding box
Resize the trees image to the size of the bounding box.
Use numpy indexing and slicing to multiply the region of the substrate with the resized trees image.
Save the results.
Optionally, display the various images.
Substrate Image:
Trees Image:
import cv2
import numpy as np
# load substrate with alpha channel
substrate = cv2.imread("substrate.png", cv2.IMREAD_UNCHANGED)
hh, ww, cc = substrate.shape
# load colored image
trees = cv2.imread("trees.jpg")
# make img white where alpha is black to merge the alpha channel with the image
alpha = substrate[:,:,3]
img = substrate[:,:,0-2]
img[alpha==0] = 255
img = cv2.merge((img,img,img))
# threshold the img
ret, thresh = cv2.threshold(alpha,0,255,0)
# invert thresh
thresh = 255 - thresh
# make grid lines white in thresh so will get only one contour
kernel = np.ones((9,9), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# find one outer contour
cntrs = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
# get bounding box of contour of white rectangle in thresh
for c in cntrs:
x,y,w,h = cv2.boundingRect(c)
#cv2.rectangle(img, (x,y), (x+w,y+h),(0, 0, 255), 2)
# resize trees
trees = cv2.resize(trees,(w,h),0,0)
# generate result
result = img.copy()
result[y:y+h, x:x+w] = img[y:y+h, x:x+w]/255 * trees
# write result to disk
cv2.imwrite("substrate_over_trees.jpg", result)
cv2.imshow("ALPHA", alpha)
cv2.imshow("IMG", img)
cv2.imshow("THRESH", thresh)
cv2.imshow("TREES", trees)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Note that there is distortion of the trees image, because its aspect ratio does not match the region of the substrate image corresponding to the contour bounding box. This can be changed to maintain the aspect ratio, but then the image will need to be padded to white or some other color to fill the remaining area of the bounding box.

Categories

Resources