python - opencv morphologyEx remove specific color - python

After remove captcha's background.
The image remain digits and noise.
Noise line is all in one color : RGB(127,127,127)
And then using morphology method.
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
self.im = cv2.morphologyEx(self.im, cv2.MORPH_CLOSE, kernel)
Some part of digit will be remove.
How to use morphologyEx() remove only color in RGB(127,127,127) ?

In order to eliminate color within a particular range you have to use cv2.inRange() function.
Here is the code:
lower = np.array([126,126,126]) #-- Lower range --
upper = np.array([127,127,127]) #-- Upper range --
mask = cv2.inRange(img, lower, upper)
res = cv2.bitwise_and(img, img, mask= mask) #-- Contains pixels having the gray color--
cv2.imshow('Result',res)
This is what I got for the two images you have:
Image 1:
Image 2:
You carry on from here.

COLOR RANGE
color_dict_HSV = {'black': [[180, 255, 30], [0, 0, 0]],
'white': [[180, 18, 255], [0, 0, 231]],
'red1': [[180, 255, 255], [159, 50, 70]],
'red2': [[9, 255, 255], [0, 50, 70]],
'green': [[89, 255, 255], [36, 50, 70]],
'blue': [[128, 255, 255], [90, 50, 70]],
'yellow': [[35, 255, 255], [25, 50, 70]],
'purple': [[158, 255, 255], [129, 50, 70]],
'orange': [[24, 255, 255], [10, 50, 70]],
'gray': [[180, 18, 230], [0, 0, 40]]}
CREDITS:
Ali Hashemian
HOW TO REMOVE A COLOR FROM YOUR IMAGE USING OPENCV
Since most of you would like to do that, i.e. in my case the task was to remove blue color from the image, I used the following code, to remove blue ink stamps and, blue tick marks from my image in order for proper OCR using Tesseract.
[COLOR REMOVAL] CODE
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# image path:
#path = "D://opencvImages//"
#fileName = "out.jpg"
# Reading an image in default mode:
inputImage = cv2.imread('0.jpg')
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Convert the BGR image to HSV:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
# Create the HSV range for the blue ink:
# [128, 255, 255], [90, 50, 70]
lowerValues = np.array([90, 50, 70])
upperValues = np.array([128, 255, 255])
# Get binary mask of the blue ink:
bluepenMask = cv2.inRange(hsvImage, lowerValues, upperValues)
# Use a little bit of morphology to clean the mask:
# Set kernel (structuring element) size:
kernelSize = 3
# Set morph operation iterations:
opIterations = 1
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
bluepenMask = cv2.morphologyEx(bluepenMask, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101)
# Add the white mask to the grayscale image:
colorMask = cv2.add(grayscaleImage, bluepenMask)
_, binaryImage = cv2.threshold(colorMask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imwrite('bwimage.jpg',binaryImage)
thresh, im_bw = cv2.threshold(binaryImage, 210, 230, cv2.THRESH_BINARY)
kernel = np.ones((1, 1), np.uint8)
imgfinal = cv2.dilate(im_bw, kernel=kernel, iterations=1)
cv2.imshow(imgfinal)
BEFORE [Original Image]
Blue Mark Extraction
Final Image
Here you can see that all of the tick marks are almost, removed the reason is that because there is always room for improvement, but this, as it seems, is the best we can get because even removing these little marks is not going to have a profound effect on the OCR using Tesseract.
HOPE THAT HELPS!

Here is my solution.
Your answer is obvious better than my.
def mop_close(self):
def morphological(operator=min):
height, width, _ = self.im.shape
# create empty image
out_im = np.zeros((height,width,3), np.uint8)
out_im.fill(255) # fill with white
for y in range(height):
for x in range(width):
try:
if self.im[y,x][0] ==127 and self.im[y,x][1] ==127 and self.im[y,x][2] ==127:
nlst = neighbours(self.im, y, x)
out_im[y, x] = operator(nlst,key = lambda x:np.mean(x))
else:
out_im[y,x] = self.im[y,x]
except Exception as e:
print(e)
return out_im
def neighbours(pix,y, x):
nlst = []
# search pixels around im[y,x] add them to nlst
for yy in range(y-1,y+1):
for xx in range(x-1,x+1):
try:
nlst.append(pix[yy, xx])
except:
pass
return np.array(nlst)
def erosion(im):
return morphological(min)
def dilation(im):
return morphological(max)
self.im = dilation(self.im)
self.im = erosion(self.im)
final result:

Related

Image supposed to be black and white has intermediate values and can not be used as a binary mask

Assuming we have this image that seems to be binary:
However, when reading with opencv via:
img = cv2.imread("myImage.png", cv2.IMREAD_UNCHANGED)
We notice that its values vary:
e.g
array([ 0, 16, 32, 48, 64, 79, 80, 95, 96, 111, 112, 127, 128,
143, 144, 159, 160, 175, 176, 191, 192, 207, 208, 223, 224, 239,
240, 255], dtype=uint8)
Is there a safe way to convert it to a simple binary range ([0,1])?
There is no restriction for package to be used, it can be either opencv, pillow etc.
Your image's RGB channel is all of the zeros, color difference in the alpha channel
Get Alpha channel
img = cv2.imread("myImage.png", cv2.IMREAD_UNCHANGED)
img_alpha = img[:,:,3]
Convert alpha channel to binary
thresh = 127
im_bw = cv2.threshold(img_alpha, thresh, 255, cv2.THRESH_BINARY)[1]
Range in [0,1]
im_bw = im_bw // 255
You can define a threshold such as 127. Then define pixel values below the threshold as zero and above the threshold as 1. But I think you have another problem because the values of your image have a wide range (0 to 255). This is not consistent with the image you show.
Your image has alpha channel and transparency
import sys
import cv2
import numpy as np
dir = sys.path[0]
im = cv2.imread(dir+'/img.png', -1)
im[np.where(im[:, :, 3] == 0)] = (255, 255, 255, 255)
cv2.imwrite(dir+'/img_without_transparency.png', im)
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
min = np.min(im)
max = np.max(im)
bw = cv2.threshold(im, (min+max)//2, 255, cv2.THRESH_BINARY)[1]
print(np.min(bw), np.max(bw)) # 0-255
bw = bw/255
print(np.min(bw), np.max(bw)) # 0-1
So the white space is not really white:

OpenCV filter image by red and green

I would like to filter the image below into green and red. The aim of filtering the image is to be able to count the red and green cells in the image. I am using OpenCV to filter the image but the outcome isn't as expected please see images in Red and green filter. The filtered image seems to include more cells in either color, which will lead to an incorrect count. Is there anything I could do in the code to improve this, please? Many Thanks in advance.
import cv2
image = cv2.imread('2020-03-Sequence-p4-Day-0_Position070.png')
b = image.copy()
# set green and red channels to 0
b[:, :, 1] = 0
b[:, :, 2] = 0
g = image.copy()
# set blue and red channels to 0
g[:, :, 0] = 0
g[:, :, 2] = 0
r = image.copy()
# set blue and green channels to 0
r[:, :, 0] = 0
r[:, :, 1] = 0
# RGB - Blue
cv2.imshow('B-RGB', b)
# RGB - Green
cv2.imshow('G-RGB', g)
# RGB - Red
cv2.imshow('R-RGB', r)
cv2.waitKey(0)
Original image
converted_images
Do you mean something like this:
import cv2
import numpy as np
## Read
img = cv2.imread("img.jpg")
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask_green = cv2.inRange(hsv, (36, 25, 25), (70, 255,255))
mask_red1 = cv2.inRange(hsv, (0, 70, 50), (10, 255, 255))
mask_red2 = cv2.inRange(hsv, (170, 70, 50), (180, 255, 255))
mask_orange = cv2.inRange(hsv, (10, 100, 20), (25, 255, 255))
mask_yellow = cv2.inRange(hsv, (21, 39, 64), (40, 255, 255))
## slice the red and orange
imask_red1 = mask_red1>0
imask_red2 = mask_red2>0
imask_orange = mask_orange>0
imask_yellow = mask_yellow>0
red = np.zeros_like(img, np.uint8)
red[imask_red1] = img[imask_red1]
red[imask_red2] = img[imask_red2]
red[imask_orange] = img[imask_orange]
red[imask_yellow] = img[imask_yellow]
## slice the green
imask_green = mask_green>0
green = np.zeros_like(img, np.uint8)
green[imask_green] = img[imask_green]
## save
cv2.imwrite("green.jpg", green)
cv2.imwrite("red.jpg", red)
Some of the green is getting carried over into the red version, I guess you could adjust the hsv range to fix that

How to fill a polygon in OpenCV?

The Python code given below draws a triangle, how can I fill it with a color inside? Or another easier way to draw a triangle in OpenCV?
pts = np.array([[100,350],[165,350],[165,240]], np.int32)
cv2.polylines(img,[pts],True,(0,255,255),2)
You have to use cv2.fillPoly().
Illustration for 2-channeled image
Change the second line to:
cv2.fillPoly(img, [pts], 255)
Code:
img = np.zeros([400, 400],dtype=np.uint8)
pts = np.array([[100,350],[165,350],[165,240]], np.int32)
cv2.fillPoly(img, [pts], 255)
cv2.imshow('Original', img)
Result:
Illustration for 3-channeled color image
img = cv2.imread('image_path')
pts = np.array([[170,50],[240, 40],[240, 150], [210, 100], [130, 130]], np.int32)
cv2.fillPoly(img, [pts], (255,150,255))
Result:

Fill the outside of contours OpenCV

I am trying to color in black the outside region of a contours using openCV and python language.
Here is my code :
contours, hierarchy = cv2.findContours(copy.deepcopy(img_copy),cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]
# how to fill of black the outside of the contours cnt please? `
Here's how you can fill an image with black color outside of a set of contours:
import cv2
import numpy
img = cv2.imread("zebra.jpg")
stencil = numpy.zeros(img.shape).astype(img.dtype)
contours = [numpy.array([[100, 180], [200, 280], [200, 180]]), numpy.array([[280, 70], [12, 20], [80, 150]])]
color = [255, 255, 255]
cv2.fillPoly(stencil, contours, color)
result = cv2.bitwise_and(img, stencil)
cv2.imwrite("result.jpg", result)
UPD.: The code above exploits the fact that bitwise_and with 0-s produces 0-s, and won't work for fill colors other than black. To fill with an arbitrary color:
import cv2
import numpy
img = cv2.imread("zebra.jpg")
fill_color = [127, 256, 32] # any BGR color value to fill with
mask_value = 255 # 1 channel white (can be any non-zero uint8 value)
# contours to fill outside of
contours = [ numpy.array([ [100, 180], [200, 280], [200, 180] ]),
numpy.array([ [280, 70], [12, 20], [80, 150]])
]
# our stencil - some `mask_value` contours on black (zeros) background,
# the image has same height and width as `img`, but only 1 color channel
stencil = numpy.zeros(img.shape[:-1]).astype(numpy.uint8)
cv2.fillPoly(stencil, contours, mask_value)
sel = stencil != mask_value # select everything that is not mask_value
img[sel] = fill_color # and fill it with fill_color
cv2.imwrite("result.jpg", img)
Can fill with another image as well, for example, using img[sel] = ~img[sel] instead of img[sel] = fill_color would fill it with the same inverted image outside of the contours:

Identifying the range of a color in HSV using openCV

I am working on identifying the color yellow using openCV in python. I have come to this step where I have to define the lower and upper range of the color yellow in HSV.
Example for defining the range of Blue:
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
The HSV is usually defined in percentage, I want to know how to define the range for yellow like the example.
This is the colorspaces tutorial I've been following.
Edit:
There are some suggestions in the blog mentioned above, but it does not give me the desired output.
It's Simple. You can use the function, cv2.cvtColor().
Instead of passing an image, you just pass the BGR values which you want to convert to hsv.
For Example, to find the HSV value of Green, type the following command
import numpy as np
import cv2
green = np.uint8([[[0, 255, 0]]]) #here insert the bgr values which you want to convert to hsv
hsvGreen = cv2.cvtColor(green, cv2.COLOR_BGR2HSV)
print(hsvGreen)
lowerLimit = hsvGreen[0][0][0] - 10, 100, 100
upperLimit = hsvGreen[0][0][0] + 10, 255, 255
print(upperLimit)
print(lowerLimit)
Now, the Upper Limit will be [H+10, 100,100]
and Lower Limit will be [H-10, 255, 255]
Official documentation (see the last part of following webpage) https://docs.opencv.org/3.1.0/df/d9d/tutorial_py_colorspaces.html
take a look at this page you will find HSV values of the color you want.
For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255]. Different softwares use different scales. So if you are comparing OpenCV values with them, you need to normalize these ranges.
i guess you are searching the values like below for yellow
lower_blue = np.array([25,50,50])
upper_blue = np.array([32,255,255])
You can use this sample color palette. First value is the upper limit and second value is the lower limit
color_dict_HSV = {'black': [[180, 255, 30], [0, 0, 0]],
'white': [[180, 18, 255], [0, 0, 231]],
'red1': [[180, 255, 255], [159, 50, 70]],
'red2': [[9, 255, 255], [0, 50, 70]],
'green': [[89, 255, 255], [36, 50, 70]],
'blue': [[128, 255, 255], [90, 50, 70]],
'yellow': [[35, 255, 255], [25, 50, 70]],
'purple': [[158, 255, 255], [129, 50, 70]],
'orange': [[24, 255, 255], [10, 50, 70]],
'gray': [[180, 18, 230], [0, 0, 40]]}
COLOR RANGE
color_dict_HSV = {'black': [[180, 255, 30], [0, 0, 0]],
'white': [[180, 18, 255], [0, 0, 231]],
'red1': [[180, 255, 255], [159, 50, 70]],
'red2': [[9, 255, 255], [0, 50, 70]],
'green': [[89, 255, 255], [36, 50, 70]],
'blue': [[128, 255, 255], [90, 50, 70]],
'yellow': [[35, 255, 255], [25, 50, 70]],
'purple': [[158, 255, 255], [129, 50, 70]],
'orange': [[24, 255, 255], [10, 50, 70]],
'gray': [[180, 18, 230], [0, 0, 40]]}
CREDITS:
Ali Hashemian
HOW TO REMOVE A COLOR FROM YOUR IMAGE USING OPENCV
Since most of you would like to do that, i.e. in my case the task was to remove blue color from the image, I used the following code, to remove blue ink stamps and, blue tick marks from my image in order for proper OCR using Tesseract.
[COLOR REMOVAL] CODE
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# image path:
#path = "D://opencvImages//"
#fileName = "out.jpg"
# Reading an image in default mode:
inputImage = cv2.imread('0.jpg')
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Convert the BGR image to HSV:
hsvImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2HSV)
# Create the HSV range for the blue ink:
# [128, 255, 255], [90, 50, 70]
lowerValues = np.array([90, 50, 70])
upperValues = np.array([128, 255, 255])
# Get binary mask of the blue ink:
bluepenMask = cv2.inRange(hsvImage, lowerValues, upperValues)
# Use a little bit of morphology to clean the mask:
# Set kernel (structuring element) size:
kernelSize = 3
# Set morph operation iterations:
opIterations = 1
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
bluepenMask = cv2.morphologyEx(bluepenMask, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101)
# Add the white mask to the grayscale image:
colorMask = cv2.add(grayscaleImage, bluepenMask)
_, binaryImage = cv2.threshold(colorMask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
cv2.imwrite('bwimage.jpg',binaryImage)
thresh, im_bw = cv2.threshold(binaryImage, 210, 230, cv2.THRESH_BINARY)
kernel = np.ones((1, 1), np.uint8)
imgfinal = cv2.dilate(im_bw, kernel=kernel, iterations=1)
cv2.imshow(imgfinal)
BEFORE [Original Image]
Blue Mark Extraction
Final Image
Here you can see that all of the tick marks are almost, removed the reason is that because there is always room for improvement, but this, as it seems, is the best we can get because even removing these little marks is not going to have a profound effect on the OCR using Tesseract.
HOPE THAT HELPS!
if i want to do it ,first find rgb numbers for yellow (i use 'Edit colors 'in paint)
then change them to HSV whith this method:
u = np.uint8([[[0,236,236]]])
# define range of blue color in HSV
lower_yellow = np.array(cv2.cvtColor(l,cv2.COLOR_BGR2HSV))
upper_yellow = np.array( cv2.cvtColor(u,cv2.COLOR_BGR2HSV))```
If you take pictures from a camera, it will depend on lighting condition. If your aim is to track some objects, you should always update your HSV values. My advise is keep your boundary as narrow as possible in your lighting condition.

Categories