I have the following code to binarize an image using opencv, I can binarize and save, but before saving I wanted to remove all the white pixels from the edge of the image until I find the line of black pixels as in the image. It's possible? and how to do?
import cv2
import numpy as np
from tkinter import filedialog
image1 = filedialog.askopenfilename()
image1 = cv2.imread(image1)
img = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
ret, bin = cv2.threshold(img, 120, 255, cv2.THRESH_TOZERO)
cv2.imwrite('bin.png', bin)
cv2.imshow('Imagem binarizada', bin)
cv2.waitKey(0)
cv2.destroyAllWindows()
CodePudding user response:
There are multiple ways to crop the shape. I'm using a direct approach, based on the only sample you posted. Basically you need to find the target external contour (i.e., the square) on an inverted binary image.
There are multiple contours though, but you can filter them based on two simple metrics: aspect ratio (the relation between width and height - the square should be close to 1.0
) and area (the square is the biggest blob on your image). Once you have the shape, you can get use its bounding box data to crop it using numpy
slicing:
# Imports:
import numpy as np
import cv2
# Image path
path = "D://opencvImages//"
fileName = "square.png"
# Reading an image in default mode:
inputImage = cv2.imread(path fileName)
# Deep Copy:
inputImageCopy = inputImage.copy()
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# (Otsu) Threshold:
thresh, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV cv2.THRESH_OTSU)
This bit just reads the image and converts it from BGR
to inverted binary, this is the result:
Alright, next, find all the contours on this binary image and apply the properties filter:
# Find the EXTERNAL contours on the binary image:
contours, _ = cv2.findContours(binaryImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Look for the outer bounding boxes (no children):
for _, c in enumerate(contours):
# Get the bounding rectangle:
boundRect = cv2.boundingRect(c)
# Draw the rectangle on the input image:
# Get the dimensions of the bounding rect:
rectX = int(boundRect[0])
rectY = int(boundRect[1])
rectWidth = int(boundRect[2])
rectHeight = int(boundRect[3])
# Compute contour area:
contourArea = rectHeight * rectWidth
# Compute aspect ratio:
referenceRatio = 1.0
contourRatio = rectWidth/rectHeight
epsilon = 0.1
ratioDifference = abs(referenceRatio - contourRatio)
print((ratioDifference, contourArea))
# Apply contour filter:
if ratioDifference <= epsilon:
minArea = 1000
if contourArea > minArea:
# Crop contour:
croppedShape = inputImageCopy[rectY:rectY rectHeight,rectX:rectX rectWidth]
cv2.imshow("Shape Crop", croppedShape)
cv2.waitKey(0)
# (Optional) Set color and draw:
color = (0, 0, 255)
cv2.rectangle(inputImageCopy, (int(rectX), int(rectY)),
(int(rectX rectWidth), int(rectY rectHeight)), color, 2)
# (Optional) Show image:
cv2.imshow("Bounding Rectangle", inputImageCopy)
cv2.waitKey(0)
The most relevant parts of the last snippet are the contour selection method and properties filter. I'm looking for external contours (no nested contours) so the "retrieval" method is set to cv2.RETR_EXTERNAL
. Next, the properties filter. I get the bounding box of every found contour and compute its area and aspect ratio. Both values should be above a minimum threshold. If the contour satisfies both conditions, crop the shape using its bounding box.
This is the filtered contour:
This is the cropped shape: