I have been ingesting image data from np arrays and adding two additional dimensions so I can use an image processing pipeline which requires RGB data, and the images are heavily dominated by red. Here's what I'm doing, starting with a dataframe of file paths:
#get filename
f = files.tail(-1)['name'].values[0]
img = plt.imread(f)
#check if it's an array in 3 dimensions
if len(img.shape) == 2:
print('not RGB')
#image sizes vary so get shape
s = img.shape[0:2]
dim2 = np.zeros((s))
dim3 = np.zeros((s))
pix = np.stack((img, dim2,dim3), axis=2)
pix = np.true_divide(pix, 255)
plt.imshow(pix)
And a sample of the result:
Appreciate your help!
The following code explains your problem:
import numpy as np
import matplotlib.pyplot as plt
A = np.random.rand(10, 10)
B = np.zeros((*A.shape, 3))
B[:,:,0] = A
C = A.reshape((*A.shape, 1)).repeat(3, 2)
fig, axs = plt.subplots(ncols=3)
mats = [A, B, C]
for ax, mat in zip(axs, mats):
ax.imshow(mat)
A is your grayscale image. B is what you did: The values of A assigned to the red channel of an RGB image. C is most likely what you want: Since you need an RGB image, you just copy the values of A two times. Results:
Left to right: A, B, C
Related
iam a beginner at python and image processing etc. I want to plot this white pixel. as i know the pixel color identifier for black is 0 and for white is 255. here's the image that i want to plot:
The Image
i try to print out the image ndarray with these following command:
#importing module
import cv2
import numpy as np
import matplotlib.pyplot as plt
import sys
#load image
img = cv2.imread('thin.png')
#image to nd.array
arr0 = np.array(img)
#finding white pixel
arr1 = np.where(arr0 == 255)
#indexing tuple, the printout arr1 is tuple with dtype=int64
tuple1 = arr1[0]
tuple2 = arr1[1]
tuple3 = arr1[2]
#defining x and y axis
x = np.array(tuple1)
y = np.array(tuple2)
z = np.array(tuple3)
plot = plt.plot(x,y)
plt.show()
this is what i get..
output image
i think it's very noisy but i dont have a clue. Thank you very much for help
I think there is some confusion in the dimensions of you array arr0. I think you should look at the indices of the white pixels:
import cv2
import numpy as np
import matplotlib.pyplot as plt
#load image
img = cv2.imread('thin.png')
#image to nd.array
arr0 = np.array(img)
height, width, _ = arr0.shape
x = range(width)
y = [height - np.argwhere(arr0[:, i, 0]==255).mean() for i in x] # "height -" to reverse the y-axis
plt.plot(x,y)
plt.show()
Note: taking the mean because a vertical line can have more than one white pixel (also some won't have any, see picture below)
Output:
I use opencv for an image segmentation in 5 dimensional feature space {r,g,b,x,y} to get better result. The problem is after segmentation is done I can't reconstruct segmented clusters back into image. I don't understand how to do it. What I did is just deleted last 2 columns in segmentedData array to match original image shape. But this doesn't work. The opencv example shows how to do this in just 3-dimensional feature space {r,g,b}. Do you have any ideas?
Thanks.
Below is the code.
import numpy as np
import matplotlib.pyplot as plt
import cv2
img = cv2.imread("../images/segmentation/peppers_BlueHills.png")
x = np.arange(0, img.shape[0])
x = np.tile(x, img.shape[1]).reshape((-1,1))
x = np.float32(x)
y = np.arange(0, img.shape[1])
y = np.repeat(y, img.shape[0]).reshape((-1,1))
y = np.float32(y)
# Reshaping the image into a 2D array of pixels and 3 color values (RGB)
pixelVals = img.reshape((-1,3))
# Convert to float type only for supporting cv2.kmean
pixelVals = np.float32(pixelVals)
features = np.hstack((pixelVals,x,y))
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.85) #criteria 0.85
k = 16 # Number of cluster
ret, labels, centers = cv2.kmeans(features, k, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
centers = np.uint8(centers) # convert data into 8-bit values
segmentedData = centers[labels.flatten()] # Mapping labels to center points( RGB Value)
#segmentedImg = segmentedData.reshape((img.shape)) # reshape data into the original image dimensions
segmentedImg = segmentedData.reshape((img.shape[0],img.shape[1],img.shape[2]+2))
segmentedImg = np.delete(segmentedImg, [3,4], axis=2)
cv2.imshow('segmentedImg', segmentedImg)
cv2.waitKey(0)
I have an image with a black background that contains different shapes in different colors. I want to generate an image per shape, in which the shape is white and the background is black. I have been able to do this with numpy, but I would like to optimize my code using vectorization. This is what I have so far:
import numpy as np
import cv2
image = cv2.imread('mask.png')
image.shape
# (720, 1280, 3)
# Get all colors that are not black
colors = np.unique(image.reshape(-1,3), axis=0)
colors = np.delete(colors, [0,0,0], axis=0)
colors.shape
# (5, 3)
# Example for one color. I could do a for-loop, but I want to vectorize instead
c = colors[0]
query = (image == c).all(axis=2)
# Make the image all black, except for the pixels that match the shape
image[query] = [255,255,255]
image[np.logical_not(query)] = [0,0,0]
Approach #1
You can save a lot on intermediate array data with extension of unique colors into higher dim and then comparing against original data array and then using the mask directly to get the final output -
# Get unique colors (remove black)
colors = np.unique(image.reshape(-1,3), axis=0)
colors = np.delete(colors, [0,0,0], axis=0)
mask = (colors[:,None,None,:]==image).all(-1)
out = mask[...,None]*np.array([255,255,255])
Approach #2
A better/memory-efficient way to get that mask would be with something like this -
u,ids = np.unique(image.reshape(-1,3), axis=0, return_inverse=1)
m,n = image.shape[:-1]
ids = ids.reshape(m,n)-1
mask = np.zeros((ids.max()+1,m,n),dtype=bool)
mask[ids,np.arange(m)[:,None],np.arange(n)] = ids>=0
and hence, a better way to get the final output, like so -
out = np.zeros(mask.shape + (3,), dtype=np.uint8)
out[mask] = [255,255,255]
and probably a better way to get ids would be with matrix-multiplication. Hence :
u,ids = np.unique(image.reshape(-1,3), axis=0, return_inverse=1)
could be replaced by :
image2D = np.tensordot(image,256**np.arange(3),axes=(-1,-1))
ids = np.unique(image2D,return_inverse=1)[1]
I was able to solve it the following way:
import numpy as np
import cv2
# Read the image
image = cv2.imread('0-mask.png')
# Get unique colors (remove black)
colors = np.unique(image.reshape(-1,3), axis=0)
colors = np.delete(colors, [0,0,0], axis=0)
# Get number of unique colors
instances = colors.shape[0]
# Reshape colors and image for broadcasting
colors = colors.reshape(instances,1,1,3)
image = image[np.newaxis]
# Generate multiple images, one per instance
mask = np.ones((instances, 1, 1, 1))
images = (image * mask)
# Run query with the original image
query = (image == colors).all(axis=3)
# For every image, color the shape white, everything else black
images[query] = [255,255,255]
images[np.logical_not(query)] = [0,0,0]
Well, I'm working with image processing to identify the color variation of an image and to be able to plot that data in a histogram. For this, I use images of skin spots in the RGB color space. The code below I can get the colors of each pixel and convert to HSV using color.rgb2lab. But as I want to convert to L*a*b*, because it is closer to human vision, in the python library there is no conversion to L*a*b*. With this, through the separated pixels of RGB, how do I transform these pixels into LAB colors?
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
import colorsys
from PIL import Image
# (1) Import the file to be analyzed!
img_file = Image.open("IMD006.png")
img = img_file.load()
# (2) Get image width & height in pixels
[xs, ys] = img_file.size
max_intensity = 100
hues = {}
# (3) Examine each pixel in the image file
for x in xrange(0, xs):
for y in xrange(0, ys):
# (4) Get the RGB color of the pixel
[r, g, b] = img[x, y]
# (5) Normalize pixel color values
r /= 255.0
g /= 255.0
b /= 255.0
# (6) Convert RGB color to HSV
[h, s, v] = colorsys.rgb_to_hsv(r, g, b)
# (7) Marginalize s; count how many pixels have matching (h, v)
if h not in hues:
hues[h] = {}
if v not in hues[h]:
hues[h][v] = 1
else:
if hues[h][v] < max_intensity:
hues[h][v] += 1
You can do it with PIL/Pillow using the built-in Colour Management System and building a transform like this:
#!/usr/local/bin/python3
import numpy as np
from PIL import Image, ImageCms
# Open image and discard alpha channel which makes wheel round rather than square
im = Image.open('colorwheel.png').convert('RGB')
# Convert to Lab colourspace
srgb_p = ImageCms.createProfile("sRGB")
lab_p = ImageCms.createProfile("LAB")
rgb2lab = ImageCms.buildTransformFromOpenProfiles(srgb_p, lab_p, "RGB", "LAB")
Lab = ImageCms.applyTransform(im, rgb2lab)
And Lab is now your image in Lab colourspace. If you carry on and add the following lines to the end of the above code, you can split the Lab image into its constituent channels and save them each as greyscale images for checking.
# Split into constituent channels so we can save 3 separate greyscales
L, a, b = Lab.split()
L.save('L.png')
a.save('a.png')
b.save('b.png')
So, if you start with this image:
you will get this as the L channel:
this as the a channel:
and this the b channel:
Being non-scientific for a moment, the a channel should be negative/low where the image is green and should be high/positive where the image is magenta so it looks correct. And the b channel should be negative/low where the image is blue and high/positive where it is yellow, so that looks pretty good to me! As regards the L channel, the RGB to greyscale formula is (off the top of my head) something like:
L = 0.2*R + 0.7*G + 0.1*B
So you would expect the L channel to be much brighter where the image is green, and darkest where it is blue.
Alternatively, you can do it with the scikit-image module, maybe even more simply like this:
import numpy as np
from skimage import color, io
# Open image and make Numpy arrays 'rgb' and 'Lab'
rgb = io.imread('image.png')
Lab = color.rgb2lab(rgb)
I am not 100% sure of the scaling, but I suspect the L channel is a float in range 0..100, and that a and b are also floats in range -128..+128, though I may be wrong!
With my colour wheel image above I got the following minima/maxima for each channel:
Lab[:,:,0].min() # L min
32.29567256501352
Lab[:,:,0].max() # L max
97.13950703971322
Lab[:,:,1].min() # a min
-86.18302974439501
Lab[:,:,1].max() # a max
98.23305386311316
Lab[:,:,2].min() # b min
-107.85730020669489
Lab[:,:,2].max() # b max
94.47812227647823
from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
def rgb_to_cielab(a):
"""
a is a pixel with RGB coloring
"""
a1,a2,a3 = a/255
color1_rgb = sRGBColor(a1, a2, a3);
color1_lab = convert_color(color1_rgb, LabColor);
return color1_lab
rgb_to_cielab(np.array([255,0,255]))
Output: LabColor(lab_l=60.32364943499053,lab_a=98.23532017664644,lab_b=-60.83501679458592)
Using cv2 you can easily implement this conversion. RGB->LAB, LAB->RGB.
import numpy as np
import cv2
img = cv2.imread('1.jpg')
LAB = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
cv2.imwrite('L.png', LAB[:,:,0])
cv2.imwrite('a.png', LAB[:,:,1])
cv2.imwrite('b.png', LAB[:,:,2])
BGR = cv2.cvtColor(LAB, cv2.COLOR_LAB2BGR)
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
cv2.imwrite('new.png', BGR)
I've seen this problem like you 'bout for 3 months and here is my solution for this
import numpy as np
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt
import colorsys
from PIL import Image
from past.builtins import xrange
img_file = Image.open("F:/coding/Project/FDD/neo5.png")
img = img_file.load()
[xs, ys] = img_file.size
max_intensity = 100
hues = {}
for x in xrange(0, xs):
for y in xrange(0, ys):
[r, g, b] = img[x, y]
r /= 255.0
g /= 255.0
b /= 255.0
[h, s, v] = colorsys.rgb_to_hsv(r, g, b)
if h not in hues:
hues[h] = {}
if v not in hues[h]:
hues[h][v] = 1
else:
if hues[h][v] < max_intensity:
hues[h][v] += 1
h_ = []
v_ = []
i = []
colours = []
for h in hues:
for v in hues[h]:
h_.append(h)
v_.append(v)
i.append(hues[h][v])
[r, g, b] = colorsys.hsv_to_rgb(h, 1, v)
colours.append([r, g, b])
fig = plt.figure()
ax = p3.Axes3D(fig)
ax.scatter(h_, v_, i, s=5, c=colours, lw=0)
ax.set_xlabel('Hue')
ax.set_ylabel('Value')
ax.set_zlabel('Intensity')
fig.add_axes(ax)
plt.show()
I have a set of images that are located in 3 separate folders, based on their Type. I want to iterate through every Type and count the red pixel values of every image. I have set a limit for red, being in range from 200 to 256. I want to create histograms for each type and later cluster the histogram and discriminate between the 3 classes. My experience with Python is very limited and I am stuck at how to isolate and count the red pixel values. I have attached my code and the resulting histogram for Type 1, which is a straight line. Could someone help on this?
import numpy as np
import cv2
import os.path
import glob
import matplotlib.pyplot as plt
## take the image, compute sum of all row colors and return the percentage
#iterate through every Type
for t in [1]:
#load_files
files = glob.glob(os.path.join("..", "data", "train", "Type_{}".format(t), "*.jpg"))
no_files = len(files)
#iterate and read
for n, file in enumerate(files):
try:
image = cv2.imread(file)
hist = cv2.calcHist([img], [0], None, [56], [200, 256])
print(file, t, "-files left", no_files - n)
except Exception as e:
print(e)
print(file)
plt.plot(hist)
plt.show()
This is the solution I came up with. I have taken the liberty to refactor and simplify your code a bit.
import os
import glob
import numpy as np
import matplotlib.pyplot as plt
from skimage import io
root = 'C:\Users\you\imgs' # Change this appropriately
folders = ['Type_1', 'Type_2', 'Type_3']
extension = '*.bmp' # Change if necessary
threshold = 150 # Adjust to fit your neeeds
n_bins = 5 # Tune these values to customize the plot
width = 2.
colors = ['cyan', 'magenta', 'yellow']
edges = np.linspace(0, 100, n_bins+1)
centers = .5*(edges[:-1]+ edges[1:])
# This is just a convenience class used to encapsulate data
class img_type(object):
def __init__(self, folder, color):
self.folder = folder
self.percents = []
self.color = color
lst = [img_type(f, c) for f, c in zip(folders, colors)]
fig, ax = plt.subplots()
for n, obj in enumerate(lst):
filenames = glob.glob(os.path.join(root, obj.folder, extension))
for fn in filenames:
img = io.imread(fn)
red = img[:, :, 0]
obj.percents.append(100.*np.sum(red >= threshold)/red.size)
h, _ = np.histogram(obj.percents, bins=edges)
h = np.float64(h)
h /= h.sum()
h *= 100.
ax.bar(centers + (n - .5*len(lst))*width, h, width, color=obj.color)
ax.legend(folders)
ax.set_xlabel('% of pixels whose red component is >= threshold')
ax.set_ylabel('% of images')
plt.show()
Notice that I have I used scikit-image rather than OpenCV to read the images. If this is not an option for you, insert import cv2 and change:
img = io.imread(fn)
red = img[:, :, 0]
to:
img = cv2.imread(fn)
red = img[:, :, 2]