Grayscale .png to numpy array - python-3.x

Indeed, this question has been answered many times. However, as I am not allowed to add a comment to an answer due to "too low" reputation, I would like to discuss the solution presented in the most comprehensive answer.
Wouldn't the solution:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt #Used in the comparison below
im = Image.open('file.png').convert('RGB') #Opens a picture in grayscale
pic = np.array(im)
im.close()
work properly? I am wondering whether unacceptable changes in the quality occur. I have noticed some differences (i.e. black rows at the top in plt.imshow()) when I display the image:
im.show() #Before closing
plt.imshow(pic)
but I don't know whether they are only inevitable consequences of converting to np.array.
PS - If it is important, I would mention that I prepare the image for color quantization (KMeans) and Floyd dithering.
PPS - If you advised me how not to post duplicate question but discuss answers directly - it would be really appreciated.

Try it and see!
from PIL import Image
import numpy as np
# Other answer method
im1 = Image.open('gray.png').convert('L')
im1 = np.stack((im1,)*3, axis=-1)
# Your method
im2 = Image.open('gray.png').convert('RGB')
im2 = np.array(im2)
# Test if identical
print(np.array_equal(im1,im2))
Sample Output
True
I would say the one aspect that is different, is that the method in the other answer will work (insofar as it actually makes a greyscale image where R=G=B) even if the input image is colour, whereas your method will produce a colour image.

I was working on doing a similar thing, and I'm not sure why but I ran into a bunch of issues. In the end this worked for me pretty well without loss of any data.
from PIL import Image
import numpy as np
img=np.array(Image.open(filename).convert('L'))
and to convert back:
import imageio
array = array.astype(np.uint8)
imageio.imwrite(newfilename, array)
edit: this only works for black and white images. Color images need 3D arrays rather than 2D arrays

Related

Crop TIFF using JPG mask

I'm currently working on cloud removals from satellite data (I'm pretty new).
This is the image I'm working on (TIFF)
And this is the mask, where black pixels represent clouds (JPG)
I'm trying to remove the clouds from the TIFF, using the mask to identify the position of the cloud, and the cloudless image itself, like this (the area is the same, but the period is different):
I'm kindly ask how can I achieve that. A Python solution, with libraries like Rasterio or skimage is particularly appreciated.
Thanks in advance.
You can read the images with rasterio, PIL, OpenCV or tifffile, so I use OpenCV
import cv2
import numpy as np
# Load the 3 images
cloudy = cv2.imread('cloudy.png')
mask = cv2.imread('mask.jpg')
clear = cv2.imread('clear.png')
Then just use Numpy where() to choose whether you want the clear or cloudy image at each location according to the mask:
res = np.where(mask<128, clear, cloudy)
Note that if your mask was a single channel PNG rather than JPEG, or if it was read as greyscale like this:
mask = cv2.imread('mask.jpg', cv2.IMREAD_GRAYSCALE)
you would have to make it broadcastable to the 3 channels of the other two arrays by adding a new axis like this:
res = np.where(mask[...,np.newaxis]<128, clear, cloudy)

OpenCV - ArUco : detectMarkers failed identified some markers in a photos

I have pictures containing ArUco markers but I am unable to detect all of them with the detectMarkers function. Actually, I have many pictures : in some of them I can detect all the markers, in others I cannot and I don't really understand why.
I thought it was because of the quality of the photo, but it seems to be not so simple. Here's an example of my code :
import cv2
import matplotlib.pyplot as plt
from cv2 import aruco
aruco_dict = aruco.Dictionary_get(aruco.DICT_4X4_1000)
inputfile = 'EOS-1D-X-Mark-II_1201-ConvertImage.jpg'
frame = cv2.imread(inputfile)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
parameters = aruco.DetectorParameters_create()
corners, ids, rejectedImgPoints = aruco.detectMarkers(frame, aruco_dict, parameters=parameters)
frame_markers = aruco.drawDetectedMarkers(frame.copy(),rejectedImgPoints)
plt.figure(figsize=(20,10))
plt.imshow(frame_markers)
for i in range(len(ids)):
c = corners[i][0]
plt.plot([c[:, 0].mean()], [c[:, 1].mean()], "o", label = "id={0}".format(ids[i]))
plt.legend()
plt.show()
In this picture, 1 marker is not detected and I don't understand why.
I tried to tune the parameters of detectMarkers function manually with an interactive method thanks to jupyter notebook. There are many parameters and I found nothing that really helped me, except in some photos the reduction of polygonalApproxAccuracyRate.
The photo is orginally in 5472 x 3648 pixels but the one I send in this post is 2189 x 1459 pixels. Note that it doesn't work with the better resolution neither. Actually, I found in some photos that reducing the resolution help to detect the markers ... It's a contradiction but I think this is because the default parameters of the function are not adapted to my pictures, but I found no solution when tuning the parameters.
Another idea is to use the refineDetectMarkers function after calling detectMarkers. It uses the candidates that were found in detectMarkers but failed to be identified, and try to refine their identification. However, as far as I understood, I need to know where my markers should be in the picture and put it in refineDetectMarkers (as a board). In my situation, I don't know where the markers should be, otherwise I wouldn't take photos. The photos are used to observe precisely the evolution of their positions.
I am interested in any ideas you may have, thanks for reading !

cv2.cvtColor(img,cv2.COLOR_BGR2RGB) not working

I am trying to create a screen recorder using mss and Opencv in python, the video I am capturing has a very different colours than original computer screen. I tried to find the solution online, Everyone saying it should be fixed using cvtColor() but I already have it in my code.
import cv2
from PIL import Image
import numpy as np
from mss import mss
import threading
from datetime import datetime
`
def thread_recording():
fourcc=cv2.VideoWriter_fourcc(*'mp4v')
#fourcc=cv2.VideoWriter_fourcc(*'XVID')
out=cv2.VideoWriter(vid_file,fourcc,50,(width,height))
mon = {"top": 0, "left": 0, "width":width, "height":height}
sct = mss()
thread1=threading.Thread(target=record,args=(mon,out,sct))
thread1.start()
def record(mon,out,sct):
global recording
recording=True
while recording:
frame= np.array(sct.grab(mon))
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
out.release()
the vid_file variable contains a string of output file name with mp4 extension
Screenshot of my screen
Screenshot from recorded video
So, I looked around some more and found that apparently this is a bug in opencv for versions 3.x on wards.then I tried PIL for getting rgb image and removed cvtColor(),but it produced an empty video.I removed both cvtColor() as well as PIL Image as suggested by #ZdaR it again wrote empty video Hence I had to put it back and boom. even if cvtColor() seems like doing nothing, for some unknown reason it has to be there.when you use PIL Image along with cvtColor() it writes the video as expected
from PIL import Image
def record(mon,out,sct):
global recording
recording=True
while recording:
frame=sct.grab(mon)
frame = Image.frombytes('RGB', frame.size, frame.rgb)
frame = cv2.cvtColor(np.array(frame), cv2.COLOR_BGR2RGB)
out.write(np.array(frame))
out.release()
as I am very new to programming, I would really appreciate your help if I missed or overlooked something important
You can do
frameRGB = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
Frame is in BGR, and it will work the same as you are only changing R with B where frameRGB is in RGB now. This command will transfer R to B and works to transfer frames from RGB and BGR as well as BGR to RGB. BGR2RGB might be a bug, I have it as well but the command I mentioned works perfectly. That's what I do.
MSS store raw BGRA pixels. Does it work if you change to:
# Grab it
img = np.array(sct.grab(mon))
# Convert from BGRA to RGB
frame = cv2.cvtColor(img, cv2.COLOR_BGRA2RGB)
you should run this command in cmd
pip install opencv-python

Does PIL work with skimage?

Why is is that when I do this:
from skimage import feature, io
from PIL import Image
edges = feature.canny(blimage)
io.imshow(edges)
io.show()
I get exactly what I want, that being the full edged-only image. But when I do this:
edges = feature.canny(blimage)
edges = Image.fromarray(edges)
edges.show()
I get a whole mess of random dots, lines and other things that have more resemble a Jackson Pollock painting than the image? Whats wrong, and how do I fix it such that I can get what I want through both methods?
for full code, visit my Github here:
https://github.com/Speedyflames/Image-Functions/blob/master/Image_Processing.py
Let's see what kind of image is produced by skimage.feature.canny:
edges = feature.canny(blimage)
>>> print(edges.dtype)
bool
It'a boolean image, True for white, False for black. PIL correctly identifies the datatype and tries to map it to its own 1-bit mode which is "1" (see PIL docs about it).
This looks broken though, it doesn't seem to properly get byte width or something like that.
There was an issue about it, they seem to have fixed the PIL to NumPy conversion but apparently the converse is still broken.
Anyway, long story short, your best bet to successfully convert a binary image from NumPy to PIL is converting it to grayscale:
edges_pil = Image.fromarray((edges * 255).astype(np.uint8))
>>> print edges_pil.mode
L
If you actually need a 1-bit image you can convert it afterwards with
edges_pil = edges_pil.convert('1')

How to overlay images on each other in python and opencv?

I am trying to write images over each other. Ideally, what I want to do is to write every image in one folder over every image in another folder and output every unique image to another folder. So far, I am just working on having one image write over one image, but I can't seem to get that to work.
import numpy as np
import cv2
import matplotlib
def opencv_createsamples():
mask = ('resized_pos/2')
img = cv2.imread('neg/1')
new_img = img * (mask.astype(img.dtype))
cv2.imwrite('samp', new_img)
opencv_createsamples()
It would be helpful to have more information about your errors.
Something that stands out immediately is the lack of file type extensions. Your images are probably not being read correctly, to begin with. Also, image sizes would be a good thing to consider so you could resize as required.
If the goal is to blend images, considering the alpha channel is important. Here is a relevant question on StackOverflow:How to overlay images in python
Some other OpenCV docs that have helped me in the past: https://docs.opencv.org/trunk/d0/d86/tutorial_py_image_arithmetics.html
https://docs.opencv.org/3.1.0/d5/dc4/tutorial_adding_images.html
Hope this helps!

Resources