NumPy: Understanding values in colour matrix - python

I have an image which I have read and converted into a numpy array. I have then extracted each colour channel (R,G,B) of the image into three separate arrays:
import cv2
import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt
from itertools import cycle
from PIL import Image
image = Image.open('sample_images/fruit_half.png').convert('RGB')
image = np.array(image)
red = image[:,:,2]
green = image[:,:,1]
blue = image[:,:,0]
When I print the value of the "red" array, I get the following output:
print(red)
[[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
...
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]
[0 0 0 ... 0 0 0]]
I would like to know what do the numbers in the red, green and blue arrays represent. Do they represent the intensity of red/green/blue for a specific pixel? Any insights are appreciated.

They stand for the pixel intensity of each color channel in the image. If you print out image.shape you will be able to access its properties
print(image.shape)
You will get something like this
(93, 296, 3)
This tells us the (rows, columns, channels) in the image. In this case, the image has three channels. If you print out each individual channel, they represent pixel intensity ranging from 0-255. Every pixel is made up of the combination of these three channels. If instead you printed out the shape of the image and you got this
(93, 296, 1)
it means that the image only has one channel (a grayscale image).
One thing to note is that OpenCV follows BGR convention while PIL follows RGB. Currently, you are splitting backwards. To split channels using PIL you can do this
image = Image.open('1.jpg').convert('RGB')
image = np.array(image)
red = image[:,:,0]
green = image[:,:,1]
blue = image[:,:,2]
Remember PIL uses RGB format where red is in channel 0, green in channel 1, and blue in channel 2.
To split channels using OpenCV you can do this
image = cv2.imread('1.jpg')
b,g,r = cv2.split(image)
or
b = image[:,:,0]
g = image[:,:,1]
r = image[:,:,2]
Taking this image as an example, you can use a histogram to visualize the channels.
import cv2
from matplotlib import pyplot as plt
image = cv2.imread('1.jpg')
b,g,r = cv2.split(image)
blue = cv2.calcHist([b], [0], None, [256], [0,256])
green = cv2.calcHist([g], [0], None, [256], [0,256])
red = cv2.calcHist([r], [0], None, [256], [0,256])
plt.plot(blue, color='b')
plt.plot(green, color ='g')
plt.plot(red, color ='r')
plt.show()

Yes they represent intensity, each value is an a 8-bit value from 0 to 255. If a value is 0 the red pixel is completely off and 255 is completely on. Usually people just use the image an array (well, opencv list them in the order blue green red). The image array holds a rgb value at every pixel (try printing image). This a standard for images and can be explained here.

RGB picture is a digital matrix with 3 channel, each channel contain a value from 0 to 255 (if your dtype = uint8) to present the percentage of that color in that pixel. Look at the picture:
You can see that if we combine red and Green at 100% (mean 255), we have yellow, if we combine them in 100% together, we have white, etc. By this formula, each pixel will have x of Red and y of Green and z of Blue.
Example:
Therefore, the value you see in red channel is the percent of red color in your picture.
Hope this useful!

Related

python function to change RGB values of an image

I have this function that solely displays the red RGB values of an image. However, I am confused about how the function works. Specifically, why is there a 0 in brackets in this line:
newimage[i][j] = [image[i][j][0], 0, 0]
How can I alter this function to swap the green and blue RGB values while keeping the red value the same?
from matplotlib.pyplot import imshow #command to display a 2D array as an image
from imageio import imread #command to read images from a file or url
coffee = imread('imageio:coffee.png') #Image
def RedImage(image):
newimage = ArrayOfZeros(400, 600)
for i in range(400):
for j in range(600):
newimage[i][j] = [image[i][j][0], 0, 0]
return newimage
imshow(RedImage(coffee))
In
newimage[i][j] = [image[i][j][0], 0, 0]
image[i][j] and newimage[i][j] presumably are arrays consisting of three values for red, green, and blue at a particular pixel location (i, j) in the image.
So image[i][j][0] is the red value of the original image which will also be used as the red value for the new image. The blue and green values of the new image will be 0.
The green value of the original image is image[i][j][1] and the blue value is image[i][j][2]. So in order to swap them in the new image, use the green value of the old image in the position of the blue value for the new image, and the blue value of the old image in the position of the green value for the new image.
newimage[i][j] = [image[i][j][0], image[i][j][2], image[i][j][1]]

How to randomly generate scratch like lines with opencv automatically

I am trying to generate synthetic images for my deep learning model. I need to draw scratches on a black surface. I already have a little script that can generate random white scratch like lines but only horizontally. I need the scratches to also be vertically and curved. On top of that it would also be very helpfull if the thickness of the scratches would also be random so I have thick and thin scratches.
This is my code so far:
import cv2
import numpy as np
import random
height = 384
width = 384
blank_image = np.zeros((height, width, 3), np.uint8)
num_scratches= random.randint(0,5)
for _ in range(num_scratches):
row_random = random.randint(20,370)
blank_image[row_random:(row_random+1), row_random:(row_random+random.randint(25,75))] = (255,255,255)
cv2.imshow("synthetic", blank_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
This is one example result outcome:
How do I have to edit my script so I can get more diverse looking scratches?
The scratches should somehow look like this for example (Done with paint):
need the scratches to also be vertically
Your method might be adopted as follows
import numpy as np # cv2 read image into np.array
img = np.zeros((5,5),dtype='uint8') # same as loading 5 x 5 px black rectangle
img[1:4,2:3] = 255
print(img)
Output:
[[ 0 0 0 0 0]
[ 0 0 255 0 0]
[ 0 0 255 0 0]
[ 0 0 255 0 0]
[ 0 0 0 0 0]]
Explanation: I set all elements (pixel) which have y-cordinate between 1 (inclusive) and 4 (exclusive) and x-cordinate between 2 (inclusive) and 3 (exclusive).
Nonetheless cv2 provide function for drawing lines namely cv2.line which is more handy to use, it does accept img on which to work, start point, end point, color and thickness, docs give following example:
# Draw a diagonal blue line with thickness of 5 px
img = cv2.line(img,(0,0),(511,511),(255,0,0),5)
If you are working in grayscale use value rather than 3-tuple as color.

Image processing: counting individual number pixel in an mask image using Pillow and 3D Numpy

Please help me. I need some opinion on this problem.
I am trying to count individual number of R,G,B value of an mask image.
I have an image which is masked filled background with green and it mask a human with red and an object with blue.
The image size and data type are
(1536, 2048, 3)
uint8
I have tried to access the numpy array of pixels
img_path = "sample.png"
i = Image.open(img_path, 'r')
data = asarray(i)
array = np.array(i)
But the array only show the background green. Something like below.
[[[ 0, 255, 0
0, 255,0]]]
It does not show red and blue color of an image
I have tried getpixel()
i = Image.open(img_path, 'r')
r, g, b = i.getpixel((0, 0))
print("Red: {}, Green: {}, Blue: {}".format(r, g, b))
It does not count the red and and blue color of mask image.
How to count the number of R,G,B pixel in mask image?
Where can I find read more about accessing and counting total number of pixels with numpy and pillow?
Please tell me anything related to this.
from PIL import Image
with Image.open('hopper.jpg') as im:
px = im.load()
r, g, b = px[x, y]
print(r, g, b)
This code worked for me. x and y pixel cordinates.
You can get full info from this

Obtain a color from a image and represent it

I want to know if X color appears in an image. In this case, the color of the study will be green, therefore its RGB value is (0.255.0).
I apply the following code:
img = cv2.imread('img.jpg')
L1 = [0, 255, 0]
matches = np.all(img == L1, axis=2)
result = np.zeros_like(img)
print(result.any())
result[matches] = [255, 0, 255]
cv2.imwrite('resultado.jpg', result)
Basically:
I load the image that I want to analyze.
I describe the RGB value I want to obtain.
I check if this color (green) appears in the image.
I create an image of mine's size completely black and call it
"result".
I show by screen if that color appears through Boolean.
I DRAW THE GREEN AREA OF RED IN RESULT.
Finally I keep this last step.
Below is shown the studio image and then what is painted red.
Image to study:
Result:
Why is not a box painted the same as green but in red? Why just that little dots?
Thank you!
Problem is caused by that green area is NOT build only from [0, 255, 0] as do you think, OT21t.jpg is your input image, when I did:
import cv2
img = cv2.imread('OT21t.jpg')
print(img[950,1300])
I got [ 2 255 1], so it is not [0,255,0]. Keep in mind that when .jpg images are saved, most often it is lossy process - part of data might be jettisoned allowing smaller file size (for more about that search for lossy compression).
here is a script that does what you want, I used numpy too so it won't be difficult to adapt it for your needs.
This script will find a colour and replace it by another:
import numpy
from PIL import Image
im = numpy.array(Image.open("/path/to/img.jpg"))
tol = 4 # tolerence (0 if you want an exact match)
target_color = [0, 255, 0, 255] # color to change
replace_color = [255, 0, 255, 255] # color to use to paint the zone
for y, line in enumerate(im):
for x, px in enumerate(line):
if all((abs(px[i] - target_color[i]) < tol for i in range(3))):
im[y][x] = replace_color
Image.fromarray(im).save("./Desktop/img.png")
This one will be black with only the match coloured in the replace colour:
import numpy
from PIL import Image
im = numpy.array(Image.open("/path/to/img.jpg"))
new_im = numpy.zeros_like(im)
tol = 4 # tolerence (0 if you want an exact match)
target_color = [0, 255, 0, 255] # color to change
replace_color = [255, 0, 255, 255] # color to use to paint the zone
for y, line in enumerate(im):
for x, px in enumerate(line):
if all((abs(px[i] - target_color[i]) < tol for i in range(3))):
new_im[y][x] = replace_color
Image.fromarray(new_im).save("./Desktop/img.png")
What is missing from your script is some tolerance, because your green might not be a perfect green.
I is often more appropriate to use the "Hue, Saturation and Lightness" system rather than RGB to separate out colours in images - see Wikipedia article here.
So you might consider something like this:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image
# Open image and make RGB and HSV versions
RGBim = Image.open("seaside.jpg")
HSVim = RGBim.convert('HSV')
# Make numpy versions
RGBna = np.array(RGBim)
HSVna = np.array(HSVim)
# Extract Hue
H = HSVna[:,:,0]
# Find all green pixels, i.e. where 110 < Hue < 130
lo,hi = 110,130
# Rescale to 0-255, rather than 0-360 because we are using uint8
lo = int((lo * 255) / 360)
hi = int((hi * 255) / 360)
green = np.where((H>lo) & (H<hi))
# Make all green pixels red in original image
RGBna[green] = [255,0,0]
count = green[0].size
print("Pixels matched: {}".format(count))
Image.fromarray(RGBna).save('result.png')

Pixel data incorrect with alpha

I am loading in an image with alpha and am confused at the data I am see to represent each pixel. I am splitting the pixel to get each b,g,r,a band respectively and am confused with my output when loaded into a data set as seen below:
Pixel ---------------------B-band---------G-band----------R-band---------------A-band
Why are my white pixels in the r band (4th column) 254 and not 255?
Why are the alpha values ranging from 0 to 255 and not 0 to 1?
Another interesting note is that where the alpha is set to 0 (transparent) I am confused as to why the pixels being given a color and alternately why opaque pixels are displayed as white and not transparent?
Code:
img = cv2.imread('/Volumes/EXTERNAL/ClassifierImageSets/Origional_2.png',-1)
b,g,r,a = cv2.split(img)
test = pd.DataFrame({'bBnad':b.flat[:],'gBnad':g.flat[:],'rBnad':r.flat[:],'Alpha':a.flat[:]})
with pd.option_context('display.max_rows', None, 'display.max_columns', 4):
print(test)

Categories

Resources