Creating a gray-scale image from an 2D array of pixel values - python

I am trying to change the values from a gray scale PNG image and then create an image from those changed values.
The first step I took was using a python implementation of libpng.
With that I was able to get a list of all the pixel values of the PNG. It is a gray scale image so the values where from 0 to 100. I do a quick algorithm to change the values. And then I try to create it into a new PNG file. Its all on a single line so I use regex to format it into a 2D array.
I attempted to use this that I found here
from PIL import Image
import numpy as np
pixels = [[100,0,0],[0,100,0],[0,0,100]]
# Convert the pixels into an array using numpy
array = np.array(pixels)
print(array)
# Use PIL to create an image from the new array of pixels
new_image = Image.fromarray(array, 'L')
new_image.save('testing.png')
But I guess the formatting that PIL uses is different from what the libpng is so instead of making an image that looks like 3 white pixels diagonally, I only get 1 white pixel on the top left. So either I change the values I am getting from libpng so that numpy works, or I find something in libpng that will allow me to change the values directly and create the new file.

The error in your code is that you are not setting the right data type for the array. If you call array = np.array(pixels, dtype='uint8') then your code will work.

Related

Displaying Tiff images in ipynb or jupyter notebook

I am trying to visualize a tiff image in an ipython notebook using the following code, import statements are ignored for clarity purposes.
from PIL import Image
orig_img_path = os.path.join("path/to/tiff/0.tiff")
img_orig = Image.open(orig_img_path,"r")
plt.imshow(img_orig)
plt.show()
The above snippet just shows me the following black image -
I know that the image pixel values are non-zero since I can open the original images on my MacBook and they look like the following -
I also double check that the pixel values are nonzero by using the following code of casting the PIL image to an np array using the following code and printing the array out -
img_arr = np.array(img_orig)
print(img_arr)
which gives me the following output -
I think I know what the issue is as well - that the matplotlib pyplot thinks that this is a PNG image or something like that and as we can see the pixel values are very small to be perceivable by the human eyes. I mean I can simply multiply the image by 255 which gives the following image as expected, although scaling by 255 is not entirely right since the pixel values need to be normalized for the minimum to corrspond to 0 and max to 255 assuming mat plot lib thinks that it is showing a PNG image -
I would like to know if there is a better way of doing so rather than me manually doing it all the time, any native way of displaying tiff in a Jupyter/ipython notebook which preserves all the good properties of tiff images such as floating point numbers etc.
Thanks and please let me know if anything is unclear.
Edit 1: Link to the original file is here - https://drive.google.com/file/d/1O1-QM6aeU5-QZhT36vOMjDND2vkZNgqB/view?usp=sharing
-- Megh
If you share your original image in .tiff format the solution might be more precise.
You are reading the image pixels as type float: 0-1, and after that, you parse them as uint8 : 0-255 which will turn all pixels values into 0 or 1: Black or almost Black
You can try the following approach to read your image (supposedly Black and White) and parse it:
import cv2
gray = cv2.imread("path/to/tiff/0.tiff", cv2.IMREAD_UNCHANGED)
cv2.namedWindow("MyImage", cv2.WINDOW_NORMAL)
cv2.imshow("MyImage", gray)
cv2.waitKey(0)
What is the range of values that you expect in that image? Did you do any preprocessing to it?
The image you are trying to display in matplotlib contains negative values. Visualizing works well for any kind of uint8 data (Grayscale and RGB) and data in the range of 0 - 1. Your displaying issue can be addressed by adding the min value of the image and then dividing by the max value (effectively normalizing your data to the range 0-1).
[...]
img = np.array(img_orig, dtype=float)
img += abs(np.min(img))
img /= np.max(img)
plt.imshow(img)
plt.show()

Python PIL read/open TIFF is black only

I try to read a TIFF file with pillow/PIL (7.2.0) in Python (3.8.3), e.g. this image.
The resulting file seems to be corrupted:
from PIL import Image
import numpy as np
myimage = Image.open('moon.tif')
myimage.mode
# 'L'
myimage.format
# 'TIFF'
myimage.size
# (358, 537)
# so far all good, but:
np.array(myimage)
# shows only zeros in the array, likewise
np.array(myimage).sum()
# 0
It doesn't seem to be a problem of the conversion to numpy array only, since if I save it to a jpg (myimage.save('moon.jpg')) the resulting jpg image has the appropriate dimensions but is all black, too.
Where did I do wrong or is it a bug?
I am not an expert in coding but i had same problem and found the TIFF file has 4 layers. R, G ,B and Alpha. When you convert it using PIL it is black.
try to view the image as plt.imshow(myimage[:, :, 0])
you could also remove the Alpha layer by saving the read image ( i used plt.imread('image')) and then saving it as image=image[:,:,3]. Now its a RGB image.
I don't know if i answered your question, but i felt this info might be of help.

How to change the grey scale value of a region in an image?

I am new to Python and not really sure how to attack this problem.
What I am trying to do is to take a black and white image and change the value of the edge (x pixels thick) from 255 to some other greyscale value.
I need to do this to a set of png images inside of a folder. All images will be geometric (mostly a combination of straight lines) no crazy curves or patterns. Using Python 3.
Please check the images.
A typical file will look like this:
https://drive.google.com/open?id=13ls1pikNsO7ZbsHatC6cOr4O6Fj0MPOZ
I think this is what you want. The comments should explain pretty well what I going on:
#!/usr/bin/env python3
import numpy as np
from PIL import Image, ImageFilter
from skimage.morphology import dilation, square
# Open input image and ensure it is greyscale
image = Image.open('XYbase.png').convert('L')
# Find the edges
edges = image.filter(ImageFilter.FIND_EDGES)
# Convert edges to Numpy array and dilate (fatten) with our square structuring element
selem = square(6)
fatedges = dilation(np.array(edges),selem)
# Make Numpy version of our original image and set all fatedges to brightness 128
imnp = np.array(image)
imnp[np.nonzero(fatedges)] = 128
# Convert Numpy image back to PIL image and save
Image.fromarray(imnp).save('result.png')
So, if I start with this image:
The (intermediate) edges look like this:
And I get this as the result:
If you want the outlines fatter/thinner, increase/decrease the 6 in:
selem = square(6)
If you want the outlines lighter/darker, increase/decrease the 128 in:
imnp[np.nonzero(fatedges)] = 128
Keywords: image, image processing, fatten, thicken, outline, trace, edge, highlight, Numpy, PIL, Pillow, edge, edges, morphology, structuring element, skimage, scikit-image, erode, erosion, dilate, dilation.
I can interpret your question in a much simpler way, so I thought I'd answer that simpler question too. Maybe you already have a grey-ish edge around your shapes (like the Google drive files you shared) and just want to change all pixels that are neither black nor white into a different colour - and the fact that they are edges is irrelevant. That is much easier:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
# Open input image and ensure it is greyscale
image = Image.open('XYBase.png').convert('L')
# Make Numpy version
imnp = np.array(image)
# Set all pixels that are neither black nor white to 220
imnp[(imnp>0) & (imnp<255)] = 220
# Convert Numpy image back to PIL image and save
Image.fromarray(imnp).save('result.png')

Saving an array of pixel values into a text file

I have an array of pixel values from a greyscale image. I want to export these values into a text file or CSV. I have been trying to to this using various function including: xlsxwrite, write, CSV but I have so far been unsuccessful in doing this. This is what I am working with:
from PIL import Image
import numpy as np
import sys
# load the original image
img_file = Image.open("seismic.png")
img_file.show()
# get original image parameters...
width, height = img_file.size
format = img_file.format
mode = img_file.mode
# Make image Greyscale
img_grey = img_file.convert('L')
img_grey.save('result.png')
img_grey.show()
# Save Greyscale values
value = img_grey.load()
Essentially I would like to save 'value' to use elsewhere.
Instead of using the .load() method, you could use this (with 'x' as your grey-scale image):
value = np.asarray(x.getdata(),dtype=np.float64).reshape((x.size[1],x.size[0]))
This is from: Converting an RGB image to grayscale and manipulating the pixel data in python
Once you have done this, it is straightforward to save the array on file using numpy.savetxt
numpy.savetxt("img_pixels.csv", value, delimiter=',')
Note: x.load() yields an instance of a Pixel Access class which cannot be manipulated using the csv writers you mentioned. The methods available for extracting and manipulating individual pixels in a Pixel Access class can be found here: http://pillow.readthedocs.io/en/3.1.x/reference/PixelAccess.html

convert greyscale image back to vector

I have a list called w (size: 784), which I outputted to a png greyscale image:
import matplotlib.pyplot as plt
tmp = 1/(1+np.exp(-10*w/w.max()))
plt.imshow(tmp.reshape(28,28),cmap="gray")
plt.draw()
plt.savefig("final_weight_vector")
Now I want to read the png image back to be a vector.
The solutions I found so far:
First:
import matplotlib.image as mpimg
img=mpimg.imread('final_weight_vector.png')
but img appears to not be greyscale, because its dimensions turend out to be (600, 800, 4).
Second:
reading the file as RGB and converting to greyscale:
im = Image.open('final_weight_vector.png').convert('LA')
However, I couldn't find how to iterate over im so I have no idea as to what's inside. Further, I am not sure the output of im will have the exact same values as the original w.
Help please?
The problem is that what you saved is probably a plot of the 28x28 image, not the image itself.
To be sure, please preview the image. I bet it is 600x800, not 28x28. I also suppose it contains many additional elements, like axes and padding.
If you want to store your array in a loadable format, you may use numpy.save() (and numpy.load() to load it).
You may also use PIL to save your array as image (e.g. using something similar to: http://code.activestate.com/recipes/577591-conversion-of-pil-image-and-numpy-array/)

Categories