Home > Back-end >  Remove white part of binarized image
Remove white part of binarized image

Time:09-19

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?

Square with white background

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:

  • Related