'OpenCV detect towel margins

What would be the best method (using python and OpenCV) of finding the lowest corner ( not edge ) of towel in this images? Also, the towel color can be different, but the background color will be always the same.

towel

And I need this corner ( the lowest "real" towel corner ): expected result



Solution 1:[1]

Since you have approximately two distinct colors in the image (one each for foreground and background) you could convert your image to HSV color space and visualize each of the individual channels.

Code:

path = r'C:\Users\Desktop'
filename = 'towel.jpg'

img = cv2.imread(os.path.join(path, filename))    
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)     #--- convert to HSV
cv2.imshow('hsv.jpg', hsv)
h = hsv[:,:,0]
cv2.imshow('h.jpg', h)                         #--- visualize the hue channel

enter image description here

ret, thresh = cv2.threshold(h, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY_INV)
cv2.imshow('thresh1', thresh)                  #--- apply Otsu threshold on hue channel

enter image description here

Notice that white blob in the center of the towel, it has to be removed. For that purpose I have used morphological opening.

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(25, 25))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
cv2.imshow('fin', cv2.bitwise_not(opening))

enter image description here

EDIT

OpenCV provides the functionality to find top, bottom, right-most and left-most corners for a given contour. I obtained the contour of the final resulting image and found the four extreme points.

Code:

im2, contours, hierarchy = cv2.findContours(cv2.bitwise_not(opening), cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)                #--- finding contours
cnt = contours[0]                                 #--- since there is only one contour present

leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

print('The extreme points are leftmost: {}, rightmost: {}, topmost: {} and bottommost: {}'.format(leftmost, rightmost, topmost, bottommost))

The extreme points are leftmost: (32, 336), rightmost: (807, 439), topmost: (459, 12) and bottommost: (699, 743)

I have also marked the extreme points on a copy of the original image:

img2 = img.copy()
cv2.circle(img2, leftmost, 5, (0, 255, 255), -1)    #-- leftmost
cv2.circle(img2, rightmost, 5, (0, 255, 255), -1)    #-- rightmost
cv2.circle(img2, topmost, 5, (0, 255, 255), -1)    #-- topmost
cv2.circle(img2, bottommost, 5, (0, 255, 255), -1)    #-- bottommost

enter image description here

Solution 2:[2]

what you are looking for is edge detection algorithm.

Use the below code from the following Link

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('Your towel image.jpg',0)
edges = cv.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

Solution 3:[3]

You can use the canny edge detector. For to detect the corner of the image.I have given you the link of 2 sources below.

Source1

Source2

Code:

import cv2
import matplotlib.pyplot as plt

im = cv2.imread('boat.png')
edges = cv2.Canny(im,25,255,L2gradient=False)
plt.imshow(edges,cmap='gray')
plt.show()

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2 InAFlash
Solution 3 Muhammad Usman