Inverting numpy array image which might be uint8, uint16, - python

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]]

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')

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

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.

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]]

OpenCV_Python: Lightness Channel Manipulation, Trying to get same brightness to all images

I am hoping to fix my uneven brightness/lightness of all my images(Hoping to get all brightness).
After getting the difference in lightness channel for my loop images to my reference images. I add the difference and save it to new images...however after checking the new images, I realised I still gotten uneven brightness...Is there anything wrong with my coding??? Any help or correction is appreciated. I have tried this code on both LAB and HSV colorspace, still the same. Below is the code and a couple of result that I got.
from PIL import Image
import numpy as np
import cv2
path = 'R:\\Temp\\zzzz\\AlignedPhoto_in_PNG\\'
path1 = 'R:\\Temp\\zzzz\\Testing1\\'
img = cv2.imread(path + 'aligned_IMG_1770.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a = np.mean(img[:,:,0])
for i in range (1770,1869):
img1 = cv2.imread(path + 'aligned_IMG_%d.png'%(i))
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2LAB)
img1[:,:,0], img1[:,:,1], img1[:,:,2] = cv2.split(img1)
print(img1[:,:,0])
b = np.mean(img1[:,:,0])
diff= b-a
print(diff)
img1[:,:,0] = img1[:,:,0] + diff
img1 = cv2.merge([img1[:,:,0], img1[:,:,1], img1[:,:,2]])
print(img1[:,:,0])
img1 = cv2.cvtColor(img1, cv2.COLOR_LAB2BGR)
cv2.imwrite(path1 + 'Testing1_%d.png'%(i), img1)
Also, any guidance on how I can edit the existing code to make sure after adding the difference, the new value does not exceed the max/min range of Lightness Channel in LAB or the max/min range of Value Channel in HSV? I realised after addition if the new value is >255 , the value jump to starting counting from 1. I googled around on how to fix this or set the range but I dun understand how to do it
Below is a few images result I got from above code. Hopefully that help to identify what went wrong with my code as I am still getting uneven brightness for the new images after adding the difference.
[[ 39 39 39 ..., 38 38 36]
[ 39 38 39 ..., 39 39 39]
[ 40 40 40 ..., 39 39 39]
...,
[119 119 122 ..., 165 166 167]
[118 118 120 ..., 169 166 166]
[115 116 117 ..., 175 169 167]]
0.0
[[ 39 39 39 ..., 38 38 36]
[ 39 38 39 ..., 39 39 39]
[ 40 40 40 ..., 39 39 39]
...,
[119 119 122 ..., 165 166 167]
[118 118 120 ..., 169 166 166]
[115 116 117 ..., 175 169 167]]
[[ 0 0 0 ..., 0 0 0]
[ 0 0 0 ..., 0 0 0]
[ 0 0 0 ..., 0 0 0]
...,
[117 119 119 ..., 165 163 131]
[117 117 118 ..., 170 166 131]
[115 116 116 ..., 176 171 134]]
-1.48181156101
[[255 255 255 ..., 255 255 255]
[255 255 255 ..., 255 255 255]
[255 255 255 ..., 255 255 255]
...,
[115 117 117 ..., 163 161 129]
[115 115 116 ..., 168 164 129]
[113 114 114 ..., 174 169 132]]
[[ 0 0 0 ..., 0 0 0]
[ 0 0 0 ..., 0 0 0]
[ 0 0 0 ..., 0 0 0]
...,
[ 0 97 115 ..., 165 164 165]
[ 0 96 114 ..., 169 166 164]
[ 0 95 113 ..., 175 170 166]]
-3.69765536832
[[253 253 253 ..., 253 253 253]
[253 253 253 ..., 253 253 253]
[253 253 253 ..., 253 253 253]
...,
[253 93 111 ..., 161 160 161]
[253 92 110 ..., 165 162 160]
[253 91 109 ..., 171 166 162]]
That's why maths is a skill every programmer should have.
You correct your brightness by adding diff.
So if you want a to equal the sum of b and diff
a = b + diff
and you know a and be. then how do you get diff?
diff = a - b
not
diff = b - a
Otherwise you will make darker images darker and brighter images brighter instead of bringing them to your reference mean a...
Of course using a global offset will cause problems with pixels that exceed your value range. You have to work around this problem. Otherwise your new mean will be wrong.

How can I change a color channel in an image using numpy?

I have an image where some color channels in the pixels have a value of zero (ie 255, 146, 0). I want to be able to change any value equal to zero in the array to a different value, but I do not know how to access these values. Any help with this?
This is the image array:
[[[ 76 163 168]
[109 166 168]
[173 172 167]
...,
[ 83 182 144]
[ 78 172 134]
[ 82 150 131]]
[[ 51 151 168]
[ 99 157 171]
[173 195 159]
...,
[ 56 165 144]
[ 25 198 125]
[ 35 185 121]]
[[ 76 163 121]
[112 147 120]
[175 151 118]
...,
[ 57 162 159]
[ 36 185 132]
[ 32 194 97]]
...,
[[ 78 189 126]
[ 68 173 129]
[ 58 171 150]
...,
[ 41 188 163]
[ 34 176 126]
[ 35 176 102]]
[[131 155 161]
[101 141 161]
[ 42 151 177]
...,
[ 56 178 122]
[ 45 192 114]
[ 46 184 112]]
[[130 157 185]
[ 83 141 185]
[ 42 158 185]
...,
[ 63 187 88]
[ 45 194 102]
[ 45 184 129]]]
Use masking -
img[(img==zero_val).all(-1)] = new_val
, where zero_val is the zero color and new_val is the new color to be assigned at those places where we have zero colored pixels.
Sample run -
# Random image array
In [112]: img = np.random.randint(0,255,(4,5,3))
# Define sample zero valued and new valued arrays
In [113]: zero_val = [255,146,0]
...: new_val = [255,255,255]
...:
# Set two random points/pixels to be zero valued
In [114]: img[0,2] = zero_val
In [115]: img[2,3] = zero_val
# Use proposed approach
In [116]: img[(img==zero_val).all(-1)] = new_val
# Verify that new values have been assigned
In [117]: img[0,2]
Out[117]: array([255, 255, 255])
In [118]: img[2,3]
Out[118]: array([255, 255, 255])

Categories