Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting a horizontal line in an image

Problem: I'm working with a dataset that contains many images that look something like this:

img2 img1

Now I need all these images to be oriented horizontally or vertically, such that the color palette is either at the bottom or the right side of the image. This can be done by simply rotating the image, but the tricky part is figuring out which images should be rotated and which shouldn't.

What I have tried:

I thought that the best way to do this, is by detecting the white line that separates the the color palette from the image. I decided to rotate all images that have the palette at the bottom such that they have it at the right side.

# yes I am mixing between PIL and opencv (I like the PIL resizing more)
# resize image to be 128 by 128 pixels
img = img.resize((128, 128), PIL.Image.BILINEAR)
img = np.array(img)

# perform edge detection, not sure if these are the best parameters for Canny
edges = cv2.Canny(img, 30, 50, 3, apertureSize=3)

has_line = 0

# take numpy slice of the area where the white line usually is 
# (not always exactly in the same spot which probably has to do with the way I resize my image) 
for line in edges[75:80]:

    # check if most of one of the lines contains white pixels
    counts = np.bincount(line)
    if np.argmax(counts) == 255:
        has_line = True

# rotate if we found such a line
if has_line == True:
    s = np.rot90(s)

An example of it working correctly:

enter image description here

An example of it working incorrectly:

enter image description here

This works maybe on 98% of images but there are some cases where it will rotate images that shouldn't be rotated or not rotate images that should be rotated. Maybe there is an easier way to do this, or maybe a more elaborate way that is more consistent? I could do it manually but I'm dealing with a lot of images. Thanks for any help and/or comments.

Here are some images where my code fails for testing purposes:

enter image description here enter image description here

like image 638
Ahmad Moussa Avatar asked Nov 07 '22 07:11

Ahmad Moussa


1 Answers

You can start by thresholding your image by setting a very high threshold like 250 to take advantage of the property that your lines are white. This will make all the background black. Now create a special horizontal kernel with a shape like (1, 15) and erode your image with it. What this will do is remove the vertical lines from the image and only the horizontal lines will be left.

import cv2
import numpy as np

img = cv2.imread('horizontal2.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)

kernel_hor = np.ones((1, 15), dtype=np.uint8)
erode = cv2.erode(thresh, kernel_hor)

thresheroded

As stated in the question the color palates can only be on the right or the bottom. So we can test to check how many contours does the right region has. For this just divide the image in half and take the right part. Before finding contours dilate the result to fill in any gaps with a normal (3, 3) kernel. Using the cv2.RETR_EXTERNAL find the contours and count how many we have found, if greater than a certain number the image is correct side up and there is no need to rotate.

right = erode[:, erode.shape[1]//2:]

kernel = np.ones((3, 3), dtype=np.uint8)
right = cv2.dilate(right, kernel)

cnts, _ = cv2.findContours(right, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnts) > 3:
    print('No need to rotate')
else:
    print('rotate')
    #ADD YOUR ROTATE CODE HERE

P.S. I tested for all four images you have provided and it worked well. If in case it does not work for any image let me know.

like image 136
Vardan Agarwal Avatar answered Nov 16 '22 11:11

Vardan Agarwal