Importing images like MNIST - python

I have a 100 images, each 10 for every digit and i am trying to convert it like MNIST images in python. But, constantly i am getting an error. Error is posted down!
from PIL import Image, ImageFilter
from os import listdir
def imageprepare(argv):
"""
This function returns the pixel values.
The imput is a png file location.
"""
imagesList = listdir(argv)
for image in imagesList:
im = Image.open(argv).convert('L')
width = float(im.size[0])
height = float(im.size[1])
newImage = Image.new('L', (28, 28), (255)) # creates white canvas of 28x28 pixels
if width > height: # check which dimension is bigger
# Width is bigger. Width becomes 20 pixels.
nheight = int(round((20.0 / width * height), 0)) # resize height according to ratio width
if (nheight == 0): # rare case but minimum is 1 pixel
nheight = 1
# resize and sharpen
img = im.resize((20, nheight), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wtop = int(round(((28 - nheight) / 2), 0)) # calculate horizontal position
newImage.paste(img, (4, wtop)) # paste resized image on white canvas
else:
# Height is bigger. Heigth becomes 20 pixels.
nwidth = int(round((20.0 / height * width), 0)) # resize width according to ratio height
if (nwidth == 0): # rare case but minimum is 1 pixel
nwidth = 1
# resize and sharpen
img = im.resize((nwidth, 20), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wleft = int(round(((28 - nwidth) / 2), 0)) # caculate vertical pozition
newImage.paste(img, (wleft, 4)) # paste resized image on white canvas
# newImage.save("sample.png
tv = list(newImage.getdata()) # get pixel values
# normalize pixels to 0 and 1. 0 is pure white, 1 is pure black.
tva = [(255 - x) * 1.0 / 255.0 for x in tv]
print(tva)
return tva
argv= 'images/'
x=imageprepare(argv)#file path here
print(len(x))# mnist IMAGES are 28x28=784 pixels
error:
File "C:/Users/lenovo/.spyder-py3/Project1/test12.py", line 47, in
x=imageprepare(argv)#file path here
File "C:/Users/lenovo/.spyder-py3/Project1/test12.py", line 14, in imageprepare
im = Image.open(argv).convert('L')
File "C:\Users\lenovo\Anaconda3\lib\site-packages\PIL\Image.py", line 2477, in open
fp = builtins.open(filename, "rb")
PermissionError: [Errno 13] Permission denied: 'images/'

From the log above, it seems that you have no permission on folder images/ which has been passed as an argument to function imageprepare. Have you tried to change the access privileges of images? Or just run this from prompt as Administrator.

Related

How to merge multiple pictures diagonally into a single one using Python

I'm trying to merge multiple images diagonally into a single one using Python.
I checked a lot of questions but didn't find something similar to my need.
All I can do right now is a simple merge of files on top of each other:
from PIL import Image
import numpy as np
img = Image.open("1.png")
background = Image.open("2.png")
background.paste(img, (0, 0), img)
background.save('result.png',"PNG")
Here are the pictures to test :
image1, image2, image3
I need the pictures to be arranged diagonally to fit into a final 900 x 1200 px size picture with white Background. Probably they need to be sized down a bit and fit ? At least that's the process I am doing in Photoshop, manually (time consuming).
Sometimes there's 2 pictures to fit, sometimes could be 4 or 5.
This should do the job:
from PIL import Image
images = ['1.png', '2.png', '3.png']
# shift between images
offset = (200, 100)
target_size = (900, 1200)
images = [Image.open(fn) for fn in images]
no_img = len(images)
image_size = [s+no_img*o for s, o in zip(images[0].size, offset)]
#create empty background
combined_image = Image.new('RGBA', image_size)
# paste each image at a slightly shifted position, start at top right
for idx, image in enumerate(images):
combined_image.paste(image, ((no_img - idx - 1) * offset[0], idx * offset[1]), image)
# crop to non-empty area
combined_image = combined_image.crop(combined_image.getbbox())
# resizing and padding such that it fits 900 x 1200 px
scale = min(target_size[0] / combined_image.size[0], target_size[1] / combined_image.size[1])
combined_image = combined_image.resize((int(combined_image.size[0] * scale), int(combined_image.size[1] * scale)), Image.BICUBIC)
img_w, img_h = combined_image.size
finale_output = Image.new('RGB', target_size, (255, 255, 255))
offset = ((target_size[0] - img_w) // 2, (target_size[1] - img_h) // 2)
finale_output.paste(combined_image, offset, combined_image)
# display
finale_output.show()
EDIT: I added the code for resizing and padding such that the output is exactly of your wanted size (whilst maintaining the aspect ratio).

How can I randomly add 20 images(10x10) in an empty background image (200x200) in python?

I want the 10 small images to be placed in this circle
I'm working on a small project to randomly place or put several images of size (10 w x 10 h) in another image that will be used as background of size (200 w x 200 h) in python. The small images should be put at a random location in the background image.
I have 20 small images of size (10x10) and one empty image background of size (200x200). I want to put my 20 small images in the empty background image at a random location in the background.
Is there a way to do it in Python?
Code
# Depencies importation
import cv2
# Saving directory
saving_dir = "../Saved_Images/"
# Read the background image
bgimg = cv2.imread("../Images/background.jpg")
# Resizing the bacground image
bgimg_resized = cv2.resize(bgimg, (2050,2050))
# Read the image that will be put in the background image (exemple of 1)
small_img = cv2.imread("../Images/small.jpg")
# Convert the resized background image to gray
bgimg_gray = cv2.cvtColor(bgimg, cv2.COLOR_BGR2GRAY)
# Convert the grayscale image to a binary image
ret, thresh = cv2.threshold(bgimg_gray,127,255,0)
# Determine the moments of the binary image
M = cv2.moments(thresh)
# calculate x,y coordinate of center
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])
# drawing the circle in the background image
circle = cv2.circle(bgimg, (cX, cY), 930, (0,0,255), 9)
print(circle)
# Saving the new image
cv2.imwrite(saving_dir+"bgimg"+".jpg", bgimg)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.resizeWindow("Test", 1000, 1200)
# Showing the images
cv2.imshow("image", bgimg)
# Waiting for any key to stop the program execution
cv2.waitKey(0)
the above code is for one image, I want to do it for the 20 and to put them in a random location
Assuming you have that background image background.jpg (decreased to 200x200 px) and 10 images: image01.png, image02.png ... image10.png (10x10 px). Then:
import glob
import random
from PIL import Image
img_bg = Image.open('circle.jpg')
width, height = img_bg.size
images = glob.glob('*.png')
for img in images:
img = Image.open(img)
x = random.randint(40, width-40)
y = random.randint(40, height-40)
img_bg.paste(img, (x, y, x+10, y+10))
img_bg.save('result.png', 'PNG')
Output image:

How do I make ImageOps.fit not crop?

How do I get ImageOps.fit(source28x32, (128, 128)) to fit without cropping off the top/bottom/sides? Do I really have to find the aspect, resize accordingly so the enlarged version does not exceed 128x128, and then add border pixels (or center the image in a 128x128 canvas)? Mind you that the source can be of any ratio, the 28x32 is just an example.
source image (28x32)
fitted image (128x128)
This is my attempt so far, not particularly elegant
def fit(im):
size = 128
x, y = im.size
ratio = float(x) / float(y)
if x > y:
x = size
y = size * 1 / ratio
else:
y = size
x = size * ratio
x, y = int(x), int(y)
im = im.resize((x, y))
new_im = Image.new('L', (size, size), 0)
new_im.paste(im, ((size - x) / 2, (size - y) / 2))
return new_im
New fitted image
Here is the function implemented in both PIL and cv2. The input can be of any size; the function finds the scale needed to fit the largest edge to the desired width, and then puts it onto a black square image of the desired width.
In PIL
def resize_PIL(im, output_edge):
scale = output_edge / max(im.size)
new = Image.new(im.mode, (output_edge, output_edge), (0, 0, 0))
paste = im.resize((int(im.width * scale), int(im.height * scale)), resample=Image.NEAREST)
new.paste(paste, (0, 0))
return new
In cv2
def resize_cv2(im, output_edge):
scale = output_edge / max(im.shape[:2])
new = np.zeros((output_edge, output_edge, 3), np.uint8)
paste = cv2.resize(im, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST)
new[:paste.shape[0], :paste.shape[1], :] = paste
return new
With a desired width of 128:
→
→
Not shown: these functions work on images larger than the desired size
This works pretty good to fit the image to size you want while filling in the rest with black space
from PIL import Image, ImageOps
def fit(im, width):
border = int((max(im.width, im.height) - min(im.width, im.height))/2)
im = ImageOps.expand(im, border)
im = ImageOps.fit(im, (width, width))
return im

python - Cropping an image of handwritten digit

I'm trying to predict handwritten digits using MNIST as the dataset & python. Right now, I have to give already cropped images as input to the program.
Further processing to make it to MNIST dataset format is done using the following function, but how to auto crop a random image given as input ?
def imageprepare(argv):
"""
This function returns the pixel values.
The imput is a png file location.
"""
im = Image.open(argv).convert('L')
width = float(im.size[0])
height = float(im.size[1])
newImage = Image.new('L', (28, 28), (255)) #creates white canvas of 28x28 pixels
if width > height: #check which dimension is bigger
#Width is bigger. Width becomes 20 pixels.
nheight = int(round((20.0/width*height),0)) #resize height according to ratio width
if (nheigth == 0): #rare case but minimum is 1 pixel
nheigth = 1
# resize and sharpen
img = im.resize((20,nheight), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wtop = int(round(((28 - nheight)/2),0)) #caculate horizontal pozition
newImage.paste(img, (4, wtop)) #paste resized image on white canvas
else:
#Height is bigger. Heigth becomes 20 pixels.
nwidth = int(round((20.0/height*width),0)) #resize width according to ratio height
if (nwidth == 0): #rare case but minimum is 1 pixel
nwidth = 1
# resize and sharpen
img = im.resize((nwidth,20), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wleft = int(round(((28 - nwidth)/2),0)) #caculate vertical pozition
newImage.paste(img, (wleft, 4)) #paste resized image on white canvas
#newImage.save("sample.png")
tv = list(newImage.getdata()) #get pixel values
#normalize pixels to 0 and 1. 0 is pure white, 1 is pure black.
tva = [ (255-x)*1.0/255.0 for x in tv]
return tva
You can use OpenCV contours to locate potential digits within your actual image, some of the techniques will depend on the actual data you are working from. There is an example of digit candidate location at http://www.pyimagesearch.com/2017/02/13/recognizing-digits-with-opencv-and-python/
that can give you some pointers.
However, you may get problems with some scripts as I believe that while in all European scripts every digit is supposed to be contiguous and distinct I am not sure that both points apply to all scripts.

Python PIL: image crop with black area if not enough photo region

I want to make thumb and crop it to needed size. It works fine, but if my new thumb area is smaller than crop one, all empty space fills with black color.
Code:
import os
from PIL import Image
def resize(file_path):
file, ext = os.path.splitext(file_path)
im = Image.open(file_path)
size = (100, 'auto')
new_path = file + "_.jpg"
im.thumbnail(size, Image.ANTIALIAS)
region = im.crop((0, 0, 100, 100))
region.save(new_path, "JPEG")
Maybe there is some option like max_height for crop method or anything else?
Thanks!
You will need to apply some simple algorithm there instead of a blind cropping.
Get the square of maximum size possible in the image with square center aligning with the center of the image.
Square of maximum size would be having side equal to max of height or width of the image.
After getting the square, resample it to the size of your thumbnail dimensions.
This should work fine for most images, however if you are generating thumbnails for face images, this might not be a good method, and you might need some face recognition techniques for better output.
Are you trying to only conditionally crop the image if its LARGER than 100x100?
If so,
def resize(file_path):
file, ext = os.path.splitext(file_path)
im = Image.open(file_path)
size = (100, 'auto')
new_path = file + "_.jpg"
im.thumbnail(size, Image.ANTIALIAS)
if im.size[1] > 100:
im = im.crop((0, 0, 100, 100))
im.save(new_path, "JPEG")
I found solution:
import os
from PIL import Image
def resize(file_path):
file, ext = os.path.splitext(file_path)
im = Image.open(file_path)
size = (100, 'auto')
new_path = file + "_.jpg"
im.thumbnail(size)
(width, height) = im.size
if height >= width: box = (0, 0, 100, 100)
else: box = (0, 0, 100, height)
region = im.crop(box)
region.save(new_path, "JPEG")
Thanks for your responses!
I would do it this way:
If the image is wide, then scale it to be 100px tall. If it's tall, scale it to be 100px wide.
Crop out the middle 100x100.
def resize(file_path):
file, ext = os.path.splitext(file_path)
im = Image.open(file_path)
w, h = im.size
size = (100, 'auto') if h > w else ('auto', 100)
new_path = file + "_.jpg"
im.thumbnail(size, Image.ANTIALIAS)
w, h = im.size
region = im.crop((w/2 - 50, h/2 - 50, w/2 + 50, h/2 + 50))
region.save(new_path, "JPEG")

Categories