How mosaic 1000 of rasters in a grid? sequentially by file name? - python

I split my image (Raster image) into 1000 tiles for image segmentation prediction, and I want to mosaic the predicted rasters to a 1664*2432 grid sequentially.
I used these posts:
Generate a Photographic mosaic from a given set of thumbnails
How do you merge images into a canvas using PIL/Pillow?
PIL fill background image repeatedly
This is the code:
import PIL, os, glob
from PIL import Image
from math import ceil, floor
PATH = '/content/drive/MyDrive/classification/CNN_segmentation/big_Image/prediction'
frame_width = 1664
images_per_row = 26
padding = 2
os.chdir(PATH)
images = glob.glob("*.tiff")
images = images[:988]
img_width, img_height = Image.open(images[0]).size
sf = (frame_width-(images_per_row-1)*padding)/(images_per_row*img_width) #scaling factor
scaled_img_width = ceil(img_width*sf) #s
scaled_img_height = ceil(img_height*sf)
number_of_rows = 38
frame_height = 2432
new_im = Image.new('RGB', (frame_width, frame_height))
i,j=0,0
for num, im in enumerate(images):
if num%images_per_row==0:
i=0
im = Image.open(im)
im.thumbnail((64,64))
#Iterate through a 4 by 4 grid with 100 spacing, to place my image
y_cord = (j//images_per_row)*scaled_img_height
new_im.paste(im, (i,y_cord))
i=(i+64)+0
j+=1
new_im.show()
new_im.save("out.jpg", "JPEG", quality=80, optimize=True, progressive=True)
new_im
The result is a 1664*2432 image but all the pixels are black.
I tried to use, "im=rasterio.open(im)" instead of "im = Image.open(im)", but I faced this error:
AttributeError: 'DatasetReader' object has no attribute 'thumbnail'
I will be thankful if anybody could help me.
Note: I solved the problem by replacing RGB with P, but still do not know how mosaic the images by file name sequentially.

Related

OpenCV - Creating a video from a set of images in a directory

I am trying to create a video from a sequence of images. Below is my code.
import os
import cv2
mean_width = 6000
mean_height = 4000
def generate_video():
image_folder = 'C:/New folder/Images/q/'
video_name = 'myvideo.avi'
os.chdir("C:/New folder/Images/q/")
images = [img for img in os.listdir(image_folder)
if img.endswith(".jpg") or
img.endswith(".jpeg") or
img.endswith("png")]#I'll use my own function for that, just easier to read
frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape
video = cv2.VideoWriter(video_name, 0, 0.25, (width, height))#0.25 so one image is 4 seconds
for image in images:
video.write(cv2.imread(os.path.join(image_folder, image)))
cv2.destroyAllWindows()
video.release()
generate_video()
The above code however creating the video just with one image. There are 5 other images as well in the folder C:/New folder/Images/q/ but the video is generated only for the first one. Can someone please advise if anything is missing here ? Seems like the for loop is not working
To make a video you need your images to have the same resolution. If some images have different sizes cv2.VideoWriter quietly skips them without any errors.
So you may need to resize your images to fixed size:
for image in images:
image = cv2.imread(os.path.join(image_folder, image))
image = cv2.resize(image, (width, height))
video.write(image)
An example to reproduce this behavior:
import cv2
import numpy as np
fc = cv2.VideoWriter_fourcc(*"mp4v")
video = cv2.VideoWriter("1.mp4", fc, 0.25, (500, 500))
for idx in range(10):
color = np.random.randint(0, 255, size=3)
if idx in [0, 2, 3]: # only 3 frames will be in the final video
image = np.full((500, 500, 3), fill_value=color, dtype=np.uint8)
else:
# slighly different size
image = np.full((400, 500, 3), fill_value=color, dtype=np.uint8)
video.write(image)

How to add noise (dithering) at background only?

I am trying to train a model with some noisy images having dithering.
What I have :
clean pdfs with white background
coloured pdfs(RGB) and grayscale pdfs (with 3 channels, RGB)
What I want:
convert only white background (not text) into gray background, if possible only half page should be converted
Add dithering to the gray background without loosing the text
what I tried:
import os
from PIL import Image
from numpy import asarray
ORIGIN_PATH = "/home/dithering/temp/"
DESTIN_PATH = "/home/dithering`enter code here`/temp_try/"
"""for filename in os.listdir(ORIGIN_PATH):
img = Image.open(ORIGIN_PATH + filename).convert("L")
rbg_grayscale_img = img.convert("RGB")
rbg_grayscale_img.save(DESTIN_PATH + filename)"""
for filename in os.listdir(ORIGIN_PATH):
img = Image.open(ORIGIN_PATH + filename).convert("L", dither=Image.Dither.FLOYDSTEINBERG)
# convert image to nparray
numpydata = asarray(img)
numpydata[numpydata > 250] = 128
# data
print(numpydata)
# convert array to image
final_image = Image.fromarray(numpydata)
# img show
final_image.show()
# img save
final_image.save(DESTIN_PATH + filename)
I expect something like this,
Any help would be appreciated, thanks in advance!

How to remove reflection from sunglasses and change the color of lenses in sunglasses

I am working on a project where I have to remove the reflection on sunglasses and also change the color of lenses. So, I tried to detect the dominant color in the image (sunglass lenses) and then tried to replace that color by another color using OpenCV.
But the code is not working properly. So, Please help me.
Here is the result that have obtained.
Here is the code that I have used to crop part of image(lenses) then detect the dominant color.
import cv2
from google.colab.patches import cv2_imshow
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
from skimage.color import rgb2lab, deltaE_cie76
import os
img = cv2.imread('originalimage.jpg')
cropped_lens2 = img[556:2045, 2000:3177]
image = cv2.cvtColor(cropped_lens2, cv2.COLOR_BGR2RGB)
modified_image = cv2.resize(image, (600, 400), interpolation = cv2.INTER_AREA)
cv2_imshow(modified_image)
modified_image = modified_image.reshape(modified_image.shape[0]*modified_image.shape[1], 3)
number_of_colors=2
clf = KMeans(n_clusters = number_of_colors)
labels = clf.fit_predict(modified_image)
counts = Counter(labels)
center_colors = clf.cluster_centers_
ordered_colors = [center_colors[i] for i in counts.keys()]
hex_colors = [RGB2HEX(ordered_colors[i]) for i in counts.keys()]
rgb_colors = [ordered_colors[i] for i in counts.keys()]
plt.figure(figsize = (8, 6))
plt.pie(counts.values(), labels = hex_colors, colors = hex_colors)
Input Image and output images are as shown below.
Original image
Cropped image
color range output
Code that I have used to replace range of colors by a single color.
you can also replace it by gradient colors.
from PIL import Image
img = Image.open("old_Test/DSC-0296 fold.jpg")
img = img.convert("RGB")
datas = img.getdata()
new_image_data = []
for item in datas:
if item[0] in list(range(0, 80)):
new_image_data.append((255, 204, 100))
else:
new_image_data.append(item)
img.putdata(new_image_data)
img
output image after replacing colors
Change color of any image using OpenCV:
Based on your comment maybe this can help you
You can make a mask from the part that needs to be changed.
def changeColor(im, msk, hue=130):
h, s, v = cv2.split(cv2.cvtColor(im.copy(), cv2.COLOR_BGR2HSV))
h[np.where(msk == 0)] = hue
return cv2.cvtColor(cv2.merge([h, s, v]), cv2.COLOR_HSV2BGR)
Of these 6 images, the top left is the main image I drew with a graphic software. The image at the bottom left is a mask that tells the algorithm where the image should change. Apart from these 2 images, the other 4 are function tests.

pasting images from an array to a bigger image

This is my program, I'm trying for a 1x2 array of two cv2 images to past it to a bigger one. the idea is to do that for a bigger array of images and see them as a mosaic.
Problem is that this seems a mixture of cv2 and PIL and couldn't manage it to work.
Here my little code:
from PIL import Image
from matplotlib import cm
#example of one of the two images added
new_array = cv2.resize(x_test[0], (IMG_SIZE, IMG_SIZE))
trp.append(new_array2)
im_pil=[]
#I create the big image
new_im = Image.new('RGB', (IMG_SIZE,IMG_SIZE * 2))
k=0
#here i want to place my image in the mosaic
for i in range(0,IMG_SIZE,IMG_SIZE):
for j in range(0,IMG_SIZE *2,IMG_SIZE):
#paste the image at location i,j:
im_pil.append(Image.fromarray(trp[k])
new_im.paste(im_pil[k], (i,j))
k+=1
new_im
that's all the code( a little summarized) and I don't really know where the problem is. I get different kinds of errors in each modification, from invalid syntax to not defined or some crazy ones i dont understand.
although that
plt.imshow(trp[1])
plt.show()
works and shows one image
I'm sorry if the answer is obvious buy I'm trying my best to figure it out and I can't manage it.
Thanks in advance
I don't know why you use cv2 because you can do all in PIL. You can even display it without matplot
from PIL import Image
IMG_SIZE = 128
filenames = ['image1.png', 'image2.png']
images = []
for name in filenames:
img = Image.open(name)
img = img.resize((IMG_SIZE, IMG_SIZE))
images.append(img)
new_im = Image.new('RGB', (IMG_SIZE, IMG_SIZE*2))
k = 0
for i in range(0, IMG_SIZE, IMG_SIZE):
for j in range(0, IMG_SIZE *2, IMG_SIZE):
new_im.paste(images[k], (i,j))
k += 1
new_im.save('output.png')
new_im.show()
For images from cv2 you have to
convert colors from BGR to RGB
convert array to Image
do what you want
convert back Image to array
if you want to use again with cv2 then convert colors from RGB to BGR
Code:
from PIL import Image
import cv2
import numpy as np
import matplotlib.pyplot as plt
IMG_SIZE = 128
filenames = ['image1.png', 'image2.png']
images = []
for name in filenames:
im = cv2.imread(name)
im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB)
im = cv2.resize(im, (IMG_SIZE, IMG_SIZE))
img = Image.fromarray(im)
images.append(img)
new_im = Image.new('RGB', (IMG_SIZE, IMG_SIZE*2))
k = 0
for i in range(0, IMG_SIZE, IMG_SIZE):
for j in range(0, IMG_SIZE *2, IMG_SIZE):
new_im.paste(images[k], (i,j))
k += 1
im = np.array(new_im)
plt.imshow(im)
plt.show()
#cv2.imshow('window', cv2.cvtColor(np.array(new_im), cv2.COLOR_RGB2BGR))
#cv2.waitKey(0)

Creating and combining numerous images in Python - Error: Too many open files:

So basically I've got 2 codes:
One creates a number of different coloured images with 1 pixel dimensions
The other combines all the created images into one
The first one works perfectly but in the second code I get an error: IOError: [Errno 24] Too many open files: 'Test 3161.png'
The thing is I don't necessarily want to create the files. What I really want is the combined image at the end. I'm not sure how to approach this. Any help would be greatly appreciated.
Code 1 - Creating images
from PIL import Image
import sys
im = Image.new("RGB", (1, 1))
pix = im.load()
j=0
for r in range(65,130):
for g in range(65,130):
for b in range(65,130):
for x in range(1):
for y in range(1):
axis = (r,g,b)
pix[x,y] = axis
print axis
j+=1
im.save('Test {}.png'.format(j), "PNG")
Code 2: Combining images
from PIL import Image
import sys
import glob
imgfiles = []
for file in glob.glob("*.png"):
imgfiles.append(file)
print imgfiles
#stitching images together
images = map(Image.open, imgfiles)
widths, heights = zip(*(i.size for i in images))
total_width = sum(widths)
max_height = max(heights)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
x_offset += im.size[0]
new_im.save('test.png')
This is somewhat the final image I'm trying to get but not with as many colours as shown in it:
The coloured images that are created from code 1 are images that are 1 pixel in width and diameter. For example like this:
Its harder to see as its one pixel right next to this. It looks like a fullstop but is the 1 pixel image in question.
I still don't understand what you expect to produce, but this should be close and a lot faster and easier:
#!/usr/local/bin/python3
from PIL import Image
import numpy as np
# Create array to hold output image
result=np.zeros([1,13*13*13,3],dtype=np.uint8)
j=0
for r in range(65,130,5):
for g in range(65,130,5):
for b in range(65,130,5):
result[0,j]= (r,g,b)
j+=1
# Convert output array to image and save
im=Image.fromarray(result)
im.save("result.jpg")
Note that the above script is intended to do the job of both of your scripts in one go.
Note that I made the result image a bit taller (fatter) so you can see it, in fact it is only 1 pixel high.
Note that I added a step of 5 to make the output image smaller because it otherwise exceeds the size limits - for JPEG at least.
Note that I coarsely guessed the array width (13*13*13) on the basis of (130-65)/5, because I don't really understand your requirements.
To solve the too-many-open-files error, you can make a little function:
def getImageDetails(imgfile):
im = Image.open(imgfile)
size = im.size
im.load() # closes the pointer after it loads the image
return size[0], size[1], im
widths, heights, images = zip(*(getImageDetails(i) for i in imgfiles))
replace these lines with the code above:
images = map(Image.open, imgfiles)
widths, heights = zip(*(i.size for i in images))

Categories