Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python opencv remove noise in image

I am trying to isolate certain colored lines(the field lines) in a set of hockey images. I have applied a Hue Lightness Saturation(HLS) color space filter and have managed to pass all the components of the original image which are in the specified HLS range. However, some parts of the image are also being passed through because they satisfy the color range, for example, parts of the crowd and parts of the playing field. However, I want to isolate only the line which is required. How can I do this?

NOTE: I have separate programs for blue and yellow because they require different HLS ranges. In some images, there are more than one line. Also, as can be seen in the 2nd image I have put, the lines may be slightly curved. In the 2nd image, it would be enough if I can get the straight part only.

I have tried various image transformations and morphological operations with no luck. I have also experimented quite a lot with the HLS ranges and have set them so that they produce the best result on a set of images which I have, but I still don't get a satisfactory result.

Original images:

enter image description here enter image description here

Code:

import cv2
import numpy as np

frame = cv2.imread('hockey4.jpg')
width=900
height=600
frame = cv2.resize(frame,(width,height))
# Convert BGR to HLS
hls = cv2.cvtColor(frame, cv2.COLOR_BGR2HLS)

#HLS ranges for blue
#lower array defines the lower limit and upper array defines the upper limit of the range
#The mask is a binary image where the output is white if the corresponding pixel in the input image is between the range specified by upper and lower limits

#blue
lower = np.array([90,90,19])  #90,90,19
upper = np.array([130,190,100]) #130,190,100
mask = cv2.inRange(hls, lower, upper)

# Bitwise-AND mask and original image
res = cv2.bitwise_and(frame,frame, mask= mask)
like image 442
Sibi Avatar asked Jun 09 '17 20:06

Sibi


1 Answers

You can get better results from range thresholding than you're currently getting. Additionally, try using the morphological operations like opening and closing after thresholding to remove spurious bits and add in any pieces you didn't mean to remove.

These were the results I was able to get through thresholding with cv2.inRange() and applying opening and closing with cv2.morphologyEx(): image 1 yellow, image 1 blue, image 2 yellow, image 2 blue

Here was my code for the first image:

import cv2
import numpy as np

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

# crop out the top of the image where the scores are
h, w = img.shape[:2]
score_h = int(h/8)
img = img[score_h:h, :]
h, w = img.shape[:2]

# blur for better thresholding
blur = cv2.GaussianBlur(img, (5,5), 1)

# threshold in HSV space
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)

# threshold for yellow
lb_y = np.array([11, 0, 153])
ub_y = np.array([52, 255, 255])
bin_y = cv2.inRange(hsv, lb_y, ub_y)
# cv2.imshow("thresh yellow", bin_y)
# cv2.waitKey(0)

# open to remove spurious yellow bits
open_kern = np.ones((3,3), dtype=np.uint8)
bin_y = cv2.morphologyEx(bin_y, cv2.MORPH_OPEN, open_kern, iterations=2)
# cv2.imshow("opened yellow", bin_y)
# cv2.waitKey(0)

# threshold for blue
lb_b = np.array([113, 41, 191])
ub_b = np.array([119, 76, 232])
bin_b = cv2.inRange(hsv, lb_b, ub_b)
# cv2.imshow("thresh blue", bin_b)
# cv2.waitKey(0)

# open to remove spurious blue bits
kern = np.ones((3,3), dtype=np.uint8)
bin_b = cv2.morphologyEx(bin_b, cv2.MORPH_OPEN, kern, iterations=2)
# cv2.imshow("opened blue", bin_b)
# cv2.waitKey(0)

# combine to show yellow detection
rip_y = img.copy()
rip_y[bin_y==0] = 0
mark_y = cv2.addWeighted(img, .4, rip_y, .6, 1)
cv2.imshow("marked yellow", mark_y)
cv2.waitKey(0)
# cv2.imwrite('0-y.jpg',mark_y)

# combine to show blue detection
rip_b = img.copy()
rip_b[bin_b==0] = 0
mark_b = cv2.addWeighted(img, .4, rip_b, .6, 1)
cv2.imshow("marked blue", mark_b)
cv2.waitKey(0)
# cv2.imwrite('0-b.jpg',mark_b)

And for the second:

import cv2
import numpy as np

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

# crop out the top of the image where the scores are
h, w = img.shape[:2]
score_h = int(h/10)
img = img[score_h:h, :]
h, w = img.shape[:2]

# blur for better thresholding
blur = cv2.GaussianBlur(img, (5,5), 1)

# threshold in HSV space
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)

# threshold for yellow
lb_y = np.array([14, 79, 140])
ub_y = np.array([25, 255, 217])
bin_y = cv2.inRange(hsv, lb_y, ub_y)
# cv2.imshow("thresh yellow", bin_y)
# cv2.waitKey(0)

# open to remove spurious yellow bits
open_kern = np.ones((3,3), dtype=np.uint8)
bin_y = cv2.morphologyEx(bin_y, cv2.MORPH_OPEN, open_kern, iterations=2)
# cv2.imshow("opened yellow", bin_y)
# cv2.waitKey(0)

# threshold for blue
lb_b = np.array([113, 50, 150])
ub_b = np.array([135, 255, 255])
bin_b = cv2.inRange(hsv, lb_b, ub_b)
# cv2.imshow("thresh blue", bin_b)
# cv2.waitKey(0)

# close to fill in blue lines
kern = np.ones((3,3), dtype=np.uint8)
bin_b = cv2.morphologyEx(bin_b, cv2.MORPH_CLOSE, kern, iterations=2)
# cv2.imshow("closed blue", bin_b)
# cv2.waitKey(0)

# open to remove spurious lines
bin_b = cv2.morphologyEx(bin_b, cv2.MORPH_OPEN, kern, iterations=2)
# cv2.imshow("opened blue", bin_b)
# cv2.waitKey(0)

# combine to show yellow detection
rip_y = img.copy()
rip_y[bin_y==0] = 0
mark_y = cv2.addWeighted(img, .4, rip_y, .6, 1)
cv2.imshow("marked yellow", mark_y)
cv2.waitKey(0)
cv2.imwrite('1-y.jpg',mark_y)

# combine to show blue detection
rip_b = img.copy()
rip_b[bin_b==0] = 0
mark_b = cv2.addWeighted(img, .4, rip_b, .6, 1)
cv2.imshow("marked blue", mark_b)
cv2.waitKey(0)
cv2.imwrite('1-b.jpg',mark_b)

The first and the second are identical aside from their threshold values, and the second image I am applying a closing operation on the blue (not done for the other three thresholds) to close up the gaps in the line a bit.


It's not clear whether you're looking to do this automatically or manually for a few images. If you're looking to do it automatically, my first go-to would be to create a set of threshold values that work relatively well for all the images, and be harsh with morphological operations. This will likely thin out the lines a good bit, but then you could use numpy operations to fit a curve to the lines, and then widen it to achieve roughly the same line.

If you're going to be doing it manually, then the easiest bet is to play around with threshold values for each image. I've just recently created a tool that gives you sliders for changing the threshold range and previewing it on the fly. Check it out on GitHub if you're interested in trying it out. It will output the binary thresholded image, the colorspace you're thresholding in, and the lower and upper bounds that were used for thresholding.

like image 105
alkasm Avatar answered Nov 03 '22 01:11

alkasm