My program takes in an image and crops the image into seperate images according to the scale parameter, e.g. scale = 3 produces 9 images of equal size. I then work out mean rgb of each cropped image and set all pixel values in the image equal to the mean rgb value.
I am wondering how I can stich the cropped images back together to output one image? Which in this case would be a grid of nine different colours.
Here is my code:
# import packages
import numpy as np
import cv2
import dateutil
import llist
from matplotlib import pyplot as plt
import argparse
#Read in image
img = cv2.imread('images/0021.jpg')
scale = 3
#Get x and y components of image
y_len,x_len,_ = img.shape
mean_values = []
for y in range(scale):
for x in range(scale):
#Crop image 3*3 windows
cropped_img=img[(y*y_len)/scale:((y+1)*y_len)/scale,
(x*x_len)/scale:((x+1)*x_len)/scale]
mean_val=cv2.mean(cropped_img)
mean_val=mean_val[:3]
#Set cropped img pixels equal to mean RGB
cropped_img[:,:,:] = mean_val
cv2.imshow('cropped',cropped_img)
cv2.waitKey(0)
#Print mean_values array
#mean_values.append([mean_val])
#mean_values=np.asarray(mean_values)
#print mean_values.reshape(3,3,3)
As it stands the nested for loop iterates over the image and outputs the images (which are just blocks of one colour) in the order that I want to stitch them together, but im not sure how to achieve this.
I don't know if such things exist in OpenCV, but in ImageMagick you can simply resize the image down to the tile-size (which will implicitly average the pixels) and the re-scale the image back up to the original size without interpolation - also called Nearest Neighbour Resampling. Like this:
# Get original width and height
identify -format "%wx%h" face1.jpg
500x529
# Resize down to, say 10x10 and then back up to the original size
convert face1.jpg -resize 10x10! -scale "${geom}"! out.jpg
Per your original, 3x3 becomes:
convert face1.jpg -resize 3x3! -scale "${geom}"! out.jpg
and 3x5 becomes:
convert face1.jpg -resize 3x5! -scale "${geom}"! out.jpg
Related
I'm trying to write code that takes TEM (Transmission Electron Microscope) TITFF images, and computes the FFT. But I always get plain Red, Green or Blue images.
Here's what the RAW TEM images look like :
Here's what the FFT image should look like :
But instead I get :
Here's my code :
import numpy as np
import diplib as dip
import matplotlib.pyplot as plt
from PIL import Image
from ncempy.io import dm
img1 = dip.ImageReadTIFF('RAW_FFT.tif')
f = np.fft.fft2(img1)
f = np.fft.fftshift(f)
plt.imshow(abs(f))
plt.show()
Do you have any idea what could be the problem? I even tried to convert the image to np.array and do FFT step by step but I get the same result.
FFT is complex and without a logarithm, Fourier transform would be so much brighter than all the other points that everything else will appear black.
see for details: https://homepages.inf.ed.ac.uk/rbf/HIPR2/fourier.htm
import cv2
import numpy as np
img=cv2.imread('inputfolder/yourimage.jpg',0)
def fft_image_inv(image):
f = np.fft.fft2(image)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 15*np.log(np.abs(fshift))
return magnitude_spectrum
fft= fft_image_inv(img)
cv2.imwrite('outputfolder/yourimage.jpg',fft)
output:
There are multiple issues here. First, sometimes grayscale images are written to file as if they were RGB images (in a TIFF file, this could be as simple as storing a grayscale color map, the pixel values will be interpreted as indices into the map, and the loaded image will be an RGB image instead of a grayscale image, even through it has only grayscale colors).
This is the case here. All three channels have exactly the same information, but there are three channels stored, and your FFT will compute the same thing three times!
After loading the image with dip.ImageReadTIFF(), you can use parentheses to index one of the channels:
img1 = dip.ImageReadTIFF('RAW_FFT.tif')
img1 = img1(0)
We now have an actual gray-scale image. This should get rid of the red color in the output.
After computing the FFT, we have a floating-point image with a very high dynamic range (the largest magnitude, at the middle pixel, is 437536704). pyplot, by default, will show floating-point images with 0 and all negative values as black, and 1 and all larger values as white (actual colors depend of course on the color map it uses). So your display will be all white. Use the vmax parameter to imshow to determine the value shown as white. Setting this to 1e6 should give you a similar display as in the GMS software.
Instead of pyplot you can use DIPlib for display. Its interactive viewer will let you use a slider to manually set the grayscale limits, and you can manually select to display the magnitude, as well as choose a logarithmic mapping (which tend to be most useful for displaying the frequency domain).
f = dip.FourierTransform(img)
dip.viewer.ShowModal(f)
Alternatively, you can use a static display, which uses pyplot under the hood:
f.Show((0, 1e6))
or
f.Show('log')
I want to analyse a specific part of an image, as an example I'd like to focus on the bottom right 200x200 section and count all the black pixels, so far I have:
im1 = Image.open(path)
rgb_im1 = im1.convert('RGB')
for pixel in rgb_im1.getdata():
Whilst you could do this with cropping and a pair of for loops, that is really slow and not ideal.
I would suggest you use Numpy as it is very commonly available, very powerful and very fast.
Here's a 400x300 black rectangle with a 1-pixel red border:
#!/usr/bin/env python3
import numpy as np
from PIL import Image
# Open the image and make into Numpy array
im = Image.open('image.png')
ni = np.array(im)
# Declare an ROI - Region of Interest as the bottom-right 200x200 pixels
# This is called "Numpy slicing" and is near-instantaneous https://www.tutorialspoint.com/numpy/numpy_indexing_and_slicing.htm
ROI = ni[-200:,-200:]
# Calculate total area of ROI and subtract non-zero pixels to get number of zero pixels
# Numpy.count_nonzero() is highly optimised and extremely fast
black = 200*200 - np.count_nonzero(ROI)
print(f'Black pixel total: {black}')
Sample Output
Black pixel total: 39601
Yes, you can make it shorter, for example:
h, w = 200,200
im = np.array(Image.open('image.png'))
black = h*w - np.count_nonzero(ni[-h:,-w:])
If you want to debug it, you can take the ROI and make it into a PIL Image which you can then display. So just use this line anywhere after you make the ROI:
# Display image to check
Image.fromarray(ROI).show()
You can try cropping the Image to the specific part that you want:-
img = Image.open(r"Image_location")
x,y = img.size
img = img.crop((x-200, y-200, x, y))
The above code takes an input image, and crops it to its bottom right 200x200 pixels. (make sure the image dimensions are more then 200x200, otherwise an error will occur)
Original Image:-
Image after Cropping:-
You can then use this cropped image, to count the number of black pixels, where it depends on your use case what you consider as a BLACK pixel (a discrete value like (0, 0, 0) or a range/threshold (0-15, 0-15, 0-15)).
P.S.:- The final Image will always have a dimension of 200x200 pixels.
from PIL import Image
img = Image.open("ImageName.jpg")
crop_area = (a,b,c,d)
cropped_img = img.crop(crop_area)
I have a problem in size of image size (M*N)
when i read an image as np.array after i apply an algorithm method ,the size of its change to another value.some time increase and some times decrease depend on image.
img = cv2.imread('baboon.jpg',1) # read image (255*255)
na = np.array(img) # convert it to array
x, y ,pp = img.shape[:3] # size of 3d
blue = np.array(range(x*y), int).reshape((x, y))
blue[:,:] = na[:, :, 0]
en_split_block_8(red,31,1) # function algorithm
I use this code to save the image and the size changes from (255*255) to (640*480)
plt.imshow(blue,interpolation='nearest',cmap="gray")
plt.savefig('blue.jpg')#(640*480)
I want the image to keep it's size. (I don't change size of image just i work on values).
That's because you are saving the image with pyplot which shows the image as a diagram. What you want to do instead is save the blue image using cv2 with: cv2.imwrite('blue.jpg', blue).
Beware that na = np.array(img) is redundant as to why img is already an ndarray.
I am working on hair removal from skin lesion images. Is there any way to convert binary back to rgb?
Original Image:
Mask Image:
I just want to restore the black area with the original image.
As I know binary images are stored in grayscale in opencv values 1-->255.
To create „dummy“ RGB images you can do:
rgb_img = cv2.cvtColor(binary_img, cv.CV_GRAY2RGB)
I call them „dummy“ since in these images the red, green and blue values are just the same.
Something like this, but your mask is the wrong size (200x200 px) so it doesn't match your image (600x450 px):
#!/usr/local/bin/python3
from PIL import Image
import numpy as np
# Open the input image as numpy array
npImage=np.array(Image.open("image.jpg"))
# Open the mask image as numpy array
npMask=np.array(Image.open("mask2.jpg").convert("RGB"))
# Make a binary array identifying where the mask is black
cond = npMask<128
# Select image or mask according to condition array
pixels=np.where(cond, npImage, npMask)
# Save resulting image
result=Image.fromarray(pixels)
result.save('result.png')
I updated the Daniel Tremer's answer:
import cv2
opencv_rgb_img = cv2.cvtColor(opencv_image, cv2.COLOR_GRAY2RGB)
opencv_image would be two dimension matrix like [width, height] because of binary.
opencv_rgb_img would be three dimension matrix like [width, height, color channel] because of RGB.
I can only ever find examples in C/C++ and they never seem to map well to the OpenCV API. I'm loading video frames (both from files and from a webcam) and want to reduce them to 16 color, but mapped to a 24-bit RGB color-space (this is what my output requires - a giant LED display).
I read the data like this:
ret, frame = self._vid.read()
image = cv2.cvtColor(frame, cv2.COLOR_RGB2BGRA)
I did find the below python example, but cannot figure out how to map that to the type of output data I need:
import numpy as np
import cv2
img = cv2.imread('home.jpg')
Z = img.reshape((-1,3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 8
ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((img.shape))
cv2.imshow('res2',res2)
cv2.waitKey(0)
cv2.destroyAllWindows()
That obviously works for the OpenCV image viewer but trying to do the same errors on my output code since I need an RGB or RGBA format. My output works like this:
for y in range(self.height):
for x in range(self.width):
self._led.set(x,y,tuple(image[y,x][0:3]))
Each color is represented as an (r,g,b) tuple.
Any thoughts on how to make this work?
I think the following could be faster than kmeans, specially with a k = 16.
Convert the color image to gray
Contrast stretch this gray image to so that resulting image gray levels are between 0 and 255 (use normalize with NORM_MINMAX)
Calculate the histogram of this stretched gray image using 16 as the number of bins (calcHist)
Now you can modify these 16 values of the histogram. For example you can sort and assign ranks (say 0 to 15), or assign 16 uniformly distributed values between 0 and 255 (I think these could give you a consistent output for a video)
Backproject this histogram onto the stretched gray image (calcBackProject)
Apply a color-map to this backprojected image (you might want to scale the backprojected image befor applying a colormap using applyColorMap)
Tip for kmeans:
If you are using kmeans for video, you can use the cluster centers from the previous frame as the initial positions in kmeans for the current frame. That way, it'll take less time to converge, so kmeans in the subsequent frames will most probably run faster.
You can speed up your processing by applying the k-means on a downscaled version of your image. This will give you the cluster centroids. You can then quantify each pixel of the original image by picking the closest centroid.