Fill holes holes/area in a binary image using OpenCV - python

I have below preprocessed rice image. I want to fill the rice with black color and then perform the inverse operation to find contours. I am trying to use Erosion/Dilation operation but not working. Below is the code snippet I am using.
First I used shadow removal algorithm then used adaptive thresholding which gives the Input image. Now, I want to change the Input image to the output image.
Original Image:
Input Image:
Required Output Image:
Code Snippet:
oposite = cv2.bitwise_not(img)
#Erosion
kernel = np.ones((3,3),np.uint8)
erosion = cv2.dilate(des,kernel,iterations = 1)
erosion = cv2.bitwise_not(erosion)
im_out = oposite + erosion
cv2.imshow("output", im_out)
cv2.waitKey(0)

You can create an HSV mask by using the cv2.COLOR_BGR2HSV flag in the cv2.cvtColor() method, and by using the cv2.inRange() method. I basically changed the maximum saturation value from 255 to 100:
import cv2
import numpy as np
img = cv2.imread("rice.jpg")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 0])
upper = np.array([179, 100, 255])
mask = cv2.inRange(img_hsv, lower, upper)
cv2.imshow("Mask", mask)
cv2.waitKey(0)
Output:
If you're looking to make the grains thinner, you can turn down the maximum hue value, from 179 to, say,111, along with the maximum saturation value at 100:
import cv2
import numpy as np
img = cv2.imread("rice.jpg")
img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 0])
upper = np.array([111, 100, 255])
mask = cv2.inRange(img_hsv, lower, upper)
cv2.imshow("Mask", mask)
cv2.waitKey(0)
Output:

Related

HSV space for multicolor object

I would like to detect this gate below, ideally the entire gate. I have played around for hours with a trackbar script but I am just not finding the right color space. I found other threads that just track yellow and not even that is working.. This is my code:
def track():
cap = cv2.VideoCapture('../files/sub/gate_mission1.mp4')
while True:
_, frame = cap.read()
cv2.imshow('img', frame)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower = np.array([20, 93, 0])
upper = np.array([45, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(frame, contours, -1, (0, 255, 0), 2)
Maybe there is a way to just remove all the blue/greenish too im not sure? What are my options here?
This seems to work for me by thresholding in LAB colorspace in Python/OpenCV. According to Wikipedia at https://en.wikipedia.org/wiki/CIELAB_color_space "The a* axis is relative to the green–red opponent colors, with negative values toward green and positive values toward red." So we ought to get reasonably good separation for your green and reddish colors.
Input:
import cv2
import numpy as np
# load images
img = cv2.imread('gate.jpg')
# convert to LAB
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# set black ranges
lower = (130,105,100)
upper = (170,170,160)
# threshold on black
result = cv2.inRange(lab, lower, upper)
# save output
cv2.imwrite('gate_thresh.jpg', result)
# display results
cv2.imshow('thresh',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold Image

Removing White Text with Black Borders From Image

I am trying to remove text from images that has a black border with white fill. Take the image below as an example.
I have tried a few options utilizing opencv and skimage inpaint
import cv2
from skimage.restoration import inpaint
img = cv2.imread('Documents/test_image.png')
mask = cv2.threshold(img, 210, 255, cv2.THRESH_BINARY)[1][:,:,0]
dst = cv2.inpaint(img, mask, 7, cv2.INPAINT_TELEA)
image_result = inpaint.inpaint_biharmonic(img, mask,
multichannel=True)
cv2.imshow('image',img)
cv2.imshow('mask',mask)
cv2.imshow('dst',dst)
cv2.imshow('image_result',image_result)
cv2.waitKey(0)
It seems like the inpainting is just trying to fill with black as that is what it is identifying as being around the areas of interest. What I would like to do is remove the white text and black borders completely, or secondarily try to fill the white with more information from surrounding colors than just the black.
Here is the best solution I could come up with, still open to others with more experience showing me a better way if anyone has an idea.
mask = cv2.threshold(img, 245, 255, cv2.THRESH_BINARY)[1][:,:,0]
new_mask = cv2.dilate(mask, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (10,10)))
dst = cv2.inpaint(img, new_mask, 7, cv2.INPAINT_TELEA)
Here are two inpainting methods in Python/OpenCV. Note that I use the saturation channel to create the threshold, since white and black have zero saturation, in principle.
Input:
import cv2
import numpy as np
# read input
img = cv2.imread('white_black_text.png')
# convert to hsv and extract saturation
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
sat = hsv[:,:,1]
# threshold and invert
thresh = cv2.threshold(sat, 10, 255, cv2.THRESH_BINARY)[1]
thresh = 255 - thresh
# apply morphology dilate
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15,15))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)
# do inpainting
result1 = cv2.inpaint(img,thresh,11,cv2.INPAINT_TELEA)
result2 = cv2.inpaint(img,thresh,11,cv2.INPAINT_NS)
# save results
cv2.imwrite('white_black_text_threshold.png', thresh)
cv2.imwrite('white_black_text_inpainted1.png', result1)
cv2.imwrite('white_black_text_inpainted2.png', result1)
# show results
cv2.imshow('thresh',thresh)
cv2.imshow('result1',result1)
cv2.imshow('result2',result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold and morphology cleaned result:
Result 1 (Telea):
Result 2 (Navier Stokes):

Remove Mask from Image OpenCV Python

I may be overshooting a bit but I'm trying to use a mask generated from my image and subtract it from the main image. I'm quite open to instead extracting the characters but am not sure how to collect the entire blue sample, I haven't that correct balance yet.
The page here demonstrates the inverse of what I'm trying to achieve.
Base image
The mask utilizing hsv bounds then inverting it to show it better
Darkening it
I wish to now take that mask and remove it from the main image.
import cv2
import numpy as np
import random as rng
from PIL import Image
from PIL import ImageOps
from utils import helper
image_name = 'capt13.jpg'
img = cv2.imread(image_name)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,120,70])
upper_red = np.array([10,255,255])
lower_mask = cv2.inRange(hsv, lower_red, upper_red)
lower_red = np.array([160,120,70])
upper_red = np.array([180,255,255])
upper_mask = cv2.inRange(hsv, lower_red, upper_red)
'''
lower_blue = np.array([80,40,30])
upper_blue = np.array([140,255,255])
lower_mask = cv2.inRange(hsv, lower_blue, upper_blue)
lower_blue = np.array([240,220,200])
upper_blue = np.array([360,255,255])
upper_mask = cv2.inRange(hsv, lower_blue, upper_blue)
'''
mask = lower_mask + upper_mask
res_lines = cv2.bitwise_and(img,img, mask= mask)
# Keep the inverted
image = Image.fromarray(res_lines)
image.save('res.png')
inverted = ImageOps.invert(image)
inverted = inverted.convert('L')
inverted.save('inverted.png')
binary = np.array(inverted)
for row in range(len(binary)):
for col in range(len(binary[row])):
if binary[row][col] != 255:
binary[row][col] = 0
binary_image = Image.fromarray(binary)
binary_image.save('binary.png')
Extracting the Blue (As stated above that I'm open to a better solution for this)
The mask utilizing hsv bounds then inverted it
Darkening it
Straight subtraction works, provided both images are the same size:
im = cv2.imread("image.png")
mask = cv2.imread("mask.png")
diff_im = im - im2
Alternatively, you can use OpenCV's built in subtract, which does an element-wise subtraction:
diff_im = cv2.subtract(im, im2)
As a final thought, you should also try absdiff, as it will convert negative results to zeroes, which may be what you want.
diff_im = cv2.absdiff(im, im2)

Reading lower and upper threshold arrays when used with inRange

I was reading on how to filter colors using OpenCV and came across the following snippet.
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([0,160,50])
upper_red = np.array([255,255,180])
mask = cv2.inRange(hsv, lower_red, upper_red)
res = cv2.bitwise_and(img,img, mask= mask)
What does each value in lower_red mean? Does it denote lower and upper limits of H,S,V sequentially? Should it be read as minimum value of H as 0 and maximum value of H as 255?
I want to filter red color.
You are well on your way. I've added some code that shows a solution to your problem - combining two HSV color ranges in one mask.
Result:
Code:
import numpy as np
import cv2
# load image
img = cv2.imread("HSV.JPG")
# convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Create first mask
lower_red = np.array([0,150,50])
upper_red = np.array([5,255,255])
# Threshold the HSV image to get only green colors
mask = cv2.inRange(hsv, lower_red, upper_red)
# apply mask to original image
res = cv2.bitwise_and(img,img, mask= mask)
#show image
cv2.imshow("Mask1", res)
# Create second mask
lower_red2 = np.array([175,150,50])
upper_red2 = np.array([179,255,255])
# Threshold the HSV image to get only green colors
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
# apply mask to original image
res2 = cv2.bitwise_and(img,img, mask= mask2)
#show image
cv2.imshow("Mask2", res2)
#combine masks
final_mask = cv2.bitwise_or(mask, mask2)
# apply mask to original image
result = cv2.bitwise_and(img,img, mask= final_mask)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Note: in the result image I show the results if the separate masks were applied to the original image. Of course you really only need the masks, which are black and white.

opencv python3.6 OCR

I do not know openCV but I would like to know
Area extraction
If the white area is reduced by hsv, the text is broken and can not be recognised. If there is a white area, THRESH_BINARY_INV is converted as shown above.
I would appreciate your help.
I set out to solve this problem.
I do not know if this is being interpreted properly.
My code will be attached below
import cv2
import numpy as np
def tracking():
frame = cv2.imread('test4.png')
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 0])
upper = np.array([255, 255, 240])
mask = cv2.inRange(hsv, lower, upper)
res2 = cv2.bitwise_and(frame, frame, mask=mask)
cv2.imshow('asdasd2', res2)
_, edge2 = cv2.threshold(res2, 100, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('asdasd2', edge2)
cv2.imshow('original', frame)
cv2.imshow('finish', res2)
cv2.waitKey(0)
cv2.destroyAllWindows()
tracking()

Categories

Resources