I try to use OCR (Optical Character Reader) for a lot of documents of the same type. I use pdf2image library for Python. But when it sees pdfs with AutoCAD shx text it captures the bounding boxes around text as well. At first they are not visible on pdf. You need to click on text to see the boxes. But they appear in jpg result after conversion.
Here's an image of a part of pdf document:
crop from actual pdf
And here's the output of conversion: pdf after conversion
I expect somethink like that:
pdf after conversion how it should be
Here's my function for pdf2image conversion:
def get_image_for_blueprint(path, dpi):
"""Create image for full blueprint from path
path: path to pdf file
"""
from pdf2image import convert_from_bytes
images = convert_from_bytes(open(path, 'rb').read(), dpi=dpi) # Actual conversion function
for i in images:
width, height = i.size
if width < height:
continue
else:
print(i.size) # tuple : (width, height)
image = i.resize((4096, int(height / width * 4096)))
enhancer = ImageEnhance.Sharpness(image)
image = enhancer.enhance(2) # Sharpness
enhancer = ImageEnhance.Color(image)
image = enhancer.enhance(0) # black and white
enhancer = ImageEnhance.Contrast(image)
image = enhancer.enhance(2) # Contrast
image = np.asarray(image) # array
image = image.astype(np.uint8)
return image
I found solutions to be made in AutoCAD before saving the document but I can not really find a way to get rid of these boxes having pdf only.(in python or c++)
Maybe it's possible to resolve using any other programming language library or additional software.
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)
I have a gif that I would like to resize with pillow so that its size decreases. The current size of the gif is 2MB.
I am trying to
resize it so its height / width is smaller
decrease its quality.
With JPEG, the following piece of code is usually enough so that large image drastically decrease in size.
from PIL import Image
im = Image.open("my_picture.jpg")
im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS) # decreases width and height of the image
im.save("out.jpg", optimize=True, quality=85) # decreases its quality
With a GIF, though, it does not seem to work. The following piece of code even makes the out.gif bigger than the initial gif:
im = Image.open("my_gif.gif")
im.seek(im.tell() + 1) # loads all frames
im.save("out.gif", save_all=True, optimize=True, quality=10) # should decrease its quality
print(os.stat("my_gif.gif").st_size) # 2096558 bytes / roughly 2MB
print(os.stat("out.gif").st_size) # 7536404 bytes / roughly 7.5MB
If I add the following line, then only the first frame of the GIF is saved, instead of all of its frame.
im = im.resize((im.size[0] // 2, im.size[1] // 2), Image.ANTIALIAS) # should decrease its size
I've been thinking about calling resize() on im.seek() or im.tell() but neither of these methods return an Image object, and therefore I cannot call resize() on their output.
Would you know how I can use Pillow to decrease the size of my GIF while keeping all of its frames?
[edit] Partial solution:
Following Old Bear's response, I have done the following changes:
I am using BigglesZX's script to extract all frames. It is useful to note that this is a Python 2 script, and my project is written in Python 3 (I did mention that detail initially, but it was edited out by the Stack Overflow Community). Running 2to3 -w gifextract.py makes that script compatible with Python 3.
I have been resicing each frame individually: frame.resize((frame.size[0] // 2, frame.size[1] // 2), Image.ANTIALIAS)
I've been saving all the frames together: img.save("out.gif", save_all=True, optimize=True).
The new gif is now saved and works, but there is 2 main problems :
I am not sure that the resize method works, as out.gif is still 7.5MB. The initial gif was 2MB.
The gif speed is increased and the gif does not loop. It stops after its first run.
Example:
original gif my_gif.gif:
Gif after processing (out.gif) https://i.imgur.com/zDO4cE4.mp4 (I could not add it to Stack Overflow ). Imgur made it slower (and converted it to mp4). When I open the gif file from my computer, the entire gif lasts about 1.5 seconds.
Using BigglesZX's script, I have created a new script which resizes a GIF using Pillow.
Original GIF (2.1 MB):
Output GIF after resizing (1.7 MB):
I have saved the script here. It is using the thumbnail method of Pillow rather than the resize method as I found the resize method did not work.
The is not perfect so feel free to fork and improve it. Here are a few unresolved issues:
While the GIF displays just fine when hosted by imgur, there is a speed issue when I open it from my computer where the entire GIF only take 1.5 seconds.
Likewise, while imgur seems to make up for the speed problem, the GIF wouldn't display correctly when I tried to upload it to stack.imgur. Only the first frame was displayed (you can see it here).
Full code (should the above gist be deleted):
def resize_gif(path, save_as=None, resize_to=None):
"""
Resizes the GIF to a given length:
Args:
path: the path to the GIF file
save_as (optional): Path of the resized gif. If not set, the original gif will be overwritten.
resize_to (optional): new size of the gif. Format: (int, int). If not set, the original GIF will be resized to
half of its size.
"""
all_frames = extract_and_resize_frames(path, resize_to)
if not save_as:
save_as = path
if len(all_frames) == 1:
print("Warning: only 1 frame found")
all_frames[0].save(save_as, optimize=True)
else:
all_frames[0].save(save_as, optimize=True, save_all=True, append_images=all_frames[1:], loop=1000)
def analyseImage(path):
"""
Pre-process pass over the image to determine the mode (full or additive).
Necessary as assessing single frames isn't reliable. Need to know the mode
before processing all frames.
"""
im = Image.open(path)
results = {
'size': im.size,
'mode': 'full',
}
try:
while True:
if im.tile:
tile = im.tile[0]
update_region = tile[1]
update_region_dimensions = update_region[2:]
if update_region_dimensions != im.size:
results['mode'] = 'partial'
break
im.seek(im.tell() + 1)
except EOFError:
pass
return results
def extract_and_resize_frames(path, resize_to=None):
"""
Iterate the GIF, extracting each frame and resizing them
Returns:
An array of all frames
"""
mode = analyseImage(path)['mode']
im = Image.open(path)
if not resize_to:
resize_to = (im.size[0] // 2, im.size[1] // 2)
i = 0
p = im.getpalette()
last_frame = im.convert('RGBA')
all_frames = []
try:
while True:
# print("saving %s (%s) frame %d, %s %s" % (path, mode, i, im.size, im.tile))
'''
If the GIF uses local colour tables, each frame will have its own palette.
If not, we need to apply the global palette to the new frame.
'''
if not im.getpalette():
im.putpalette(p)
new_frame = Image.new('RGBA', im.size)
'''
Is this file a "partial"-mode GIF where frames update a region of a different size to the entire image?
If so, we need to construct the new frame by pasting it on top of the preceding frames.
'''
if mode == 'partial':
new_frame.paste(last_frame)
new_frame.paste(im, (0, 0), im.convert('RGBA'))
new_frame.thumbnail(resize_to, Image.ANTIALIAS)
all_frames.append(new_frame)
i += 1
last_frame = new_frame
im.seek(im.tell() + 1)
except EOFError:
pass
return all_frames
According to Pillow 4.0x, the Image.resize function only works on a single image/frame.
To achieve what you want, I believe you have to first extract every frame from the .gif file, resize each frame one at a time and then reassemble them up again.
To do the first step, there appears to be some detail that needs to be attended to. E.g. whether each gif frame uses a local palette or a global palette is applied over all frames, and whether gif replace each image using a full or partial frame. BigglesZX has developed a script to address these issues while extracting every frame from a gif file so leverage on that.
Next, you have to write the scripts to resize each of the extracted frame and assemble them all as a new .gif using the PIL.Image.resize() and PIL.Image.save().
I noticed you wrote "im.seek(im.tell() + 1) # load all frames". I think this is incorrect. Rather it is use to increment between frames of a .gif file. I noticed you used quality=10 in your save function for your .gif file. I did not find this as provided in the PIL documentation. You can learn more about the tile attribute mentioned in BiggleZX's script by reading this link
I am using the function below to resize and crop images including animated ones (GIF, WEBP) Simply, we need to iterate each frame in the gif or webp.
from math import floor, fabs
from PIL import Image, ImageSequence
def transform_image(original_img, crop_w, crop_h):
"""
Resizes and crops the image to the specified crop_w and crop_h if necessary.
Works with multi frame gif and webp images also.
args:
original_img is the image instance created by pillow ( Image.open(filepath) )
crop_w is the width in pixels for the image that will be resized and cropped
crop_h is the height in pixels for the image that will be resized and cropped
returns:
Instance of an Image or list of frames which they are instances of an Image individually
"""
img_w, img_h = (original_img.size[0], original_img.size[1])
n_frames = getattr(original_img, 'n_frames', 1)
def transform_frame(frame):
"""
Resizes and crops the individual frame in the image.
"""
# resize the image to the specified height if crop_w is null in the recipe
if crop_w is None:
if crop_h == img_h:
return frame
new_w = floor(img_w * crop_h / img_h)
new_h = crop_h
return frame.resize((new_w, new_h))
# return the original image if crop size is equal to img size
if crop_w == img_w and crop_h == img_h:
return frame
# first resize to get most visible area of the image and then crop
w_diff = fabs(crop_w - img_w)
h_diff = fabs(crop_h - img_h)
enlarge_image = True if crop_w > img_w or crop_h > img_h else False
shrink_image = True if crop_w < img_w or crop_h < img_h else False
if enlarge_image is True:
new_w = floor(crop_h * img_w / img_h) if h_diff > w_diff else crop_w
new_h = floor(crop_w * img_h / img_w) if h_diff < w_diff else crop_h
if shrink_image is True:
new_w = crop_w if h_diff > w_diff else floor(crop_h * img_w / img_h)
new_h = crop_h if h_diff < w_diff else floor(crop_w * img_h / img_w)
left = (new_w - crop_w) // 2
right = left + crop_w
top = (new_h - crop_h) // 2
bottom = top + crop_h
return frame.resize((new_w, new_h)).crop((left, top, right, bottom))
# single frame image
if n_frames == 1:
return transform_frame(original_img)
# in the case of a multiframe image
else:
frames = []
for frame in ImageSequence.Iterator(original_img):
frames.append( transform_frame(frame) )
return frames
I tried to use the script given in the chosen answer but as Pauline commented, it had some problems such as speed issue.
The problem was that the speed wasn't given when saving the new gif. To solve that you must take the speed from the original gif and pass it to the new one when saving it.
Here is my script:
from PIL import Image
def scale_gif(path, scale, new_path=None):
gif = Image.open(path)
if not new_path:
new_path = path
old_gif_information = {
'loop': bool(gif.info.get('loop', 1)),
'duration': gif.info.get('duration', 40),
'background': gif.info.get('background', 223),
'extension': gif.info.get('extension', (b'NETSCAPE2.0')),
'transparency': gif.info.get('transparency', 223)
}
new_frames = get_new_frames(gif, scale)
save_new_gif(new_frames, old_gif_information, new_path)
def get_new_frames(gif, scale):
new_frames = []
actual_frames = gif.n_frames
for frame in range(actual_frames):
gif.seek(frame)
new_frame = Image.new('RGBA', gif.size)
new_frame.paste(gif)
new_frame.thumbnail(scale, Image.ANTIALIAS)
new_frames.append(new_frame)
return new_frames
def save_new_gif(new_frames, old_gif_information, new_path):
new_frames[0].save(new_path,
save_all = True,
append_images = new_frames[1:],
duration = old_gif_information['duration'],
loop = old_gif_information['loop'],
background = old_gif_information['background'],
extension = old_gif_information['extension'] ,
transparency = old_gif_information['transparency'])
Also I noticed that you must save the new gif using new_frames[0] instead of creating a new Image Pillow's object to avoid adding a black frame to the gif.
If you want to see a test using pytest on this script you can check my GitHub's repo.
I wrote a simple code that resize Gif with the same speed and background transparency. I think it could be helpful.
"""
# Resize an animated GIF
Inspired from https://gist.github.com/skywodd/8b68bd9c7af048afcedcea3fb1807966
Useful links:
* https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#saving
* https://stackoverflow.com/a/69850807
Example:
```
python resize_gif.py input.gif output.gif 400,300
```
"""
import sys
from PIL import Image
from PIL import ImageSequence
def resize_gif(input_path, output_path, max_size):
input_image = Image.open(input_path)
frames = list(_thumbnail_frames(input_image))
output_image = frames[0]
output_image.save(
output_path,
save_all=True,
append_images=frames[1:],
disposal=input_image.disposal_method,
**input_image.info,
)
def _thumbnail_frames(image):
for frame in ImageSequence.Iterator(image):
new_frame = frame.copy()
new_frame.thumbnail(max_size, Image.Resampling.LANCZOS)
yield new_frame
if __name__ == "__main__":
max_size = [int(px) for px in sys.argv[3].split(",")] # "150,100" -> (150, 100)
resize_gif(sys.argv[1], sys.argv[2], max_size)
I am very new in Python and this is going to be a very basic question.I have a website which is image based and i am developing it using Django.Now i want to resize the image or you can say i want to minimize the size of the images.There are different size of images are avaible,some images are largest in width,some images are largest in height and i want to resize images without changing there shape.
Here are some example what dimensions images are using in my website.
Here the First image is largest in width and the second image is largest in height and they are really big in Dimension.so they need to be resized or rather these images are need to be minimized in size.So i have used the PIL as below.
from PIL import Image,ImageDraw, ImageFont, ImageEnhance
def image_resize(request,image_id):
photo = Photo.objects.get(pk=image_id)
img = Image.open(photo.photo.file)
image = img.resize((1000, 560), Image.ANTIALIAS)
image.save()
so this function returns all the images with width of 1000 and height of 560.But i don't want to resize all the images with same width and height,rather i want to resize each images maintaining there own shape. That is there shape will be same but the images will be resized.How can i do this? i am really new in python.
Do you want to have all images with same width 1000? Try this code. It will resize to at most 1000 as width (if the image's width is less than 1000, nothing changes)
def image_resize(request,image_id):
photo = Photo.objects.get(pk=image_id)
image = Image.open(photo.photo.file)
(w,h) = image.size
if (w > 1000):
h = int(h * 1000. / w)
w = 1000
image = image.resize((w, h), Image.ANTIALIAS)
image.save()
I recall doing this sometime back without any problem except that I used thumbnail method rather than resize. Try it. You need not assign img to image. You can process img and save the same.
# open img
img.thumbnail((1000,560), Image.ANTIALIAS)
# save img
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.