Fancy indexing to replace the values of an N dimensional array? - python

I am currently working with a numpy array containing pixel color information of shape (width, height, 3).
I would like to be able to replace colors in the original array, with colors described in a new array and to do this WITHOUT loops if possible.
I have tried indexing it with fancy index's but was not quite sure how to index properly.
Optimally the function would like something like this:
>>>import numpy as np
>>>imgdat = np.random.randint(0, 255, size=(2, 2, 3))
>>>imgdat
[[[138 149 41]
[100 186 136]]
[[181 202 169]
[205 247 195]]]
>>>pixels = np.random.randint(0, imgdat.shape[0], size=(2, 2))
>>>pixels
[[1,0]
[0,1]]
>>>colors = np.random.randint(0, 255, size=(2, 3))
>>>colors
[[ 16 229 138]
[ 86 76 209]]
######apply the function#######
>>>filledimgdat = fillPixels(imgdat, pixels, colors)
>>>filledimgdat
[[[ 138 149 41]
[ 86 76 209]]
[[ 16 229 138]
[205 247 195]]]
EDIT:
Because my originally description was a little unclear, what I am trying to do is replace specific colors in imgdat at specific index's. If anybody can think of a better way to format the datatypes or handle the info to simplify an operation like this, that would also be welcome.

Related

Change pixels for improve contrast in picture

I have input image file with hidden text, problem is difference of pixels of hidden text is really small, sometimes only 1px. I want change pixels for see this text.
Because never working with something similar idea is convert image to numpy array and replace values by dict:
from PIL import Image
import matplotlib
img = Image.open('4YtCA.jpg')
data = np.array( img, dtype='uint8' )
#print (data)
a = np.ravel(data)
u, c = np.unique(a, return_counts=True)
print (u)
[ 48 49 50 51 77 78 79 80 100 101 102 103 121 122 123 124 142 143
144 145 164 165 166 167 188 189 190 191 208 209 210 211 212 230 231 232
233 253 254 255]
#new values for replace
new = (u.reshape(-1, 4) / [1,2,3,4]).astype(int)
print (new)
[[ 48 24 16 12]
[ 77 39 26 20]
[100 50 34 25]
[121 61 41 31]
[142 71 48 36]
[164 82 55 41]
[188 94 63 47]
[208 104 70 52]
[212 115 77 58]
[233 126 84 63]]
d = dict(zip(u, np.ravel(new)))
#print (d)
#https://stackoverflow.com/a/46868996
indexer = np.array([d.get(i, -1) for i in range(data.min(), data.max() + 1)])
out = indexer[(data - data.min())]
matplotlib.image.imsave('out.png', out.astype(np.uint8))
I think my solution is not nice, because last value are not seen very well. Is possible change pixels to some different colors like red, green, purple? Or change contract some better way? The best should be change each pixels some smart way, but not idea how.
Input image:
Output image:
You could try a histogram equalisation. I'll just do it with ImageMagick in the Terminal for now to demonstrate:
magick hidden.jpg -equalize -rotate -90 result.png
Or a "Local Adaptive Threshold" - see here:
magick hidden.jpg -lat 50x50 -rotate -90 result.png
If you are running v6 ImageMagick, replace magick with convert in the previous commands.
This is pretty equivalent in Python:
from PIL import Image
from skimage.filters import threshold_local
import numpy as np
# Open image in greyscale
im = Image.open('hidden.jpg').convert('L')
na = np.array(im)
# Local Adaptive Threshold
LAT = threshold_local(na, 49)
result = na > LAT
Image.fromarray((result*255).astype(np.uint8)).save('result.png')
If you really, really don't want to introduce a new dependency on skimage, you can use PIL or Numpy to generate a blurred copy of your image and subtract the blurred from the original and then threshold the difference image. That looks like this:
#!/usr/bin/env python3
from PIL import Image, ImageFilter
import numpy as np
# Open image in greyscale, and make heavily blurred copy
im = Image.open('hidden.jpg').convert('L')
blur = im.filter(ImageFilter.BoxBlur(25))
# Go to Numpy for maths!
na = np.array(im)
nb = np.array(blur)
# Local Adaptive Threshold
res = na >= nb
# Save
Image.fromarray((res*255).astype(np.uint8)).save('result.png')
from PIL import Image
import numpy as np
img = Image.open('4YtCA.jpg').convert('L')
data = np.array(img, dtype='uint8')
u, c = np.unique(data, return_counts=True)
# Set the background colors to white and the rest to black
#data = np.where(np.isin(data, u[c>17000]), 255, 0).astype(np.uint8)
data = np.isin(data, u[c>17000]).astype(np.uint8) * 255 # thanks to Mad Physicist
# Create new image and save
img_new = Image.fromarray(data)
img_new.save('4YtCA_new.jpg')

Apply Function foreach Pixel in Numpy Array

I have a function like:
def calcChromaFromPixel(red, green, blue):
r = int(red)
g = int(green)
b = int(blue)
return math.sqrt(math.pow(r - g, 2) +
math.pow(r - b, 2) +
math.pow(g - b, 2))
and I have an RGB Image, which is already converted into an numpy array with a shape like [width, height, 3], where 3 are the color channels.
What I want to do is to apply the method to every pixel and build the mean from the result. I already have done the obvious thing and iterated over the array with two loops, but that seems to be a really slow thing to do... Is there a faster and prettier way to do that?!
Thanks :)
Code:
import math
import numpy as np
np.random.seed(1)
# FAKE-DATA
img = np.random.randint(0,255,size=(4,4,3))
print(img)
# LOOP APPROACH
def calcChromaFromPixel(red, green, blue):
r = int(red)
g = int(green)
b = int(blue)
return math.sqrt(math.pow(r - g, 2) +
math.pow(r - b, 2) +
math.pow(g - b, 2))
bla = np.zeros(img.shape[:2])
for a in range(img.shape[0]):
for b in range(img.shape[1]):
bla[a,b] = calcChromaFromPixel(*img[a,b])
print('loop')
print(bla)
# VECTORIZED APPROACH
print('vectorized')
res = np.linalg.norm(np.stack(
(img[:,:,0] - img[:,:,1],
img[:,:,0] - img[:,:,2],
img[:,:,1] - img[:,:,2])), axis=0)
print(res)
Out:
[[[ 37 235 140]
[ 72 137 203]
[133 79 192]
[144 129 204]]
[[ 71 237 252]
[134 25 178]
[ 20 254 101]
[146 212 139]]
[[252 234 156]
[157 142 50]
[ 68 215 215]
[233 241 247]]
[[222 96 86]
[141 233 137]
[ 7 63 61]
[ 22 57 1]]]
loop
[[ 242.56545508 160.44313634 138.44132331 97.21111048]
[ 246.05283985 192.94040531 291.07730932 98.66103588]
[ 124.99599994 141.90842117 207.88939367 17.20465053]
[ 185.66636744 133.02631319 77.82030583 69.29646456]]
vectorized
[[ 242.56545508 160.44313634 138.44132331 97.21111048]
[ 246.05283985 192.94040531 291.07730932 98.66103588]
[ 124.99599994 141.90842117 207.88939367 17.20465053]
[ 185.66636744 133.02631319 77.82030583 69.29646456]]

subtracting RGB values from an Image in Python

I'm working in a project where I need to subtract the RGB values from an Image. In example I want to subtract the BLUE channel from RED, so RED gets the difference value of the subtraction.
I have the next properties of the image:
Dimension:1456x2592,
bpp:3
The image I'm using gives me the following arrays:
[[[ 63 58 60]
[ 63 58 60]
[ 64 59 61]
...,
[155 155 161]
[155 155 161]
[155 155 161]]
[[ 58 53 55]
[ 60 55 57]
[ 62 57 59]
...,
[157 157 163]
[157 157 163]
[158 158 164]]
I know those are the values(RGB) from the image, so now I move on to do the code (I based on this code)
import cv2
import numpy as np
from PIL import Image
# read image into matrix.
m = cv2.imread("ITESO.jpeg")
# get image properties.
h,w,bpp = np.shape(m)
# iterate over the entire image.
# BLUE = 0, GREEN = 1, RED = 2.
for py in range(0,h):
for px in range(0,w):
#m[py][px][2] = 2
n = m[py][px][2] //n takes the value of RED
Y = [n, 0, 0] //I create an array with [RED, 0, 0]
m, Y = np.array(m), np.array(Y)
m = np.absolute(m - Y) //Get the matriz with the substraction
y = 1
x = 1
print (m)
print (m[x][y])
#display image
#cv2.imshow('matrix', m)
#cv2.waitKey(0)
cv2.imwrite('new.jpeg',m)
img = Image.open('new.jpeg')
img.show()
img = Image.open('new.jpeg').convert('L')
img.save('new_gray_scale.jpg')
img.show()
When I print the J matrix it gives the following arrays:
B,G,R
Blue = BLUE - RED
[[[ 3 58 60]
[ 3 58 60]
[ 4 59 61]
...,
[ 95 155 161]
[ 95 155 161]
[ 95 155 161]]
[[ 2 53 55]
[ 0 55 57]
[ 2 57 59]
...,
[ 97 157 163]
[ 97 157 163]
[ 98 158 164]]
But I'm not able to open the new image and if I set one RGB channel to one value it shows me the image. I use the next lines for that:
import cv2
import numpy as np
# read image into matrix.
m = cv2.imread("python.png")
# get image properties.
h,w,bpp = np.shape(m)
# iterate over the entire image.
for py in range(0,h):
for px in range(0,w):
m[py][px][0] = 0 //setting channel Blue to values of 0
# display image
cv2.imshow('matrix', m)
cv2.waitKey(0)
How can I subtract the RGB channels from each other?
PS: In MatLab it works like a charm, but I'm not able to do it in python.
Pay attention that this operation is changing the dtype of the matrix (image) from uint8 to int32, and this can cause other problems. A better way (and more efficient) to do this, IMO, is this:
import cv2
import numpy as np
img = cv2.imread('image.png').astype(np.float) # BGR, float
img[:, :, 2] = np.absolute(img[:, :, 2] - img[:, :, 0]) # R = |R - B|
img = img.astype(np.uint8) # convert back to uint8
cv2.imwrite('new-image.png', img) # save the image
cv2.imshow('img', img)
cv2.waitKey()
Code manipulating RGB negative values to zero...
m = cv2.imread("img.jpg")
# get image properties.
h,w,bpp = np.shape(m)
# iterate over the entire image.
# BLUE = 0, GREEN = 1, RED = 2.
for py in range(0,h):
for px in range(0,w):
n = m[py][px][1]
Y = [0, 0, n]
m, Y = np.array(m), np.array(Y)
a = (m - Y)
if (a[py][px][0] <=0): #if Blue is negative or equal 0
a[py][px][0] = 0 #Blue set to 0
cv2.imwrite('img_R-G.jpg',a)
img = Image.open('img_R-G.jpg').convert('L')
img.save('img_R-G_GS.jpg')

Python numpy subtraction no negative numbers (4-6 gives 254)

I wish to subtract 2 gray human faces from each other to see the difference, but I encounter a problem that subtracting e.g. [4] - [6] gives [254] instead of [-2] (or difference: [2]).
print(type(face)) #<type 'numpy.ndarray'>
print(face.shape) #(270, 270)
print(type(nface)) #<type 'numpy.ndarray'>
print(nface.shape) #(270, 270)
#This is what I want to do:
sface = face - self.nface #or
sface = np.subtract(face, self.nface)
Both don't give negative numbers but instead subtract the rest after 0 from 255.
Output example of sface:
[[ 8 255 8 ..., 0 252 3]
[ 24 18 14 ..., 255 254 254]
[ 12 12 12 ..., 0 2 254]
...,
[245 245 251 ..., 160 163 176]
[249 249 252 ..., 157 163 172]
[253 251 247 ..., 155 159 173]]
My question:
How do I get sface to be an numpy.ndarray (270,270) with either negative values after subtracting or the difference between each point in face and nface? (So not numpy.setdiff1d, because this returns only 1 dimension instead of 270x270)
Working
From the answer of #ajcr I did the following (abs() for showing subtracted face):
face_16 = face.astype(np.int16)
nface_16 = nface.astype(np.int16)
sface_16 = np.subtract(face_16, nface_16)
sface_16 = abs(sface_16)
sface = sface_16.astype(np.int8)
It sounds like the dtype of the array is uint8. All the numbers will be interpreted as integers in the range 0-255. Here, -2 is equal to 256 - 2, hence the subtraction results in 254.
You need to recast the arrays to a dtype which supports negative integers, e.g. int16 like this ...
face = face.astype(np.int16)
...and then subtract.
This is a problem with your datatype in the numpy array. You have a uint8 inside it, which seems to wrap around
Have a look at nfac.dtype whichwill show it to you. You have to convert it prior to your calculation operation. Use numpy.ndarray.astype to convert it or have a look at In-place type conversion of a NumPy array

Inverting numpy array image which might be uint8, uint16,

Is there a prettier way to do this? Specifically, are these max values available through the numpy API? I haven't been able to find them in the API, although they are easily found here in the docs.
MAX_VALUES = {np.uint8: 255, np.uint16: 65535, np.uint32: 4294967295, \
np.uint64: 18446744073709551615}
try:
image = MAX_VALUES[image.dtype] - image
except KeyError:
raise ValueError, "Image must be array of unsigned integers."
Packages like PIL and cv2 provide convenient tools for inverting an image, but at this point in the code I have a numpy array -- more sophisticated analysis follows -- and I'd like to stick with numpy.
Try doing
image ^= MAX_VALUES[image.dtype]
By the way, you do not need to define MAX_VALUES yourself. NumPy has them built-in:
import numpy as np
h, w = 100, 100
image = np.arange(h*w).reshape((h,w)).astype(np.uint8)
max_val = np.iinfo(image.dtype).max
print(max_val)
# 255
image ^= max_val
print(image)
# [[255 254 253 ..., 158 157 156]
# [155 154 153 ..., 58 57 56]
# [ 55 54 53 ..., 214 213 212]
# ...,
# [ 27 26 25 ..., 186 185 184]
# [183 182 181 ..., 86 85 84]
# [ 83 82 81 ..., 242 241 240]]

Categories