The order of list elements seems to be changing - python-2.7

I am trying to make a little script to switch the rgb values of an image. The code I have works to alter the image, but the list of images is out of order when compared to the list of permutations. I have been unable to figure out why the images at indices 3 and 4 are switched in order.
from skimage import io
import numpy as np
import itertools
def create_permutations_auto(image):
im_col = np.split(image, 3, axis = -1)
color_keys = ["red", "green", "blue"]
colors = dict(zip(color_keys, im_col))
cp = list(itertools.permutations(color_keys))
im_list = []
for perm in cp:
color_data = [colors.get(k) for k in perm]
image = np.squeeze(np.stack(color_data, axis = -1))
im_list.append(image)
return cp, im_list
orig = io.imread(filename)
text, image_perms = create_permutations_auto(orig)
for i in range(len(image_perms)):
print text[i]
io.imshow(image_perms[i])
io.imsave(filepath + "{}_{}_{}.png".format(text[i][0], text[i][1], text[i][2]), image_perms[i])
io.show()
I would expect this code to output the original image with the green values replacing the red, the blue values replacing the green and the red values replacing the blue for the fourth image created. However, what I get is blue → red, red → green, and green → blue. The fifth image created seems like it should be the fourth.
Third Image (should be green, blue, red):
Fourth Image(should be blue, red, green):
To see if it was the something to do with the order of the dictionary, I tried to do the permutations manually. Using the following:
def create_permutations(image):
red, green, blue = np.split(image, 3, axis = -1)
perms = []
perms.append(np.squeeze(np.stack((red, blue, green), axis = -1)))
perms.append(np.squeeze(np.stack((green, red, blue), axis = -1)))
perms.append(np.squeeze(np.stack((green, blue, red), axis = -1)))
perms.append(np.squeeze(np.stack((blue, green, red), axis = -1)))
perms.append(np.squeeze(np.stack((blue, red, green), axis = -1)))
return perms
This code also seems to switch the placements of the two same permutations although it is the images at index 2 and 4. For this simple example, I can switch them around but it seems like I am missing something fundamental here. I am using a Python(x,y) distribution with Python 2.7.10 and numpy 1.12.1 on a machine running windows 10.

The effect of spectral permutation is different from that of spatial permutation. When you split the original image into its chromatic bands, referred to as red, green, and blue, and then you rearrange them as blue, red, and green, the order of the colors from left to right in the resulting image is green-blue-red (spectral permutation) rather than blue-red-green (spatial permutation).
The figure below is intended to schematically explain such a counter-intuitive result. For the sake of clarity let us denote the red, green, and blue chromatic channels with indices 0, 1, and 2, respectively. It clearly emerges from the figure that the intensities of the pixels in the leftmost region of the image are transformed through permutattion from [255, 0, 0] to [0, 255, 0], Indeed, in the leftmost region of the permuted image pixels' intesities are 0 for channel 0 (red component), 255 for channel 1 (green component), and 0 for channel 2 (blue component). That's the reason why the color of the leftmost region of the image changes from red to green. Similar arguments apply to the central and rightmost regions.
Bonus
You could simplify your function like this:
def create_permutations_auto(img):
colors = {0: 'red', 1: 'green', 2: 'blue'}
index_perm = list(itertools.permutations(colors.keys()))
cp = [tuple(colors[i] for i in perm) for perm in index_perm]
im_list = [np.dstack([img[:, :, i] for i in perm]) for perm in index_perm]
return cp, im_list

Related

Map a pixel color found in OpenCV to a pre-determined list of colors

I have a scenario where I have obtained one or more colors from an image, but now I need to determine which one of my existing color options it is closest to.
For example, I may have red(255,0,0), green(0,255,0) and blue(0,0,255) as my three choices, but the image may contain orange(255,165,0).
What I need then is a way to determine which one of those three values I should choose as my output color to replace orange.
One approach I have considered is to measure the range from those three values and see which one is the smallest & select that color.
Example:
orange -> red
abs(255 - 255) = 0, abs(165 - 0) = 165, abs(0 - 0) = 0
0 + 165 + 0 = 165
orange -> green
abs(255 - 0) = 255, abs(165 - 255) = 90, abs(0 - 0) = 0
255 + 90 + 0 = 345
orange -> blue
abs(255 - 0) = 255, abs(165 - 0) = 165, abs(0 - 255) = 255
255 + 165 + 255 = 675
Under this approach, I would pick red.
However, I am not sure if this is the best, or even a particularly valid, one so was wondering if there is something out there that is more accurate & would scale better to an increased color pallete.
Update The reduction answer linked in here does not help as it reduces things across the board. I need the ability to link a broad range of colors to several specific options.
I think you should represent and compare colors in different color space. I suggest space, that represent human color perception. Therefore L*a*b color space will be the best.
https://en.wikipedia.org/wiki/Lab_color_space/
Color distances in that coordinate space are represented by delta e value. You could find different standards for delta e below:
https://en.wikipedia.org/wiki/Color_difference#CIELAB_Delta_E.2A/
In order to change color space you have to use cv::cvtColor() method. Color conversion for single pixel is described below:
https://stackoverflow.com/a/35737319/8682088/
After calculating pixel coordinates in L*a*b space, you could easily calculate delta e and compare colors with any reference and pick the one with the smallest error.

algorithm that finds the "mean positioned" white pixel in a column and repeats the process for each column

I am attempting to create an algorithm that locates the white pixels in a column of a binary image, and then adds the y co-ordinates/column number of each white pixel and divides this value by the number of white pixels in the column, in order to get the "mean/middle positioned" white pixel in the column. And this returns an (x,y) co-ordinate that can be plotted. This process repeats for each column in the image and each time sy sets back to 0.
The end goal is instead of having a lines that are multiple pixels thick/wide, as shown in the image's numpy arraycurrent line multiple thicks wide array, I have lines that are just one pixel wide, whilst mantaining the original shape. I planned on doing this by selecting the "mean positioned white pixel in each column". I will then use these pixels to obtain x and y co-ordinates to plot.
Here is what I have
sx = x = img.shape[1]
sy = 0
whitec = cv2.countNonZero(img.shape[1])
arrayOfMeanY = [] #array to place (x,y) co-ordinate in
#Select column to iterate
for x in range(img.shape[1]):
# iterating through individual items in the column
for y in range(img.shape[0]):
# Checking for white pixels
pixel = img[x,y]
if pixel == 255:
# Then we check the y values of the white pixels in the column and add them all up
sy = sy+y
whitec +=1
# Doing the calculation for the mean and putting it into the meanY list
sy = sy/whitec
y = sy
print img[x,y]
array.append(y)
cv2.waitKey(0)
# reset sy to 0 for the next column
sy = 0
My issue is I recieve this error when I run the code:
File "<ipython-input-6-e4c2225ff632>", line 27, in <module>
whitec = cv2.countNonZero(img.shape[1]) #n= number of white pixels
in the column
TypeError: src is not a numpy array, neither a scalar
How do I rectify this issue, and once once this issue is rectified will my coding do what I described above.
No need for loops here. With numpy you hardly ever need to loop over individual pixels.
Instead, create a function which takes the mean of the locations of the non-zero pixels for each column (I converted to np.intp to index the image; you could just cast with int() but np.intp is what Numpy uses for indexing arrays so, it's slightly more appropriate).
def avgWhiteLocOverCol(col):
return np.intp(np.mean(np.where(col)))
Then you can simply apply the function along all columns with np.apply_along_axis().
avgRows = np.apply_along_axis(avgWhiteLocOverCol, 0, img)
For example, let's create an image with white pixels on the middle row and on the diagonal:
import numpy as np
import cv2
img = np.eye(500)*255
img[249,:] = 255
cv2.imshow('',img)
cv2.waitKey(0)
Then we can apply the function over each column, which should give a line with half the slope:
def avgWhiteLocOverCol(col):
return int(np.mean(np.where(col)))
avgRows = np.apply_along_axis(avgWhiteLocOverCol, 0, img)
avgIndImg = np.zeros_like(img)
avgIndImg[avgRows,range(img.shape[1])] = 255
cv2.imshow('',avgIndImg)
cv2.waitKey(0)

Getting mean of an image using a mask

I have a series of concentric rectangles and wish to obtain the means of the outer rectangle excluding the inner rectangle. See the attached diagram , I need to get the mean for the shaded area.
So I am using a mask of the inner rectangle to pass into the cv2.mean method, but I am not sure how to set the mask. I have the following code:
for i in xrange(0,len(wins)-2,1):
means_1 = cv2.mean(wins[i])[0]
msk = cv2.bitwise_and(np.ones_like((wins[i+1]), np.uint8),np.zeros_like((wins[i]), np.uint8))
means_2 = cv2.mean(wins[i+1],mask=msk)
means_3 = cv2.mean(wins[i+1])[0]
print means_1,means_2,means_3
I get this error for the means_2 (means_3 works fine).:
error:
/Users/jenkins/miniconda/0/2.7/conda-bld/work/opencv-2.4.11/modules/core/src/arithm.cpp:1021:
error: (-209) The operation is neither 'array op array' (where arrays
have the same size and type), nor 'array op scalar', nor 'scalar op
array' in function binary_op
The mask here refers to a binary mask which has 0 as background and 255 as foreground, So You need to create an empty mask with default color = 0 and then paint the Region of Interest where you want to find the mean with 255. Suppose I have input image [512 x 512]:
Lets's assume 2 concentric rectangles as:
outer_rect = [100, 100, 400, 400] # top, left, bottom, right
inner_rect = [200, 200, 300, 300]
Now create the binary mask using these rectangles as:
mask = np.zeros(image.shape[:2], dtype=np.uint8)
cv2.rectangle(mask, (outer_rect[0], outer_rect[1]), (outer_rect[2], outer_rect[3]), 255, -1)
cv2.rectangle(mask, (inner_rect[0], inner_rect[1]), (inner_rect[2], inner_rect[3]), 0, -1)
Now you may call the cv2.mean() to get the mean of foreground area, labelled with 255 as:
lena_mean = cv2.mean(image, mask)
>>> (109.98813432835821, 96.60768656716418, 173.57567164179105, 0.0)
In Python/OpenCV or any software, if you have a masked image and the binary mask, then the mean of the non-black pixels in the image (i.e. ROI) is the mean of the masked image divided by the mean of the mask
Input:
Mask:
import cv2
import numpy as np
# load image
img = cv2.imread('lena_g.png', cv2.IMREAD_GRAYSCALE)
# load mask
mask = cv2.imread('lena_mask.png', cv2.IMREAD_GRAYSCALE)
# compute means
mean_img = np.mean(img)
mean_mask = np.mean(mask)
# compute 255*mean_img/mean_mask
mean_roi = 255 * mean_img / mean_mask
# print mean of each
print("mean of image:", mean_img)
print("mean of mask:", mean_mask)
print("mean of roi:", mean_roi)
mean of image: 98.50196838378906
mean of mask: 216.090087890625
mean of roi: 116.23856597522328

Python OpenCV get bottom most value of mask

I have an image from which I extract a colour into a mask as shown in the code below. The mask gives a black and white image. White being the colour I detect. The pixel value of white is 255 and black is 0.
I want to get the bottommost x and Y pixel of the white portion of the mask. How do I do this?
My code is as follows:
image = cv2.imread(FILENAME)
# THE COLOURS ARE IN RGB
lower_blue = np.array([50, 0, 0])
upper_blue = np.array([255, 50, 50])
# loop over the boundaries
# for (lower, upper) in boundaries:
# create NumPy arrays from the boundaries
lower = np.array(lower_blue, dtype = "uint8")
upper = np.array(upper_blue, dtype = "uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
you can use numpy's where to search your mask for a specific value:
np.max(np.where(np.max(img_binary,axis=1)==255)

Algorithm to convert film negative RGB to positive RGB

Assuming I have a photographic film negative scanned as an RGB image, I'm trying to find an algorithm that will convert the color values to an RGB positive.
Due to the orange bias ( http://photo.net/learn/orange-negative-mask ) if I simply say redPositive = 255 - redNegative I get a final image that has a strong cyan tint to it, and is very washed out. That means the answers given here: Convert negative image to positive are NOT correct.
So how would I craft the following routine:
struct RGB
{
unsigned byte red;
unsigned byte green;
unsigned byte blue;
};
void FilmNegativeToPositive(RGB const &negative, RGB &positive)
{
// What goes here?
}
I don’t have data to test, but according to the link you gave, the negative is a mixture of cyan, magenta and yellow dyes that are impure:
The yellow dye layer is the most pure. The magenta dye layer has a noticeable amount of yellow in it. The cyan dye layer has noticeable amounts of both yellow and magenta in it.
Therefore, you want to do something like this (untested pseudocode):
Let I_MY be the ratio of yellow impurity to pure magenta dye
Let I_CY be the ratio of yellow impurity to pure cyan dye
Let I_CM be the ratio of magenta impurity to pure cyan dye
Given R, G, B in [0, 255]
Convert to CMY:
C = 1.0 - R/255.0
M1 = 1.0 - G/255.0
Y1 = 1.0 - B/255.0
Calculate the impurities in the cyan dye and remove them, since we assume no other dye has cyan impurities:
M = M1 - I_CM×C
Y2 = Y1 - I_CY×C
Now the amount of magenta dye is correct, so subtract its yellow impurity:
Y = Y2 - I_MY×M
Convert the corrected CMY values back to RGB:
R' = 255×(1.0-C)
G' = 255×(1.0-M)
B' = 255×(1.0-Y)
If it turns out there’s more complicated contamination than that, you get a linear algebra problem:
[ 1 I_MC I_YC] [C'] [C]
[I_CM 1 I_YM] × [M'] = [M]
[I_CY I_MY 1] [Y'] [Y]
Where you want to solve for C', M', and Y', then convert back to the RGB color space.