Classifying black and silver images - python

I am struggling to classify the images at black and silver colores
How can I use OpenCV in order to classify them? Image with black color Image with Silver color?
What error is there below ?
import numpy as np
import cv2
# RGB color boundaries
black = ([0, 0, 0], [50, 50, 50])
boundaries = [black]
#
# Load an color image in grayscale
img = cv2.imread('zmFf4.jpg')
print(img.shape)
breakpoint()
for (lower, upper) in boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(img, lower, upper)
output = cv2.bitwise_and(img, img, mask=mask)
# show the images
cv2.imshow("images", np.hstack([img, output]))
cv2.waitKey(0)

You are not looking the silver range
Do as under
import numpy as np
import cv2
# RGB color boundaries
black = ([0, 0, 0], [50, 50, 50])
silver = ([192, 192, 192], [212, 212, 212])
boundaries = [black, silver]
#
# Load an color image in grayscale
img = cv2.imread('zmFf4.jpg')
print(img.shape)
breakpoint()
for (lower, upper) in boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(img, lower, upper)
output = cv2.bitwise_and(img, img, mask=mask)
# show the images
cv2.imshow("images", np.hstack([img, output]))
cv2.waitKey(0)

Related

Improve quality of extracted image in OpenCV

#Segmenting the red pointer
img = cv2.imread('flatmap.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([140, 110, 0])
upper_red = np.array([255, 255 , 255])
# Threshold with inRange() get only specific colors
mask_red = cv2.inRange(hsv, lower_red, upper_red)
# Perform bitwise operation with the masks and original image
red_pointer = cv2.bitwise_and(img,img, mask= mask_red)
# Display results
cv2.imshow('Red pointer', red_pointer)
cv2.imwrite('redpointer.jpg', red_pointer)
cv2.waitKey(0)
cv2.destroyAllWindows()
I have a map and need to extract the red arrow. The code works but the arrow has black patches in it. How would I go about altering the code to improve the output of the arrow so it's a solid shape?
You could use:
dilate to fill up the internal noise in the shape
external contour finding to get the outline of the triangle
convex hull to further smooth it out
import cv2
import numpy as np
img = cv2.imread('dCkpC.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_red = np.array([140, 60, 0])
upper_red = np.array([255, 255, 255])
mask_red = cv2.inRange(hsv, lower_red, upper_red)
element = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
mask_red = cv2.dilate(mask_red, element)
contours, _ = cv2.findContours(mask_red, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
hull_list = [cv2.convexHull(contour) for contour in contours]
drawing = np.zeros_like(img)
for hull in hull_list:
cv2.fillConvexPoly(img, hull, (255, 0, 0))
cv2.imshow('Image', img)
cv2.imwrite('out.jpg', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
out.jpg ends up looking like
where the triangle has been filled in with blue.
I've looked at the channels in HSL/HSV space.
The arrows are the only stuff in the picture that has any saturation. That would be one required (but insufficient) aspect to get a lock on the desired arrow. I've picked those pixels and they appear to have a bit more than 50% saturation, so I'll use a lower bound of 25% (64).
That red arrow's hue dithers around 0 degrees (red)... that means some of its pixels are on the negative side of 0, i.e. something like 359 degrees.
You need to use two inRange calls to collect all hues from 0 up, and all hues from 359 down. Since OpenCV encodes hues in 2-degree steps, that'll be a value of 180 and down. I'll select 0 +- 20 degrees (0 .. 10 and 170 .. 180).
In summary:
hsv_im = cv.cvtColor(im, cv.COLOR_BGR2HSV)
mask1 = cv.inRange(hsv_im, np.array([0, 64, 0]), np.array([10, 255, 255]))
mask2 = cv.inRange(hsv_im, np.array([170, 64, 0]), np.array([180, 255, 255]))
mask = mask1 | mask2
cv.imshow("mask", mask)
cv.waitKey()

How to remove background gray drawings from image in OPENCV python

I need to remove the gray drawing from the image background and only need symbols drawn over it.
Here is my code to do that using morphologyEx but it did not remove the entire gray drawing that is in background.
img_path = "images/new_drawing.png"
img = cv2.imread(img_path)
kernel = np.ones((2,2), dtype=np.uint8)
result = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel, iterations=1)
cv2.imshow('Without background',result);
cv2.waitKey(0)
cv2.destroyAllWindows()
I tried this also and got expected results in grayscale but unable to convert it to BGR.
Here is my code
img = cv2.imread('images/new_drawing.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
med_blur = cv2.medianBlur(gray_img, ksize=3)
_, thresh = cv2.threshold(med_blur, 190, 255, cv2.THRESH_BINARY)
blending = cv2.addWeighted(gray_img, 0.5, thresh, 0.9, gamma=0)
cv2.imshow("blending", blending);
Also i used contours to identify symbols and draw them to white image but problem is that it also identify background drawing that i don't want.
Input image
Expected output image
Also the drawing will be always in gray color as in image.
Please help me out to get better result.
You are almost there...
Instead of using cv2.inRange to "catch" the non-gray pixel I suggest using cv2.inRange for catching all the pixels you want to change to white color:
mask = cv2.inRange(hsv, (0, 0, 100), (255, 5, 255))
The hue range is irrelevant.
The saturation is close to zero (shades of gray).
The brightness excludes the black pixels (you like to keep).
In order to get a nicer solution, I also used the following additional stages:
Build a mask of non-black pixels:
nzmask = cv2.inRange(hsv, (0, 0, 5), (255, 255, 255))
Erode the above mask:
nzmask = cv2.erode(nzmask, np.ones((3,3)))
Apply and operation between mask and nzmask:
mask = mask & nzmask
The above stages keeps the gray pixels around the black text.
Without the above stages, the black text gets thinner.
The last stage is replacing mask pixels with white:
new_img = img.copy()
new_img[np.where(mask)] = 255
Here is the code:
import numpy as np
import cv2
img_path = "new_drawing.png"
img = cv2.imread(img_path)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (0, 0, 100), (255, 5, 255))
cv2.imshow('mask before and with nzmask', mask);
# Build mask of non black pixels.
nzmask = cv2.inRange(hsv, (0, 0, 5), (255, 255, 255))
# Erode the mask - all pixels around a black pixels should not be masked.
nzmask = cv2.erode(nzmask, np.ones((3,3)))
cv2.imshow('nzmask', nzmask);
mask = mask & nzmask
new_img = img.copy()
new_img[np.where(mask)] = 255
cv2.imshow('mask', mask);
cv2.imshow('new_img', new_img);
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Here is one way to do that in Python/OpenCV.
Read the input
Convert to HSV and separate channels
Threshold the saturation channel
Threshold the value channel and invert
Combine the two threshold images as a mask
Apply the mask to the input to write white where the mask is black
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('symbols.png')
# convert image to hsv colorspace
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
# threshold saturation image
thresh1 = cv2.threshold(s, 92, 255, cv2.THRESH_BINARY)[1]
# threshold value image and invert
thresh2 = cv2.threshold(v, 128, 255, cv2.THRESH_BINARY)[1]
thresh2 = 255 - thresh2
# combine the two threshold images as a mask
mask = cv2.add(thresh1,thresh2)
# use mask to remove lines in background of input
result = img.copy()
result[mask==0] = (255,255,255)
# display IN and OUT images
cv2.imshow('IMAGE', img)
cv2.imshow('SAT', s)
cv2.imshow('VAL', v)
cv2.imshow('THRESH1', thresh1)
cv2.imshow('THRESH2', thresh2)
cv2.imshow('MASK', mask)
cv2.imshow('RESULT', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save output image
cv2.imwrite('symbols_thresh1.png', thresh1)
cv2.imwrite('symbols_thresh2.png', thresh2)
cv2.imwrite('symbols_mask.png', mask)
cv2.imwrite('symbols_cleaned.png', result)
Saturation channel thresholded:
Value channel thresholded and inverted:
Mask:
Result:

Taking a 'White' pixel count of each masked frame in a video file

I am trying to develop a way to output the number of pixels that fall between the HSV mask limits of each frame of a given video on a Raspberry Pi camera.
The aim of this is to determine the 'Red' intensity of a red dimmer light and therefore split its intensity into different levels, and hence determine which intensity the light is switched to in each frame. How would I go about calculating said pixel count?
My progress so far is that I have a method and limits for masking a frame using OpenCV commands. I just need a way to count the remaining pixels of each frame.
Here is my current code, which I have slightly adapted from this great tutorial I found: Automatic Vision Object Tracking
import cv2
import numpy as np
img = cv2.imread('hsvmeasure.jpg', 1)
img = cv2.resize(img, (0,0), fx=0.2, fy=0.2)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_range = np.array([160,100,100], dtype=np.uint8)
upper_range = np.array([180,255,255], dtype=np.uint8)
mask = cv2.inRange(hsv, lower_range, upper_range)
cv2.imshow('mask', mask)
cv2.imshow('img', img)
while(1):
k = cv2.waitKey(0)
if (k == 27):
break
cv2.destroyAllWindows()
You have already done most of the work, now you can just make a range of lower and upper intensity (non-overlapping) and count how many pixels are 255 in the mask.
import cv2
import numpy as np
img = cv2.imread('test.jpg', 1)
img = cv2.resize(img, (0,0), fx=0.2, fy=0.2)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
lower_intensity_levels = [ [150,100,100], [161, 100,100], [171, 100, 100] ] # non overlapping
upper_intensity_levels = [ [160,255,255] , [170, 255, 255], [180, 255, 255] ] # make these list based on your intensity requirements
mask_on_counts = []
for i in range(len(lower_intensity_levels)):
lower_range = np.array(lower_intensity_levels[i], dtype=np.uint8)
upper_range = np.array(upper_intensity_levels[i], dtype=np.uint8)
mask = cv2.inRange(hsv, lower_range, upper_range)
mask_on_counts.append(np.sum(mask==255))
import matplotlib.pyplot as plt
plt.imshow(mask)
plt.show()
for i in range(len(mask_on_counts)):
print(f'level {i+1} number of pixels: {mask_on_counts[i]}')

changing hsv values of an image in opencv python isnt working

Im trying to set the Minimum and Maximum value of HSV of an Image in opencv python but after running the code all I can see is a blank rectangle box.
import cv2
import sys
import numpy as np
# Load in image
image = cv2.imread('power.jpg')
# Set minimum and max HSV values to display
lower = np.array([0, 209, 0])
upper = np.array([179, 255, 236])
# Create HSV Image and threshold into a range.
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower, upper)
output = cv2.bitwise_and(image,image, mask= mask)
# Display output image
cv2.imshow('image',output)
I was able to solve it.
import numpy as np
import cv2
img = cv2.imread( "power.jpg" )
## convert to hsv
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
## mask of red (36,0,0) ~ (70, 255,255)
mask = cv2.inRange(hsv, (0, 209, 0), (179, 255,236))
bak = img.copy()
# Show only red
#bak[mask > 0] = (0, 0, 255)
imask = mask>0
green = np.zeros_like(img, np.uint8)
green[imask] = img[imask]
## save
cv2.imwrite("image.png", green)

Numpy where() creates spots of different color than the ones defined

I'm writing a script that creates a mask for an image. My input image looks like this:
The original image is only 40x40px, here it is for reference:
I want to create a mask of the purple area in the center of the image. This is what I do:
# read the 40x40 image and convert it to RGB
input_image = cv2.cvtColor(cv2.imread('image.png'), cv2.COLOR_BGR2RGB)
# get the value of the color in the center of the image
center_color = input_image[20, 20]
# create the mask: pixels with same color = 255 (white), other pixels = 0 (black)
mask_bw = np.where(input_image == center_color, 255, 0)
# show the image
plt.imshow(mask_bw)
Most of the time this works perfectly fine, but for some images (like the one I attached to this question) I consistently get some blue areas in my mask like on the image below. This is reproducible and the areas are always the same for the same input images.
This is already weird enough, but if I try to remove the blue areas, this doesn't work either.
mask_bw[mask_bw != (255, 255, 255)] = 0 # this doesn't change anything..
Why is this happening and how do I fix this?
Additional info
tried with numpy version 1.17.3 and 1.17.4
Reproduced in my local environment and in a google colab notebook
The main problem is that you're trying to compare three channels but only setting the value for one channel. This is most likely causing the blue areas on the mask. When you use np.where() to set the other pixels to black, you are only setting this on the 1st channel instead of all three channels. You can visualize this by splitting each channel and printing the before/after arrays which will show you that the resulting array values are RGB(0,0,255). So to fix this problem, we need to compare each individual channel then set the desired area in white while setting any black areas on the mask to black for all three channels. Here is one way to do it:
import numpy as np
import cv2
image = cv2.imread('1.png')
center_color = image[20, 20]
b, g, r = cv2.split(image)
mask = (b == center_color[0]) & (g == center_color[1]) & (r == center_color[2])
image[mask] = 255
image[mask==0] = 0
cv2.imshow('image', image)
cv2.waitKey()
A hotfix to remove the blue areas using your current code would be to convert the image to grayscale (1-channel) then change all non-white pixels to black.
import numpy as np
import cv2
# Load image, find color, create mask
image = cv2.imread('1.png')
center_color = image[20, 20]
mask = np.where(image == center_color, 255, 0)
mask = np.array(mask, dtype=np.uint8)
# Convert image to grayscale, convert all non-white pixels to black
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
mask[mask != 255] = 0
cv2.imshow('mask', mask)
cv2.waitKey()
Here are two alternative methods to obtain a mask of the purple area
Method #1: Work in grayscale space
import numpy as np
import cv2
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
center_color = gray[20, 20]
mask = np.array(np.where(gray == center_color, 255, 0), dtype=np.uint8)
cv2.imshow('mask', mask)
cv2.waitKey()
Method #2: Color thresholding
The idea is to convert the image to HSV color space then use a lower and upper color range to segment the image to create a binary mask
import numpy as np
import cv2
image = cv2.imread('1.png')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 124, 0])
upper = np.array([179, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
cv2.imshow('mask', mask)
cv2.waitKey()
Both methods should yield the same result
If you have a 3-channel image (i.e. RGB or BGR or somesuch) and you want to generate a single channel mask (i.e. you want 0/1 or True/False) for each pixel, then you effectively need to group the 3 values into a single using np.all() like this:
import cv2
import numpy as np
# Load image and get centre colour
image = cv2.imread('40x40.png')
cc = im[20, 20]
print(image.shape)
(40, 40, 3)
# Generate list of unique colours present in image so we know what we are dealing with
print(np.unique(im.reshape(-1,3), axis=0))
array([[140, 109, 142],
[151, 106, 140],
[160, 101, 137],
[165, 134, 157],
[175, 149, 171],
[206, 87, 109],
[206, 185, 193]], dtype=uint8)
# Generate mask of pixels matching centre colour
mask_bw = np.where(np.all(im==cc,axis=2), 255, 0)
# Check shape of mask - no 3rd dimension !!!
print(mask_bw.shape)
(40, 40)
# Check unique colours in mask
print(np.unique(mask_bw.reshape(-1,1), axis=0))
array([[ 0],
[255]])

Categories