How to replace pixel value as a tensorflow operation? - python

I need to replace a pixel value in an image as an operation in the graph. Doing this beforehand is unfortunately not an option as it is part of an optimization process.
As a fix until I come up with a solution, I am simply using tf.py_func() but since this operation has to be executed a lot it's very slow and inefficient.
# numpy function to perturb a single pixel in an image
def perturb_image(pixel, img):
# At each pixel's x,y position, assign its rgb value
x_pos, y_pos, r, g, b = pixel
rgb = [r,g,b]
img[x_pos, y_pos] = rgb
return img
# pixel is a 1D tensor like [x-dim,y-dim,R,G,B]
# image is tensor with shape (x-dim,y-dim,3)
img_perturbed = tf.py_func(perturb_image,[pixel, image], tf.uint8)
One way I thought of is using tf.add(perturbation, image) where both have the same dimension and perturbation is all zeros except at the pixel location which needs its RGB-values changed to the same value as defined in pixel from the above code snippet. Unfortunately, I would need to rewrite a lot of code surrounding this operation which I am trying to avoid.
Can you think of a solution to replace py_func with another tensorflow operation using the same inputs?
Any help is much appreciated.

Related

Replacing greyscale pixel values with colors from sample PNG image

I am trying to apply colors from a gradient image to a grayscale (in RGB format i.e. R=G=B) one. For now the code looks at the R channel and uses that value to copy a color from a certain band of a 255 px tall gradient via the R channel value acting as the Y coordinate. As an example, a pixel in image 1 at (0,0) has a value of (0,0.0), the code should replace it with the color (53,18,106) from (10,0) in the second image (x is arbitrary here, my sample gradient is 100x255). Here's my code:
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlistmaster=[filename for filename in allfiles if filename[-4:] in [".png",".PNG"]]
imlistGradient=[filename for filename in imlistmaster if "grad" in filename]
imlistSample=[filename for filename in imlistmaster if "Sample" in filename]
# Get dimensions of images
w1,h1=Image.open(imlistSample[0]).size
N1=len(imlistSample)
w2,h2=Image.open(imlistGradient[0]).size
N2=len(imlistGradient)
#Create array based on gradient
for im in imlistGradient:
imarr2=numpy.array(Image.open(im),dtype=numpy.uint8)
pix2=Image.open(im).load()
# Convert grayscale to RGB values based on gradient
for im in imlistSample:
filename1 = os.path.basename(imlistSample[0])
pix1=Image.open(im).load()
for x in range(w1):
for y in range (h1):
color=pix1[x, y]
color=list(color)
colorvalue=color[0]
newcolor=pix2[10,colorvalue]
pix1=newcolor
image:
gradient:
(imgur because I can't embed yet)
When I run the code, color=pix1[x, y] throws "TypeError: tuple indices must be integers or slices, not tuple". Which is odd, as both x and y show up as integers in variable explorer and shouldn't Image.load explicitly takes 2 coordinates in the form of (x,y)? Also while looking around in the variable explorer it does look like at least one iteration worked as newcolor has the expected value of (53,18,106) from the gradient. Frankly I'm stumped
The culprit ended up being pix1=newcolor, changing to pix1[x,y]=newcolor solved the tuple problem. Odd that the error would identify the wrong line but oh well. This also explains the partial success, the value was being found and copied correctly and failing when being overwritten.

Convert Canny edge detection into np.array

I have written a function where I want to detect the edges of an image using the Canny algorithm. I then want to extract the 2d array of this image, and then flatten it into a 1d array.
def canny_detection(image):
# Convert to grayscale and convert the image to float
RGB = img_as_float(color.rgb2gray(image))
# Apply Canny edge detection algorithm
edge_canny = feature.canny(RGB, 3).astype(int)
#Get output array
canny_arr = np.array(edge_canny)
# Flatten output array
canny_flat = canny_arr.flatten()
return canny_flat
However, when I call the function with an example image, the output is just a huge array of 0s. I'm sure that's not right. I've tested the canny algorithm on the image, and the resulting image is correct. But the problem is when I want to get the vector of the image.
Can anyone help with this?
I suspect problem might be in that line:
edge_canny = feature.canny(RGB, 3).astype(int)
please replace it with
edge_canny = feature.canny(RGB, 3)
print(edge_canny)
edge_canny = edge_canny.astype(int)
print(edge_canny)
and check what it prints, if first is some nonzero float values <1.0 and second is 0s, that probably means that feature.canny produces values from 0.0 to 1.0 and then you lose it converting to int.
EDIT: Fixed my code.

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

How to resize an image faster in python

I have an image i.e an array of pixel values, lets say 5000x5000 (this is the typical size). Now I want to expand it by 2 times to 10kx10k. The value of (0,0) pixel value goes to (0,0), (0,1), (1,0), (1,1) in the expanded image.
After that I am rotating the expanded image using scipy.interpolate.rotate (I believe there is no faster way than this given the size of my array)
Next I have to again resize this 10kx10k array to original size i.e. 5kx5k. To do this I have to take the average pixel values of (0,0), (0,1), (1,0), (1,1) in the expanded image and put them in (0,0) of the new image.
However it turns out that this whole thing is an expensive procedure an takes a lot of time given the size of my array. Is there a faster way to do it?
I am using the following code to expand the original image
#Assume the original image is already given
largeImg=np.zeros((10000,10000), dtype=np.float32)
for j in range(5000):
for k in range(5000):
pixel_value=original_img[j][k]
for x in range((2*k), (2*(k+1))):
for y in range((2*j), (2*(j+1))):
largeImg[y][x] = pixel_value
A similar method is used to reduce the image to original size after rotation.
In numpy you can use repeat:
large_img = original_img.repeat(2, axis=1).repeat(2, axis=0)
and
final_img = 0.25 * rotated_img.reshape(5000,2,5000,2).sum(axis=(3,1))
or use scipy.ndimage.zoom. this can give you smoother results than the numpy methods.
there is a nice library that probably has all the functions you need for handling images, including rotate:
http://scikit-image.org/docs/dev/api/skimage.transform.html#skimage.transform.rotate

Python - matplotlib - imshow - How to influence displayed value of unzoomed image

I need to search outliers in more or less homogeneous images representing some physical array. The images have a resolution which is much higher than the screen resolution. Thus every pixel on screen originates from a block of image pixels. Is there the possibility to customize the algorithm which calculates the displayed value for such a block? Especially the possibility to either use the lowest or the highest value would be helpful.
Thanks in advance
Scipy provides several such filters. To get a new image (new) whose pixels are the maximum/minimum over a w*w block of an original image (img), you can use:
new = scipy.ndimage.filters.maximum_filter(img, w)
new = scipy.ndimage.filters.minimum_filter(img, w)
scipy.ndimage.filters has several other filters available.
If the standard filters don't fit your requirements, you can roll your own. To get you started here is an example that shows how to get the minimum in each block in the image. This function reduces the size of the full image (img) by a factor of w in each direction. It returns a smaller image (new) in which each pixel is the minimum pixel in a w*w block of pixels from the original image. The function assumes the image is in a numpy array:
import numpy as np
def condense(img, w):
new = np.zeros((img.shape[0]/w, img.shape[1]/w))
for i in range(0, img.shape[1]//w):
col1 = i * w
new[:, i] = img[:, col1:col1+w].reshape(-1, w*w).min(1)
return new
If you wanted the maximum, replace min with max.
For the condense function to work well, the size of the full image must be a multiple of w in each direction. The handling of non-square blocks or images that don't divide exactly is left as an exercise for the reader.

Categories