Printing full document with PySide/Qt - python

Currently I'm busy with developing an application which converts a PDF to PNG and uses the PNG image to print it out to a printer.
The problem is that I can print out an image, but I don't understand how to resize it in a way that it's always full-size on the paper. Offcourse I want to set some margins, but the image has to be re-sized in a way that it fits exactly.
The problem is that I really don't have a clue how to do this, since the documentation is very limited.
This is my current code to print the image:
#set up printer
printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
printer.setPrinterName('Adobe PDF') #I print to my Adobe PDF software printer
#set up image
image = QtGui.QImage(pngFiles[0])
#paint & print
painter = QtGui.QPainter()
painter.begin(printer)
painter.drawImage(100,100, image)
painter.end()
I think the solution is in this line:
painter.drawImage(100,100, image)
It gives the image a margin of 100 from the sides, but it does not scale. How can I scale the image in a way that it fits the document? I'm especially looking for a solution that looks to the default document size of the printer.

You can get the document's size using QPrinter::paperSize.
For resizing the image when drawing it, use the overloaded version of QPainter::drawImage which takes a QRectF, not a QPoint. The image will then be scaled to fit in the destination QRectF.

Based on the conversation with Sashoalm I could make resize the image and fit it nicely on the paper for printing. I've stripped down my expirmental code and it should work like this.
from PIL import Image
imagefile = 'image.png'
def scale(w, h, x, y, maximum=True):
nw = y * w / h
nh = x * h / w
if maximum ^ (nw >= x):
return nw or 1, y
return x, nh or 1
#set up print printer.
printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)
#dlg = QtGui.QPrintDialog(printer, self)
printer.setPrinterName('Adobe PDF')
#check image size with PIL:
image = Image.open(imagefile)
imageWidth, imageHeight = image.size
paperPixels = printer.pageRect(QtGui.QPrinter.DevicePixel)
paperPixels = paperPixels.getRect() #get tuple of the "pixels on the paper"
paperWidth = paperPixels[2]
paperHeight = paperPixels[3]
#set margins for paper
paperMargin = 100
#find out the image size
paperWidth = paperWidth - (paperMargin*2) #times two, for left and right..
paperHeight = paperHeight - (paperMargin*2)
#scale image within a rectangle.
paintWidth, paintHeight = scale(imageWidth, imageHeight, paperWidth, paperHeight, True)
#construct the paint dimensions area
paintRect = QtCore.QRectF(paperMargin, paperMargin, paintWidth, paintHeight)
#start painting
image = QtGui.QImage(imagefile)
painter = QtGui.QPainter()
painter.begin(printer)
painter.drawImage(paintRect, image)
painter.end()
#now the page gets printed out and the image should fit the paper.

Related

I get an 'AttributeError: shape' when trying to rotate an image with OpenCV

I am trying to import, resize, and conditionally rotate an image with OpenCV but I'm running into some trouble. To bring in the image and resize it I use:
def draw_plane(self):
# Import image for plane
plane_path = 'planes/' + self.plane + '.jpg'
plane = Image.open(plane_path)
# Get image size
bg_height = plane.size[1]
bg_width = plane.size[0]
# Resize and crop image
if bg_height > bg_width:
# Resize
ratio = bg_width/bg_height
img_width = self.p_width
img_height = int(self.p_height/ratio)
plane_resized = plane.resize((img_width,img_height))
# Crop
top = int((img_height-self.p_height)/2)
bottom = int(((img_height-self.p_height)/2)+self.p_height)
plane_cropped = plane_resized.crop((0,top,self.p_width,bottom))
print('top:',top,'\nbottom:',bottom)
self.plane_img = plane_cropped
if bg_height < bg_width:
# Resize
ratio = bg_height/bg_width
img_width = int(self.p_width/ratio)
img_height = self.p_height
plane_resized = plane.resize((img_width,img_height))
# Crop
left = int((img_width-self.p_width)/2)
right = int(((img_width-self.p_width)/2)+self.p_width)
plane_cropped = plane_resized.crop((left,0,right,self.p_height))
self.plane_img = plane_cropped
else:
pass
If the name of an image being used as a frame for plane is in a list I call the following method and if the first item in a list of attributes for the final composition is "Polaroid" I want it to rotate plane.
def adjust_plane(self):
if a.attr[0] == 'polaroid':
plane = self.plane_img
height, width = plane.shape[:2] <----
center = (width/2, height/2)
rotate_matrix = cv2.getRotationMatrix2D(center=center, angle=-30, scale=1)
rotated_plane = cv2.warpAffine(plane, rotate_matrix, (width, height))
self.plane_img = rotated_plane
But when I run the code I get: "AttributeError: shape" on the line I noted in the code block. This is all taking place in the same class, including the conditional that triggers adjust_plane().
I admit that I am at a point in learning to program that I am just beginning to wrap my head around objects as a concept. Is there maybe some issue that this is no longer an image but is an "image object", if there is such a thing? Any help is appreciated, I've been chewing on this error for far too long.
It appears that the issue is that OpenCV and PIL images don't play well together. Someone else could better explain exactly why.
I brought in OpenCV to rotate the image because I thought PIL could not rotate an image by specific degree rather than just 90° steps, but I was wrong about that. The code below accomplishes what I wanted with the PIL library and I was able to do away with OpenCV.
plane.rotate(30, Image.NEAREST, expand = 1)

Python GUI to label and train SLIC images

Hello I am currently trying to develop a python GUI using tkinter to train a CNN. For that I need a button to label SLIC superpixel images. Can someone help me out on how to do that.
This is the part of a code I am using but I need make SLIC images appear in GUI instead of separate window.
'''
def _load_image(path):
"""
Loads and resizes an image from a given path using the Pillow library
:param path: Path to image
:return: Resized or original image
"""
image = Image.open(path);segments = slic(image, n_segments = 300, compactness=30, sigma = 1);fig = plt.figure("Superpixels -- %d segments%");ax = fig.add_subplot(1, 1, 1);pimg=mark_boundaries(image, segments);ax.imshow(pimg);plt.axis("off");x=plt.show()
if(resize):
max_height = 500
img = image;
s = img.size
ratio = max_height / s[1]
image = img.resize((int(s[0]*ratio), int(s[1]*ratio)), Image.ANTIALIAS)
return pimg
'''
Kindly help me out in this

Shrink image using python?

import image
img = image.Image("test_image.png")
ratio = 0.5
#Make new image screen.
new_width = img.getWidth()*(ratio)
new_height = img.getHeight()*(ratio)
new_img = image.EmptyImage(new_width,new_height)
win = image.ImageWin(new_width,new_height)
for col in range(img.getHeight()):
for row in range(img.getWidth()):
p = img.getPixel(row*ratio, col*ratio) #Color value stays the same.
new_img.setPixel(row*ratio,col*ratio,p)
new_img.draw(win)
win.exitonclick()
This is my attempt to shrink an image using Python. I know my algorithm is clearly not correct but I can't seem to wrap my head around what I should do instead.
It's giving me an image of the two upper corners of the original image put side by side in my new smaller image screen. What am I missing?

Adding borders to an image using python

I have a large number of images of a fixed size (say 500*500). I want to write a python script which will resize them to a fixed size (say 800*800) but will keep the original image at the center and fill the excess area with a fixed color (say black).
I am using PIL. I can resize the image using the resize function now, but that changes the aspect ratio. Is there any way to do this?
You can create a new image with the desired new size, and paste the old image in the center, then saving it. If you want, you can overwrite the original image (are you sure? ;o)
import Image
old_im = Image.open('someimage.jpg')
old_size = old_im.size
new_size = (800, 800)
new_im = Image.new("RGB", new_size) ## luckily, this is already black!
box = tuple((n - o) // 2 for n, o in zip(new_size, old_size))
new_im.paste(old_im, box)
new_im.show()
# new_im.save('someimage.jpg')
You can also set the color of the new border with a third argument of Image.new() (for example: Image.new("RGB", new_size, "White"))
Yes, there is.
Make something like this:
from PIL import Image, ImageOps
ImageOps.expand(Image.open('original-image.png'),border=300,fill='black').save('imaged-with-border.png')
You can write the same at several lines:
from PIL import Image, ImageOps
img = Image.open('original-image.png')
img_with_border = ImageOps.expand(img,border=300,fill='black')
img_with_border.save('imaged-with-border.png')
And you say that you have a list of images. Then you must use a cycle to process all of them:
from PIL import Image, ImageOps
for i in list-of-images:
img = Image.open(i)
img_with_border = ImageOps.expand(img,border=300,fill='black')
img_with_border.save('bordered-%s' % i)
Alternatively, if you are using OpenCV, they have a function called copyMakeBorder that allows you to add padding to any of the sides of an image. Beyond solid colors, they've also got some cool options for fancy borders like reflecting or extending the image.
import cv2
img = cv2.imread('image.jpg')
color = [101, 52, 152] # 'cause purple!
# border widths; I set them all to 150
top, bottom, left, right = [150]*4
img_with_border = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
Sources: OpenCV border tutorial and
OpenCV 3.1.0 Docs for copyMakeBorder
PIL's crop method can actually handle this for you by using numbers that are outside the bounding box of the original image, though it's not explicitly stated in the documentation. Negative numbers for left and top will add black pixels to those edges, while numbers greater than the original width and height for right and bottom will add black pixels to those edges.
This code accounts for odd pixel sizes:
from PIL import Image
with Image.open('/path/to/image.gif') as im:
old_size = im.size
new_size = (800, 800)
if new_size > old_size:
# Set number of pixels to expand to the left, top, right,
# and bottom, making sure to account for even or odd numbers
if old_size[0] % 2 == 0:
add_left = add_right = (new_size[0] - old_size[0]) // 2
else:
add_left = (new_size[0] - old_size[0]) // 2
add_right = ((new_size[0] - old_size[0]) // 2) + 1
if old_size[1] % 2 == 0:
add_top = add_bottom = (new_size[1] - old_size[1]) // 2
else:
add_top = (new_size[1] - old_size[1]) // 2
add_bottom = ((new_size[1] - old_size[1]) // 2) + 1
left = 0 - add_left
top = 0 - add_top
right = old_size[0] + add_right
bottom = old_size[1] + add_bottom
# By default, the added pixels are black
im = im.crop((left, top, right, bottom))
Instead of the 4-tuple, you could instead use a 2-tuple to add the same number of pixels on the left/right and top/bottom, or a 1-tuple to add the same number of pixels to all sides.
It is important to consider old dimension, new dimension and their difference here. If the difference is odd (not even), you will need to specify slightly different values for left, top, right and bottom borders.
Assume the old dimension is ow,oh and new one is nw,nh.
So, this would be the answer:
import Image, ImageOps
img = Image.open('original-image.png')
deltaw=nw-ow
deltah=nh-oh
ltrb_border=(deltaw/2,deltah/2,deltaw-(deltaw/2),deltah-(deltah/2))
img_with_border = ImageOps.expand(img,border=ltrb_border,fill='black')
img_with_border.save('imaged-with-border.png')
You can load the image with scipy.misc.imread as a numpy array. Then create an array with the desired background with numpy.zeros((height, width, channels)) and paste the image at the desired location:
import numpy as np
import scipy.misc
im = scipy.misc.imread('foo.jpg', mode='RGB')
height, width, channels = im.shape
# make canvas
im_bg = np.zeros((height, width, channels))
im_bg = (im_bg + 1) * 255 # e.g., make it white
# Your work: Compute where it should be
pad_left = ...
pad_top = ...
im_bg[pad_top:pad_top + height,
pad_left:pad_left + width,
:] = im
# im_bg is now the image with the background.
ximg = Image.open(qpath)
xwid,xhgt = func_ResizeImage(ximg)
qpanel_3 = tk.Frame(Body,width=xwid+10,height=xhgt+10,bg='white',bd=5)
ximg = ximg.resize((xwid,xhgt),Image.ANTIALIAS)
ximg = ImageTk.PhotoImage(ximg)
panel = tk.Label(qpanel_3,image=ximg)
panel.image = ximg
panel.grid(row = 2)
from PIL import Image
from PIL import ImageOps
img = Image.open("dem.jpg").convert("RGB")
This part will add black borders at the sides (10% of width)
img_side = ImageOps.expand(img, border=(int(0.1*img.size[0]),0,int(0.1*img.size[0]),0), fill=(0,0,0))
img_side.save("sunset-sides.jpg")
This part will add black borders at the bottom & top (10% of height)
img_updown = ImageOps.expand(img, border=(0,int(0.1*img.size[1]),0,int(0.1*img.size[1])), fill=(0,0,0))
img_updown.save("sunset-top_bottom.jpg")
This part will add black borders at the bottom,top & sides (10% of height-width)
img_updown_side = ImageOps.expand(img, border=(int(0.1*img.size[0]),int(0.1*img.size[1]),int(0.1*img.size[0]),int(0.1*img.size[1])), fill=(0,0,0))
img_updown_side.save("sunset-all_sides.jpg")
img.close()
img_side.close()
img_updown.close()
img_updown_side.close()

Make a thumbnail with PIL, enhanced way

I know there is a thumbnail method in PIL. What I want to make differently is how it resizes the original image. Assume I have a 300x360px vertical image. I want to resize it to a constrained box that is 150x100px horizontal image. So I need to find the smallest side of the original image, resize to it, and then crop the rest to the center from the largest side.
How can I do it?
from PIL import Image
width = 150
height = 100
infile = Image.open(in_filename)
im = infile.copy()
if im.size[0] >= im.size[1]:
im = im.resize((height * im.size[0]/im.size[1], height))
im = im.crop(((im.size[0] - width)/2, 0, (im.size[0] + width)/2, height))
else:
im = im.resize((width, width * im.size[1]/im.size[0]))
im = im.crop((0, (im.size[1] - height)/2, width, (im.size[1] + height)/2))
im.save(out_filename)
There might be a faster way to do this, but this should work.

Categories

Resources