I want to get the outline of an object in an image with OpenCV and cut it out. When I examine it, I can only find patterns that are cut out with circumscribing rectangles and squares, even with various contours. I want to cut out only the object as it is.
The only way to crop an image with OpenCV + python is to specify the pixel range of the original image, such as img [10: 100, 20: 100].
So, first cut out with the circumscribing rectangle, then make a canvas with the circumscribing rectangle size, Data is organized in the acquired contour information, and the maximum value and the minimum value are acquired for each row and made into an array. On the transparent canvas created earlier, line by line, if you go between the minimum and maximum, get it from the original image and plot it ..., It seems that we should repeat it line by line.
Go!
import cv2
import numpy as np
from pprint import pprint
from copy import deepcopy
import sys
import os
#Grayscale conversion of input image> Binarization, return the binarized image
def getBinary(im):
im_gray = cv2.cvtColor(im , cv2.COLOR_BGR2GRAY)
return cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
#Get the circumscribing rectangle
def getBoundingRectAngle(contour):
#Since the array structure is multiple, make it a simple array once(point [x y])
contour = list(map(lambda point: point[0], contour))
x_list = [ point[0] for point in contour ] # width
y_list = [ point[1] for point in contour ] # height
return [min(x_list), max(x_list), min(y_list), max(y_list)]
#Cut out
def getPartImageByRect(img, rect):
#[y-axis, x-axis]
return img[ rect[2]:rect[3], rect[0]:rect[1],]
def getCropData(contour, main_axis=0):
target_axis = 1 if main_axis == 0 else 0
#axis = 0 = x
#axis = 1 = y
contour = list(map(lambda i: i[0], contour))
axis_point_list = set([ point[main_axis] for point in contour ]) # unique
arr = []
for val in axis_point_list:
#Get an array of x with a specific value of y from the contour array
target_axis_point_list = list(filter(lambda i: i[main_axis] == val, contour))
tmp_list = [ i[target_axis] for i in target_axis_point_list ] #
arr.append( [ val, min(tmp_list), max(tmp_list)] )
return arr
##Get the x-coordinate range along the y-axis
def doCropY(input_im, points, rect) :
height = rect[3]-rect[2]
width = rect[1]-rect[0]
left = rect[0]
top = rect[2]
output_im = np.zeros((height, width, 4), np.uint8)
for point in points :
for x in range(0, width) :
#input image coordinates
in_y = point[0]
in_x = x + left
in_x_min = point[1]
in_x_max = point[2]
#output image coordinates
out_y = point[0] - top
out_x = x
out_x_min = point[1] - left
out_x_max = point[2] - left
#Copy from the original image to the new image if it is within the maximum and minimum range of the x-axis
if( out_x_min < x and x < out_x_max):
output_im[out_y : out_y + 1, out_x : out_x + 1, ] = input_im[in_y : in_y + 1, in_x : in_x + 1, ]
return output_im
##For the image that has been extracted once
##Get the y-coordinate range along the x-axis
def doCropX(im, points, rect):
height = rect[3]-rect[2]
width = rect[1]-rect[0]
left = rect[0]
top = rect[2]
for point in points :
for y in range(0, height) :
#input image coordinates
y = y
x = point[0] - left
y_min = point[1] - top
y_max = point[2] - top
#Copy from the original image to the new image if it is within the maximum and minimum range of the y-axis
if( y < y_min or y_max < y):
im[y:y+1, x:x+1,] = [0,0,0,0] #Transparent
return im
##########################
# main
##########################
outdir = './out/'
tempdir = './temporary/'
#Get image path from argument
args = sys.argv
if(len(args) <= 1):
print("need arg 1. input file path.")
sys.exit()
src_file = args[1]
root, extention = os.path.splitext(src_file)
#Image loading
im_src = cv2.imread(src_file, -1) #Alpha channel included
# cv2.imwrite(tempdir + "original" + extention, im_src)
#Get the contour as binary
im_bin = getBinary(im_src)
#contours = cv2.findContours(im_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
contours = cv2.findContours(im_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[0] #Get all paths
for i, cnt in enumerate(contours):
#Get the circumscribing rectangle
rect = getBoundingRectAngle(cnt)
#Cut in the range of the left and right edges of x in the y coordinate
crop_data = getCropData(cnt, 1) #x-axis reference
im_out = doCropY(im_src, crop_data, rect)
# # #Makes the outside of the range from the top to the bottom of y transparent for each x coordinate
crop_data = getCropData(cnt, 0) #x-axis reference
im_out = doCropX(im_out, crop_data, rect)
cv2.imwrite(outdir + "out" + str(i) + extention, im_out)
python3 crop.py filename
Intermediate file output to temporary / Output the image file cut out to out /
It may be confusing that the background of the circumscribing rectangle is transparent ...
Recommended Posts