2
\$\begingroup\$

I have this image:

enter image description here

I want to whiten the black contours (borders) around it without affecting the image content. Here is the code I used:

import cv2
import numpy as np
import shapely.geometry as shageo


img = cv2.imread('filename.jpg')

# get the gray image and do binaryzation
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray[gray < 20] = 0
gray[gray > 0] = 255

# get the largest boundry of the binary image to locate the target
contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int0(box)

poly = shageo.Polygon(box)
h, w = img.shape[:2]
ind = np.zeros((h, w), np.bool)

# check if the point is inside the target or not
for i in range(h):
    for j in range(w):
        p = shageo.Point(j, i)
        if not p.within(poly):
            ind[i, j] = True

# whiten the outside points
img[ind] = (255, 255, 255)
cv2.imwrite('result.jpg', img)

Here is the result: enter image description here

The code works fine, but it's very slow because of the for loops.

Any suggestions how to avoid the for loops or to make them faster?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

After some research, I ended to a better and faster solution. Here is the code:

# import packages
import numpy
import mahotas.polygon
import shapely.geometry as shageo
import cv2
import numpy as np

def get_mask(dims, pts):
    # create a numpy array of zeros with the same dimensions of the image 
    canvas = numpy.zeros((dims[0], dims[1]), dtype=int)
    # the points coords in the form of pt(y, x)

    # fill the polygon with ones.
    mahotas.polygon.fill_polygon(pts, canvas)
    return canvas


def find_polygon(img):
    # get the gray image and do binaryzation
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    gray[gray < 20] = 0
    gray[gray > 0] = 255

    # get the largest boundry of the binary image to locate the target
    contours, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    rect = cv2.minAreaRect(contours[0])
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    poly = shageo.Polygon(box)
    # return the polygone coords in a list
    return list(poly.exterior.coords)


def main():
    img = cv2.imread('filename.jpg')
    # get the coords of the polygon containing (around) the image.
    coords = find_polygon(img)
    poly_coords = []
    # the coords are floats and sometimes are negaive (-1), so transform them into positive ints.
    for element in coords:
        poly_coords.append(tuple(map(int, map(abs, reversed(element)))))

    mask = get_mask(img.shape, poly_coords)
    # convert the mask into array of 0 and 1.
    binary_mask = np.logical_not(mask).astype(int)
    # reshape the array to be similar to the image dimenstions
    binary_mask = binary_mask.reshape(img.shape[0], img.shape[1], -1)
    # sum the binary mask with the image
    cv2.imwrite('res.jpg', img + binary_mask * 255)


main()

I am sure this code can be optimized more, any suggestions are welcome.

Credits:
1- Drawing polygons in numpy arrays
2- Whiten black contours around a skewed image opencv

Here is the result:
enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.