How to save a 2-channel image to a folder? - python

I am concatenating two 1-channel grayscale images into one 2-channel image and writing it to a folder.
import struct
import zlib
import numpy as np
from PIL import Image
from matplotlib import pyplot as plt
import pandas as pd
import cv2
import numpy as np
import glob
import os
from keras.preprocessing import image
import imageio
filenames1 = glob.glob("folder1/*.png")
filenames1.sort()
filenames2 = glob.glob("folder2/*.png")
filenames2.sort()
for f1,f2 in zip(filenames1,filenames2):
img_name = os.path.basename(f1)
img_name = img_name[:-4] + ".png"
img1 = Image.open(f1)
img2 = Image.open(f2)
img1a = image.img_to_array(img1)
img2a = image.img_to_array(img2)
# Merged image
merge_image = np.concatenate((img1a, img2a), axis=2)
# plt.imsave('folder3/{}.png'.format(img_name[:-4]),merge_image)
imageio.imwrite('folder4/{}.png'.format(img_name[:-4]),merge_image)
When I used matplotlib's 'imsave' function, I got the following error:
ValueError: Third dimension must be 3 or 4
When I used Imageio 'imwrite' function, I got the following error:
ValueError: Image must be 2D (grayscale, RGB, or RGBA)
How can I write the 2-channel image to a folder in this case?

As your error stack says, you cannot use matplotlib.imsave or imageio.imwrite as they support only 1 (grayscale), 3 (rgb, bgr, hsv etc...) or 4 (same as 3 + alpha channel). I don't know if png format does support 2 channels at all, but if it does the result would be a single channel image (grayscale) + alpha channel.
The solution depends on what these images do represent and what you're actually trying to achieve:
if you want to save single channel image + alpha channel, you'd better to replicate the first channel 3 times, so that your channels are (BW, BW, BW, alpha)
if you're fusing two spatial informations, for example angle and magnitude of an optical flow, you have to do the conversion manually (OpenCV displaying a 2-channel image (optical flow)) and fill the remaining channel with something else.
if you're only trying to stack two images and save them, png is not the correct solution. You could stack them using numpy and save/store them as .npy objects.

Related

How to use a picture from a plot for the further code

I tried to use a plot as image for my further code. I load an image in my pretrained model and my output is a tensor variable. In the next step I plot it with Image(img_hr).show(figsize=(18,15)). And after this I would like to use the picture from the plot to convert the colors. But the problem is, I cant use the variable img_hr because the type is tensor.
My idea was in the third last line to read the plot. The input for imagehsv = cv2.cvtColor(img_hr, cv2.COLOR_BGR2HSV) need to be an array and I dont know how to convert the plot.
Here is the error:
imagehsv = cv2.cvtColor(img_hr, cv2.COLOR_BGR2HSV)
error: OpenCV(4.5.2) :-1: error: (-5:Bad argument) in function 'cvtColor'
Overload resolution failed:
src is not a numpy array, neither a scalar
Expected Ptr<cv::UMat> for argument 'src'
I am new in python so please forgive me for any bad description or wrong vocabulary. I hope everything is clear otherwise feel free to ask.
Here is the picture of the plot:
Does anyone have an idea? Thanks a lot
from fastai import *
from fastai.vision import *
from fastai.callbacks.hooks import *
from fastai.utils.mem import *
import numpy as np
import cv2 as cv2
import matplotlib as mpl
import matplotlib.pyplot as plt
def acc_camvid(input, target):
target = target.squeeze(1)
mask = target != void_code
return (input.argmax(dim=1)[mask]==target[mask]).float().mean()
learn=load_learner(r'C:\pretrained_model')
image= r"C:\image.png"
img = open_image(image); img.shape
_,img_hr,b = learn.predict(img)
Image(img_hr).show(figsize=(18,15))
#image = cv2.imread(Image(img_hr))
imagehsv = cv2.cvtColor(img_hr, cv2.COLOR_BGR2HSV)
plt.imshow(fixColor(imagehsv))
fastai works with PIL Image types.
So your variable img_hr is an Image.
OpenCV uses NumPy ndarray types. You need to convert your Image to a ndarray:
_, img_hr, b = learn.predict(img)
img_hr = np.array(img_hr)
# PIL uses RGB channel order, not BGR like OpenCV default
imagehsv = cv2.cvtColor(img_hr, cv2.COLOR_RGB2HSV)
# Display your result in HSV color space
cv2.imshow("Image HSV", imagehsv)
cv2.waitKey()

KeyError: ((1, 1, 1280), '|u1') while using PIL's Image.fromarray - PIL

I have this code:
from PIL import Image
import numpy as np
img = Image.open('img.jpg')
Image.fromarray(np.array([[np.mean(i, axis=1).astype(int).tolist()]*len(i) for i in np.array(img).tolist()]).astype('uint8')).show()
And I am trying to modify the pixels of the image in PIL, however when I run it it gives an error as follows:
KeyError: ((1, 1, 1280), '|u1')
Not just that, it also outputs a second error as follows:
TypeError: Cannot handle this data type
Is there a way to overcome this?
P.S. I searched and the most related question to mine was:
Convert numpy.array object to PIL image object
However I don't get it nor know how to implement it.
For reading specific pixel via any image library such as PIL or OpenCV first channel of image is Height second channel is Width and last one is number of channels and here is 3. When you convert image to gray scale, third channel will be 1.
But this error happen when you want to convert a numpy array to PIL image using Image.fromarray but it shows the following error:
KeyError: ((1, 1, 3062), '|u1')
Here you could see another solution:
Convert numpy.array object to PIL image object
the shape of your data.
Pillow's fromarray function can only do a MxNx3 array (RGB image), or an MxN array (grayscale). To make the grayscale image work, you have to turn you MxNx1 array into a MxN array. You can do this by using the np.reshape() function. This will flatten out the data and then put it into a different array shape.
img = img.reshape(M, N) #let M and N be the dimensions of your image
(add this before the img = Image.fromarray(img))
I am not certain what you are trying to do but if you want the mean:
from PIL import Image
import numpy as np
img = Image.open('img.jpg')
# Make Numpy array
imgN = np.array(img)
mean = np.mean(imgN,axis=2)
# Revert back to PIL Image from Numpy array
result = Image.fromarray(mean)
Alternatively, if you want a greyscale which is an alternative to the mean
from PIL import Image
import numpy as np
img = Image.open('img.jpg').convert('L')

np.reshape(): Converting an image into a feature array based on rgb intensities

I am trying to segment a colour image using the Mean-Shift algorithm using sklearn.
I have the following code:
import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from sklearn.datasets.samples_generator import make_blobs
import matplotlib.pyplot as plt
from itertools import cycle
from PIL import Image
image = Image.open('sample_images/fruit_half.png')
image = np.array(image)
#need to convert image into feature array based on rgb intensities
flat_image = np.reshape(image, [-1,3])
I am trying to convert the image into a feature array based on rgb intensities so that I can do the clustering.
However, I get the following error:
ValueError: cannot reshape array of size 3979976 into shape (3)
I am not sure why I am getting this error and how I can resolve this. Any insights are appreciated.
It's because the image you're loading does not have RGB values (if you look at the dimensions, the last one is 4.
You need to first convert it to RGB like this:
image = Image.open('sample_images/fruit_half.png').convert('RGB')
image = np.array(image)
# Now it works
flat_image = np.reshape(image, [-1,3])

Why 16bit to 8bit conversion produces striped image?

I am testing a segmentation algorithm on several VHSR satellite images, which originally comes in 16bit format, but when I convert them to 8bit images, the produced images are showing striped appearance.
I've been trying different python libraries (skimage, cv2, scipy) getting similar results.
1) The original 16-bit image it is a 4 band image (NIR,B,G,R), so you need to choose the right bands to create a true color image, RGB image (4,3,2 bands). thanks in advance. It can be downloaded from this link:
16bit image
2) I use this code to convert each pixel value, from a 16-bit integer now fitting within 8-bit range:
from scipy.misc import bytescale
SS = io.imread('Imag16bit.tif')
SS = bytescale(SS)
SS = np.asarray(SS)
plt.imshow(SS)
This is my result of above code:
bytescale works for me. I think the asarray step messes up something.
import cv2
from skimage import io
from scipy.misc import bytescale
image = io.imread('SkySat_16bit.tif')
cv2.imshow('Original', image)
print(image.dtype)
image = bytescale(image)
print(image.dtype)
cv2.imshow('Converted', image)
cv2.waitKey(0)
I think this is a way to do it:
#!/usr/local/bin/python3
from PIL import Image
from tifffile import imsave, imread
# Load image
im = imread('SkySat_16bit.tif')
# Extract Red, Green and Blue bands into separate 8-bit arrays
R = (im[:,:,3]/256).astype(np.uint8)
G = (im[:,:,2]/256).astype(np.uint8)
B = (im[:,:,1]/256).astype(np.uint8)
# Combine bands into RGB array
RGB = np.dstack((R,G,B))
# Save to disk
Image.fromarray(RGB).save('result.png')
You may want to adjust the contrast a bit, and check I selected the correct bands.

Quality loss after combining two images with PIL and numpy

I'm using PIL and numpy to combine two images while one is a .jpg and the other image is represented by a numpy array, which defines a mask that I want to put on top of the original image (basically just a matrix with one and zero entries and the same size as the .jpg). PIL’s composite function works just fine for that but for some reason, after saving the composite image, the file size shrinks to approximately 1/3 of the original image size. Can someone explain this behavior to me?
Here's a code snippet:
import numpy as np
import PIL
from PIL import Image
from PIL import ImageColor
rgb = ImageColor.getrgb('black')
# Read image and write into numpy array
image = Image.open('test_image.jpg')
(im_width, im_height) = image.size
# Create empty mask
mask = np.zeros((im_width, im_height))
# Composite image and mask
solid_color = np.expand_dims(np.ones_like(mask), axis=2) *
np.reshape(list(rgb), [1, 1, 3])
pil_solid_color =
Image.fromarray(np.uint8(solid_color)).convert('RGBA')
pil_mask = Image.fromarray(np.uint8(255.*mask)).convert('L')
image = Image.composite(pil_solid_color, image, pil_mask)
# save image
image.save('test_image_with_mask.jpg')
Code was inspired by tnesorflow's object detection api. Thanks in advance.

Categories

Resources