When opening image with PILLOW the image is modified - python

When opening the postscript image with online tools all the pixels align correctly but when using pillow, the pixels are in different sizes.
[Image of the problem]
[Image of the desired result]
def saveFile(canvas:tk.Canvas):
EpsImagePlugin.gs_windows_binary = r'C:\Program Files\gs\gs9.56.1\bin\gswin64c'
file_name = "img"
canvas.postscript(file=f"images\ps\{file_name}.ps", colormode='color')
psimage=Image.open(f'images\ps\{file_name}.ps')
psimage.save(f'images\png\{file_name}.png', "png")
psimage.close()
Keep in mind the pixels are not the size of a 'real' pixel, they are much bigger and changing the format to 'png' or 'jpg' didn't solve the problem.
If someone knows the solution to this problem, I will greatly appreciate it.
Sorry for the missing information, hopefully this is enough.
Img.ps file as text
And this is how the code generates the .ps file
import tkinter as tk
import random
from save import saveFile
pixels = []
FIELD_SIZE = (1280, 720)
PIXEL_SIZE = 10
class random_pixels():
def draw_full_screen(canvas):
height = round(FIELD_SIZE[1] / PIXEL_SIZE)
width = round(FIELD_SIZE[0] / PIXEL_SIZE)
for y in range(height):
for x in range(width):
color = ["#"+''.join([random.choice('ABCDEF0123456789') for i in range(6)])]
# color = "#444"
x_top_left = x * PIXEL_SIZE + 1
y_top_left = y * PIXEL_SIZE + 1
x_bottom_right = x_top_left + PIXEL_SIZE - 1
y_bottom_right = y_top_left + PIXEL_SIZE - 1
resolution = width * height
util.draw_pixel(canvas, color, x_top_left, x_bottom_right, y_top_left, y_bottom_right, resolution)
canvas.update()
canvas.update()
print("\nPixels drawn!")
print("\nSaving image...")
saveFile(canvas)
canvas.focus_set()
print('\nImage saved!')
class util:
def draw_pixel(canvas:tk.Canvas, color, x0, x, y0, y, pixels_to_draw=1):
pixel = canvas.create_rectangle(x0, y0, x, y, fill=color, outline=color)
pixels.append(pixel)
print(f"{len(pixels)}/{pixels_to_draw} | { round(len(pixels) / pixels_to_draw * 100, 2)}%")
return None
def get_theme(e, canvas, root):
if len(pixels) != 0:
canvas.delete('all')
pixels.clear()
root.focus_set()
if e.char == "g":
random_pixels.draw_full_screen(canvas)
canvas.focus_set()

Related

Zoom Into Image With OpenCV

I have the following picture as an example:
529 x 550 px (100 %)
As a target I would like to have the image zoomed to about
150 %, but it should still be
529 x 550 px:
I was able to write the code using PIL, but I want to have it with Cv2. Can someone help me please?
from PIL import Image
import cv2 as cv
def zoom_at(img, x, y, zoom):
w, h = img.size
zoom2 = zoom * 2
img = img.crop((x - w / zoom2, y - h / zoom2,
x + w / zoom2, y + h / zoom2))
return img.resize((w, h), Image.LANCZOS)
img = Image.open("image.png")
img = zoom_at(img, 264.5, 275, 1.5)
img = img.save('image_zoomed.png')
#Ofer Sadan
import cv2 as cv
def zoom(img, zoom_factor=1.5):
return cv.resize(img, None, fx=zoom_factor, fy=zoom_factor)
img = cv.imread('original.png')
# Original: 529 × 550
height, width = img.shape[:2]
zoomed = zoom(img, 1.5)
# Zoomed: 794 × 825
cropped = zoomed[0:550, 0:529] # Wrong area
# Now I want to crop the middle of the new image as variable.
cv.imwrite('zoomed.png', zoomed)
cv.imwrite('cropped.png', cropped)
There you go:
cv:
import cv2 as cv
def zoom_at(img, zoom=1, angle=0, coord=None):
cy, cx = [ i/2 for i in img.shape[:-1] ] if coord is None else coord[::-1]
rot_mat = cv2.getRotationMatrix2D((cx,cy), angle, zoom)
result = cv2.warpAffine(img, rot_mat, img.shape[1::-1], flags=cv2.INTER_LINEAR)
return result
Laymans manual:
import cv2 as cv
def zoom_at(img, zoom, coord=None):
"""
Simple image zooming without boundary checking.
Centered at "coord", if given, else the image center.
img: numpy.ndarray of shape (h,w,:)
zoom: float
coord: (float, float)
"""
# Translate to zoomed coordinates
h, w, _ = [ zoom * i for i in img.shape ]
if coord is None: cx, cy = w/2, h/2
else: cx, cy = [ zoom*c for c in coord ]
img = cv.resize( img, (0, 0), fx=zoom, fy=zoom)
img = img[ int(round(cy - h/zoom * .5)) : int(round(cy + h/zoom * .5)),
int(round(cx - w/zoom * .5)) : int(round(cx + w/zoom * .5)),
: ]
return img
img = cv.imread('x3Lkg.png')
cv.imwrite('x3Lkg_zoomed.png', zoom_at(img, 1.5, coord=(264.5, 275)) )
I have a little snippet I used a while ago that I can't currently test so let me know if it actually works or not
import cv2 as cv
def zoom(img, zoom_factor=2):
return cv.resize(img, None, fx=zoom_factor, fy=zoom_factor)
And you can crop before the zoom or after it as you wish:
img = cv.imread(img_path)
cropped = img[200:300, 150:250]
zoomed = zoom(img, 3)
zoomed_and_cropped = zoom(cropped, 3)
For anyone who does not want to the math manually this works for me.
import cv2
def zoom_center(img, zoom_factor=1.5):
y_size = img.shape[0]
x_size = img.shape[1]
# define new boundaries
x1 = int(0.5*x_size*(1-1/zoom_factor))
x2 = int(x_size-0.5*x_size*(1-1/zoom_factor))
y1 = int(0.5*y_size*(1-1/zoom_factor))
y2 = int(y_size-0.5*y_size*(1-1/zoom_factor))
# first crop image then scale
img_cropped = img[y1:y2,x1:x2]
return cv2.resize(img_cropped, None, fx=zoom_factor, fy=zoom_factor)
# read original
img = cv2.imread('original.png')
# call our function
img_zoomed_and_cropped = zoom_center(img)
# write zoomed and cropped version
cv.imwrite('zoomed_and_cropped.png', img_zoomed_and_cropped)
Notice that I first cropped and then rescaled. It is more efficient and you will notice it when dealing with a live video feed.
For putting a specific point in the input image at a specific point in the output image, with a precise scale factor, you would want to use cv.warpAffine.
This function requires you to build a transformation matrix. That is easy.
def translate(tx=0, ty=0):
T = np.eye(3)
T[0:2,2] = [tx, ty]
return T
def scale(s=1, sx=1, sy=1):
T = np.diag([s*sx, s*sy, 1])
return T
def rotate(degrees):
T = np.eye(3)
# just involves some sin() and cos()
T[0:2] = cv.getRotationMatrix2D(center=(0,0), angle=-degrees, scale=1.0)
return T
im = cv.imread("x3Lkg.png")
(ih,iw) = im.shape[:2] # input height, input width
# parameters
scale_factor = 10
angle_degrees = 15
(ow, oh) = (529, 550) # output size
(icx, icy) = (459, 352) # zoom onto that pixel in input
(ocx, ocy) = ((ow-1)/2, (oh-1)/2) # put there in output (it's the exact center)
# the transformation, read from right to left
H = translate(+ocx, +ocy) # rotate(degrees=angle_degrees) # scale(scale_factor) # translate(-icx, -icy)
# assume that H is affine, not a full homography
assert np.allclose(H[2], [0,0,1])
M = H[0:2]
# produce the picture
# use INTER_LINEAR, INTER_CUBIC, INTER_LANCZOS4 for smooth interpolation
# use INTER_AREA for scale factors much below 1
out = cv.warpAffine(im, dsize=(ow,oh), M=M, flags=cv.INTER_NEAREST)
# imshow(out)

python Pill applying function on images and saving them works only on first image

I'm trying to take a few png images add them an enumerated grid and save them each image by itself as a single tiff file
the output I get is, first image with grid and numbers as required,
the first image is shorter then the others if that matters
other images are only numbered but without a grid
this is my code
from PIL import Image, ImageDraw, ImageOps, ImageFont
import os
import glob
path = r'D:\in'
out = r"d:\1.tif"
font = ImageFont.truetype(r"D:\python1\New folder\keyer_layout\films.EPISODE1.ttf",32)
def add_grid(path):
im = Image.open(path)
im = ImageOps.expand(im, border=50, fill = 'rgb(255,255,255)') #add margin to the image
draw = ImageDraw.Draw(im)
y_start = 0
y_end = im.height
step_size = int(im.width / 10)
li = 0
for x in range(0, im.width, step_size):
line = ((x, y_start), (x, y_end))
draw.line(line, fill=200)
draw.text((x, y_start),str(li),'rgb(0,0,0)',font=font)
li+=1
x_start = 0
x_end = im.width
li = 0
for y in range(0, im.height, step_size):
line = ((x_start, y), (x_end, y))
draw.line(line, fill=128)
draw.text((x_start, y),str(li),'rgb(0,0,0)',font=font)
li+=1
del draw
return im
pics_path = os.path.join(path,"*.png")
pics = glob.glob(pics_path)
pics_arr = []
for i in pics:
pics_arr.append(add_grid(i))
pics_arr[0].save(r"d:\test.tif", append_images = pics_arr[1:],save_all = True)
I tried to add im.show() inside the function the images looked the same first one with grid others without
when I tried to skip the first image, non of the images had a grid on it
Thanks
Firstly, instead of the last line there should be something like this:
for i, pict in enumerate(pics_arr):
pict.save(r"d:\test" + str(i) + ".tif")
This way you will get all your images with the grid on them as a bunch of tif files.
After that you need to combine them into one big file. It can be done this way: Combine several images horizontally with Python
The final script could look like this:
from PIL import Image, ImageDraw, ImageOps, ImageFont
import os
import glob
path = r'd:\in'
out = r'd:\out'
# font = ImageFont.truetype() <-- sorry, I have no your font at hand
def add_grid(path):
im = Image.open(path)
im = ImageOps.expand(im, border=50, fill = 'rgb(255,255,255)') #add margin to the image
draw = ImageDraw.Draw(im)
y_start = 0
y_end = im.height
step_size = int(im.width / 10)
li = 0
for x in range(0, im.width, step_size):
line = ((x, y_start), (x, y_end))
draw.line(line, fill=(200,200,200)) # <----- light gray RGB color
draw.text((x, y_start),str(li),'rgb(0,0,0)') #,font=font)
li+=1
x_start = 0
x_end = im.width
li = 0
for y in range(0, im.height, step_size):
line = ((x_start, y), (x_end, y))
draw.line(line, fill=(128,128,128)) # <----- gray RGB color
draw.text((x_start, y),str(li),'rgb(0,0,0)') #,font=font)
li+=1
del draw
return im
pics_path = os.path.join(path,"*.png")
pics = glob.glob(pics_path)
pics_arr = []
for i in pics:
pics_arr.append(add_grid(i))
# save gridded images
for i, pict in enumerate(pics_arr):
pict.save(out +"\test" + str(i) + ".png")
# combine the gridded images into the one tif
pics_path = os.path.join(out, "*.png")
pics = glob.glob(pics_path)
images = [Image.open(x) for x in pics]
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(out + '/test.tif')
Input:
Step 1:
Output:

How can I solve Python 3 PIL putalpha problem?

I am using putalpha function for my project. But I have a problem.
When I don't use the putalpha:
enter image description here
When I use the putalpha:
enter image description here
How can I solve this problem ?
Code:
def add_logo(pos, size=5, rotation=0, alpha=255):
mainim = Image.open("resim.png").convert("RGB")
logoim = Image.open("pawpink.png").convert("RGBA")
logoim = logoim.rotate(rotation, expand=1)
logoim.putalpha(alpha)
#Calculate size
width, height = mainim.size
width = width / size
oran = (logoim.size[0] / logoim.size[1])
height = (width * (oran ** -1))
logoim = logoim.resize((int(width), int(height)))
mainim.paste(logoim, box=pozisyon_getir_resim(pos), mask=logoim)
return mainim
Images:
cat.png
logo.png
I found this excellent article Watermark with PIL (Python recipe) and was able to get your program to work.
Here is my version (complete, tested):
import PIL.Image
import PIL.ImageEnhance
def pozisyon_getir_resim(pos):
return (pos, pos)
def reduce_opacity(im, opacity):
"""Returns an image with reduced opacity."""
assert opacity >= 0 and opacity <= 1
if im.mode != 'RGBA':
im = im.convert('RGBA')
else:
im = im.copy()
alpha = im.split()[3]
alpha = PIL.ImageEnhance.Brightness(alpha).enhance(opacity)
im.putalpha(alpha)
return im
def add_logo(pos, size=5, rotation=0, alpha=255):
mainim = PIL.Image.open("cat.png").convert("RGB")
logoim = PIL.Image.open("logo.png").convert("RGBA")
logoim = logoim.rotate(rotation, expand=1)
logoim = reduce_opacity(logoim, alpha/255.0)
# Calculate size
width, height = mainim.size
width = width / size
oran = (logoim.size[0] / logoim.size[1])
height = (width * (oran ** -1))
logoim = logoim.resize((int(width), int(height)))
if mainim.mode != 'RGBA':
mainim.convert('RGBA')
layer = PIL.Image.new('RGBA', mainim.size, (0, 0, 0, 0))
layer.paste(logoim, pozisyon_getir_resim(pos))
return PIL.Image.composite(layer, mainim, layer)
mainim = add_logo(32, 5, 0, 127)
mainim.save('cat_with_logo.png', 'PNG')
Result:

Python PIL concatenate images

I'm working on a project where I need to concatenate a lot of images (80282). Each image is 256 x 256 pixels, and some of the files are empty (no image), so I need to create a blank image to replace the file. I have the data in this format: data-D0H0-X52773-Y14041
X and Y correspond to the coordinates that I need to concatenate in order. The order is from the top left X52773-Y14314 to the bottom right X52964-Y14041. It is 294 iterations on X and 274 on Y. Here is the code I have written which is not working correctly, I could use any help if you have an idea, currently, my images are not well aligned on Y. For example, the image X10-Y10 is not under the image X10-Y11 as it should. I think I have some problem using correctly the try: and except:
Thanks for you help !
from PIL import Image
width = 75264
height = 70144
new_im = Image.new('RGBA', (75264, 70144))
x_offset = 0
y_offset = 0
coordinate = {}
coordinate['x']=52672
coordinate['y']=14314
#top image line should be from: X52,672-Y14,314 to X52,965-Y14,314
#bottom image line should be from: X52,672-Y14,041 to X52,965-Y14,041
for irow in range(0, 274):
for icol in range(0, 294):
try:
if (x_offset == width):
coordinate['y'] = coordinate['y'] - 1
coordinate['x'] = 52672
img = Image.open("data-D0H0-X"+str(coordinate['x'])+"-Y"+str(coordinate['y'])+".png")
except:
coordinate['x'] = coordinate['x'] + 1
blank = Image.new('RGBA', (256,256))
new_im.paste(blank, (x_offset, y_offset))
x_offset += 256
if (x_offset == width):
x_offset = 0
y_offset += 256
break
new_im.paste(img, (x_offset, y_offset))
x_offset += 256
if (x_offset == width):
x_offset = 0
y_offset += 256
coordinate['x'] = coordinate['x'] + 1
new_im.show()
new_im.save('full_image.png')
EDIT:
Here is the new code I've modified according to your answer. However, I'm still getting an error:
struct.error: 'I' format requires 0 <= number <= 4294967295
Not sure if my coordinate calcul is right now.
CODE:
from PIL import Image
import glob
import imghdr
width = 75264
height = 70144
new_im = Image.new('RGBA', (width, height))
for filename in glob.glob('data-D0H0-X*.png'):
tmp_arr = filename.split('-')
x_coord = int(tmp_arr[2][1:6])
y_coord = int(tmp_arr[3][1:6])
info = imghdr.what(filename)
if (info == "png"):
new_img = Image.open(filename)
else:
new_img = Image.new('RGBA', (256,256))
x_coord = (x_coord-52672)*256
y_coord = (14314-y_coord)*256
print x_coord, y_coord
new_im.paste(new_img, (x_coord, y_coord))
new_im.show()
new_im.save('full_image.png')
Your coordinate arithmetic seems a bit off. Since your images are 256x256 you should never have to inc/dec x and y by 1 as you do in your code.
The code below hasn't been tested but should provide a general outline.
from PIL import Image
import glob
width = 75264
height = 70144
new_im = Image.new('RGBA', (width, height))
for filename in glob.glob('data-D0H0-X*.png'):
tmp_arr = filename.split('-')
x_coord = int(tmp_arr[2][1:])
y_coord = int(tmp_arr[3][1:])
small_img = Image.open(filename)
new_im.paste(small_img, (x_coord, y_coord))
new_im.show()
new_im.save('full_image.png')

Array of Matrices in Python

as the title says, I'm trying to define a array of matrices (witch represents images) in Python. But, when I try to read a matrix, I got this message: "ImageData instance has no attribute 'getitem'""
I'm starting to study Python these days, so I know that this must be simple for a lot of people, but I don't know what's wrong. This is my code:
ImageData.py
import random
import math
class ImageData:
def __init__ (self, width, height):
self.width = width
self.height = height
self.data = []
for i in range(width):
self.data.append([0] * height)
def set_data (self, x, y, value):
self.data[x][y] = value
def generate_voronoi_diagram (self, seeds):
nx = []
ny = []
nr = []
ng = []
nb = []
for i in range(seeds):
# Generate a cell position
pos_x = random.randrange(self.width)
pos_y = random.randrange(self.height)
nx.append(pos_x)
ny.append(pos_y)
# Save the rgb data
nr.append(random.randrange(256))
ng.append(random.randrange(256))
nb.append(random.randrange(256))
for x in range(self.width):
for y in range(self.height):
# Return the Euclidean norm
d_min = math.hypot(self.width-1, self.height-1)
j = -1
for i in range(seeds):
# The distance from a cell to x, y point being considered
d = math.hypot(nx[i]-x, ny[i]-y)
if d < d_min:
d_min = d
j = i
self.data[x][y] = [nr[j], ng[j], nb[j]]
UncertaintyVisualisaton.py
from PIL import Image
import numpy
import ImageData
def generate_uncertainty_visualisation (images, width, height):
image = Image.new("RGB", (width, height))
putpixel = image.putpixel
r = g = b = []
for i in range(width):
r.append([0] * height)
g.append([0] * height)
b.append([0] * height)
for i in range(len(images)):
image = images[i]
for x in range(width):
for y in range(height):
#Error here
rgb = image[x][y]
r[x][y] += rgb[0]
g[x][y] += rgb[1]
b[x][y] += rgb[2]
for x in range(width):
for y in range(height):
r[x][y] /= len(images)
g[x][y] /= len(images)
b[x][y] /= len(images)
putpixel((x, y), (r[x][y], g[x][y], b[x][y]))
image.save("output.png", "PNG")
if __name__ == "__main__":
width = 10;
height = 10;
entries = []
seeds = numpy.random.poisson(20)
images = 1
for n in range(images):
entry = ImageData.ImageData(width, height)
entry.generate_voronoi_diagram(seeds)
entries.append(entry)
generate_uncertainty_visualisation(entries, width, height)
Any help would be very appreciated.
Thanks.
In UncertaintyVisualisaton.py first you set:
image = Image.new("RGB", (width, height))
And then looping over images you reassign:
image = images[i]
This is probably not want you want.
Also your error:
#Error here
rgb = image[x][y]
is happening because ImageData is not a list. The data attibute in it is:
#no more Error here
rgb = image.data[x][y]

Categories

Resources