2

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

image = cv2.imread('filename.jpg')
height, width, channels = image.shape
white = [255, 255, 255]
black = [0, 0, 0]

for x in range(0,width):
    for y in range(0, height):
        channels_xy = image[y, x]
        if all(channels_xy == black):
            image[y, x] = white
cv2.imwrite('result.jpg', image)

The black borders are whitened (well not 100%), but the writing in the image has been affected too
enter image description here

Is there any suggestion to better whiten to black borders without affecting the image content?

singrium
  • 2,746
  • 5
  • 32
  • 45
  • 2
    You could apply a threshold (either adaptive or if you know the background will be pure black, then use that value), followed by `findContours`. Create a mask using `minAreaRect` and then `floodFill` areas around the rectangle. –  Jun 13 '19 at 17:06

2 Answers2

2

This code can help, but it is very slow. And you need to install the shapely package for python.

import cv2
import numpy as np
from pyutils_gph.utils import showIm
import shapely.geometry as shageo
from tqdm import tqdm, trange


img = cv2.imread('test.jpg')
cv2.imshow('src', img)

# get the gray image and do binaryzation
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
gray[gray < 100] = 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)
minx = min(box[:, 0])
maxx = max(box[:, 0])
miny = min(box[:, 1])
maxy = max(box[:, 1])
h, w = img.shape[:2]
ind = np.zeros((h, w), np.bool)

# chech the point is inside the target or not
for i in trange(h):
    for j in range(w):
        if j < minx or j > maxx or i < miny or i > maxy:
            ind[i, j] = True
        else:
            p = shageo.Point(j, i)
            if not p.within(poly):
                ind[i, j] = True

# make outside point to be white
img[ind] = (255, 255, 255)

cv2.imshow('res', img)
cv2.waitKey(0)

the result is like below. result.jpg

ToughMind
  • 987
  • 1
  • 10
  • 28
  • I edited the loops to be `for i in range(h): for j in trange(w): p = shageo.Point(j, i) if not p.within(poly): ind[i, j] = True` And I still get the same results, so I guess the first 'if' is not necessary since the block in your 'else' does the whole job. – singrium Jun 14 '19 at 09:17
  • I posted a new answer based on your answer, but faster. – singrium Jun 19 '19 at 08:41
2

After some research, I ended to a faster solution (based on the accepted answer). 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.
    # the mahotas.polygon.fill_polygon function accepts the coords in the form of pt(y, x) so the coords should be reversed
    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()

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

Here is the result:
enter image description here

singrium
  • 2,746
  • 5
  • 32
  • 45