I am trying to identify if an image is black and white or a color image using Open CV in python language. i have created a black and white image using MS paint to check the same. even though the image is black white it still has RGB values other than 0 and 255.
below is the code i have used and images i have used. the output i am getting is color image.
I checked the RGB values they have values other than 0 and 255, i am not able to debug why, can some one help me with this?
img = cv2.imread('C:/Comp_vision/image_data/black_and_white.jpg')
image_pixel =img.flatten()
bnw_cnt = sum(np.where((image_pixel == 0) | (image_pixel == 255), 1, 0))
if np.vectorize(bnw_cnt) == np.vectorize(image_pixel):
print("Black and white image")
else:
print ("Color image")
Image will have black-white colors if and only if for given pixel (x,y) values on each channel will be equal.
For example:
def check_gray(img):
for x in range(img.shape[0])
for y in range(img.shape[1])
b, g, r == img[x,y]
if not(b == g == r):
return False
return True
Related
I am attempting to only keep the part of the image bounded by the orange/greenish line in lot #17.
As you can see the shape is fairly non standard and I am new to image processing so my approach thus far has been brute forced and error prone.
Each image I need to do this for has a black dot (rgb of (77,77,77)) in the center of the shape I want to crop which has been my anchor.
import PIL
import pandas as pd
image = PIL.Image.open(file)
rgb_im = image.convert('RGB')
color = (77,77,77)
colorindex = pd.DataFrame(data = None,columns = ['X','Y'])
for x in range(image.size[0]):
for y in range(image.size[1]):
r, g, b = rgb_im.getpixel((x, y))
if (r,g,b) == color:
append = [x,y]
append = pd.Series(append,index = colorindex.columns)
colorindex = colorindex.append(append,ignore_index = True)
center = [colorindex.mode()['X'][0],colorindex.mode()['Y'][0]]
line = pd.read_excel('C:/Users/lines RGb.xlsx') ##Prerecorded RGB Values
def findparcelline(CenterX,CenterY,direction):
if direction == 'left':
for x in range(CenterX):
r,g,b = rgb_im.getpixel((CenterX-x,CenterY))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterX-x
return pixelsave
elif direction == 'right':
for x in range(CenterX):
r,g,b = rgb_im.getpixel((CenterX+x,CenterY))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterX+x
return pixelsave
elif direction == 'down':
for y in range(CenterY):
r,g,b = rgb_im.getpixel((CenterX,CenterY + y))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterY + y
return pixelsave
elif direction == 'up':
for y in range(CenterY):
r,g,b = rgb_im.getpixel((CenterX,CenterY - y))
for i in range(len(line)):
if (r,g,b) == (line.loc[i][0],line.loc[i][1],line.loc[i][2]):
pixelsave = CenterY - y
return pixelsave
directions = ['left','down','right','up']
coords =[]
for direction in directions:
coords.append(findparcelline(center[0],center[1],direction))
im1 = image.crop(coords)
My code only works for right side up rectangular shapes (which a good bit of them are) but it will fail when it comes to something like in the example.
I've thought about using the code written this far to then 'walk the line' from the pixel location provided via a 9x9 array of pixels and only selecting the ones that:
aren't previously selected
match the prerecorded color values
are closest to the anchor pixel location
But in the example there are even more rgb color values to and even some holes in the line I'm interested in.
Is there a way to obtain the coordinates of the line bounding the black dot in the center and subsequently crop the image after having recording all the coordinates?
Thanks in advance.
First of all: If you have access to the generation of these images, save them as lossless PNGs! Those JPG artifacts make it even harder to get proper results. For example, only one pixel of your "black" dot actually has RGB values of (77, 77, 77). Therefore, I omitted the programmatically finding of the "black" dot, and assumed the image center as the dot location.
Since you have kind of red-ish lines with some kind of yellow-ish dots, I rectified the red channel by subtracting a portion of the green channel to get rid of yellow-ish colors. After some further emphasizing (red-ish lines have high values in the red channel), the new red channel looks like this:
On that new red channel, I use some kind of Laplace operator to detect the (red-ish) lines. After some further processing, that'd be the result:
From there, it's just some thresholding using Otsu's method to get a proper binary image to work on:
Finally, I find all contours, and iterate them. If I find an inner(!) contour – please see this answer for an extensive explanation on contour hierarchies – which contains the location of the "black" dot, that must be shape of interest. Since you might get some odd, open contours from the surrounding, you need to stick to inner contours. Also, it's an assumption here, that the shape of interest is closed.
After extracting the proper contour, you just need to set up a proper mask, and for example blacken the background, or crop the image using the bounding rectangle of that mask:
Here's the full code:
import cv2
import numpy as np
# Read image, split color channels
img = cv2.imread('5aY7A.jpg')
b, g, r = cv2.split(img)
# Rectify red-ish lines (get rid of yellow-ish dots) by subtracting
# green channel from red channel
r = r - 0.5 * g
r[r < 0] = 0
# Emphasize red-ish lines
r **= 2
r = cv2.normalize(r, 0, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# Detection of red-ish lines by Laplace operator
r = cv2.Laplacian(r, cv2.CV_64F)
r = cv2.erode(r, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
r = cv2.GaussianBlur(r, (5, 5), 0)
r = cv2.normalize(r, 0, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
# Mask red-ish lines
r = cv2.threshold(r, 10, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
r = cv2.morphologyEx(r, cv2.MORPH_CLOSE,
cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
# Detection of "black" dot location omitted here due to JPG artifacts...
dot = (916, 389)
# Find contours from masked red-ish lines
cnts, hier = cv2.findContours(r, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Find some inner(!) contour containing the "black dot"
cnt = None
for i, c in enumerate(cnts):
if cv2.pointPolygonTest(c, dot, True) > 0 and hier[0, i, 3] != -1:
cnt = c
break
if cnt is None:
print('Something went wrong, no contour found.')
else:
mask = cv2.drawContours(np.zeros_like(r), [cnt], -1, 255, cv2.FILLED)
output = cv2.bitwise_xor(img, np.zeros_like(img), mask=mask)
cv2.imshow('Output', output)
cv2.waitKey(0)
cv2.destroyAllWindows()
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.2
NumPy: 1.20.3
OpenCV: 4.5.2
----------------------------------------
With PIL, I want to mask an image over other image.
However, I want to keep the black outline of original image.
Example:
Below is the code I've tried. I have different shapes (Circle, Heart, Triangle) All with black outlines. I want to put the mask over those shapes without losing the original image's black outline, but I'm unsure of how to do so.
whead = whead.resize((200, 240))
data = np.array(whead)
red, green, blue, alpha = data.T
white_areas = (red == 222) & (blue == 222) & (green == 222)
data[..., :-1][white_areas.T] = ImageColor.getrgb(headhex)
whead2 = Image.fromarray(data)
img.paste(whead2, (0, 30), whead2)
facemask1 = facemask1.resize((200, 240))
data = np.array(facemask1)
red, green, blue, alpha = data.T
white_areas = (red == 23) & (blue == 0) & (green == 255)
data[..., :-1][white_areas.T] = ImageColor.getrgb(facemask1hex)
facemask12 = Image.fromarray(data)
img.paste(facemask12, (0, 30), whead2)
Since you asked for PIL solution, try PIL.ImageChops.multiply.
Make sure you color your image white only. And you need to make your background transparent - if your tool don't support alpha channel, lots of web-based background removers are out there to help you.
Files
mask.png
outline.png
Code
from PIL import Image, ImageChops
source = Image.open("outline.png")
source = source.convert("RGBA")
mask = Image.open("mask.png")
mask = mask.convert("RGBA")
output = ImageChops.multiply(source, mask)
output.save("output.png")
You need to match image mode, for i.e. mask.png is on mode RGB while outline.png is on RGBA and will cause ValueError: images do not match.
output.png
I have an image and I would like to find the intensity of zero region (black). My idea is to draw a small box around the black region. This is what I expect as an outcome.
(x, y) is the center of the box. I draw 40 unit box around it
My attempt is as follow;
import cv2
from random import *
img = cv2.imread("path/to/image.png", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (600, 800))
rows,cols = img.shape[:2]
try:
for i in range(100):
x = randint(1, rows) # Pick a random number between 1 and rows.
y = randint(1, cols) # Pick a random number between 1 and rows.
while True:
sum_intensity = 0
for i in range(x-20, x+20):
for j in range(y-20, y+20):
intensity = img[i,j]
sum_intensity += intensity
if sum_intensity == 0:
print("zero intensity found")
cv2.rectangle(img, (x-20, y+20), (x+20, y-20), (255, 255, 255), 2)
cv2.imwrite("path/to/save.png", img)
break
else:
print("No zero intensity")
break
except:
pass
one example of the generated boxes are as below.. you can see in which there are overlapping boxes with the gray region, which I want to avoid!! I have to pick the complete background (black). Also I just need a one box... no need to see several of them. I know I can use a boolian variable, but dont know how in python.
If I understand correctly, you want to find a 41 x 41 square that only contains black (value 0).
You can achieve that by performing a 41 x 41 dilation and choosing any black pixel, which is the center of the desired square. (Image binarized for clarity; the white squares are due to spurious pixels in the original image.)
i am trying to find the coordinates that in RGB images, then use these coordinates to train my neural network model
i am running the below code but i am not getting any output.. any idea why? do you have a better idea to get the coordinates of green color from RGB images?
Please advise,
UPDATE : attached image is one of my training data images
color = (0,255,0) #green value in RGB
for image in train_data[0:5000]:
img = Image.open(image)
rgb_im = img.convert('RGB')
for x in range(rgb_im.size[0]):
for y in range(rgb_im.size[1]):
r, g, b = rgb_im.getpixel((x, y))
if (r,g,b) == color:
print(f"Found {color} at {x},{y}!")
[![Image Examples][1]][1]
following the comments correspondence above, your code seems to be working fine but the input color (0, 255, 0) is indeed not exist in the image.
I have already taken a look at this question: SO question and seem to have implemented a very similar technique for replacing a single color including the alpha values:
c = Image.open(f)
c = c.convert("RGBA")
w, h = c.size
cnt = 0
for px in c.getdata():
c.putpixel((int(cnt % w), int(cnt / w)), (255, 0, 0, px[3]))
cnt += 1
However, this is very slow. I found this recipe out on the interwebs, but have not had success using it thus far.
What I am trying to do is take various PNG images that consist of a single color, white. Each pixel is 100% white with various alpha values, including alpha = 0. What I want to do is basically colorize the image with a new set color, for instance #ff0000<00-ff>. SO my starting and resulting images would look like this where the left side is my starting image and the right is my ending image (NOTE: background has been changed to a light gray so you can see it since it is actually transparent and you wouldn't be able to see the dots on the left.)
Any better way to do this?
If you have numpy, it provides a much, much faster way to operate on PIL images.
E.g.:
import Image
import numpy as np
im = Image.open('test.png')
im = im.convert('RGBA')
data = np.array(im) # "data" is a height x width x 4 numpy array
red, green, blue, alpha = data.T # Temporarily unpack the bands for readability
# Replace white with red... (leaves alpha values alone...)
white_areas = (red == 255) & (blue == 255) & (green == 255)
data[..., :-1][white_areas.T] = (255, 0, 0) # Transpose back needed
im2 = Image.fromarray(data)
im2.show()
Edit: It's a slow Monday, so I figured I'd add a couple of examples:
Just to show that it's leaving the alpha values alone, here's the results for a version of your example image with a radial gradient applied to the alpha channel:
Original:
Result:
Try this , in this sample we set the color to black if color is not white .
#!/usr/bin/python
from PIL import Image
import sys
img = Image.open(sys.argv[1])
img = img.convert("RGBA")
pixdata = img.load()
# Clean the background noise, if color != white, then set to black.
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y] == (255, 255, 255, 255):
pixdata[x, y] = (0, 0, 0, 255)
you can use color picker in gimp to absorb the color and see that's rgba color
The Pythonware PIL online book chapter for the Image module stipulates that putpixel() is slow and suggests that it can be sped up by inlining. Or depending on PIL version, using load() instead.