So this GIF looks perfectly fine before opening:
But, when opened using Pillow using
imageObject = Image.open(path.join(petGifs, f"{pokemonName}.gif"))
it bugs out, adding various boxes that have colors similar to that of the source image. This is an example frame, but almost every frame is different, and it's in different spots depending on the GIF:
The only thing, that has worked to fix this, is ezgif's unoptimize option (found in their optimize page). But, I'd need to do that on each GIF, and there's a lot of them.
I need either a way to bulk unoptimize, or a new way to open the GIF in Python (currently using Pillow), that will handle this.
At least for extracting proper single frames there might be a solution.
The disposal method for all frames (except the first) is set to 2, which is "restore to background color".
Diving through Pillow's source code, you'll find the according line where the disposal method 2 is considered, and, in the following, you'll find:
# by convention, attempt to use transparency first
color = (
frame_transparency
if frame_transparency is not None
else self.info.get("background", 0)
)
self.dispose = Image.core.fill("P", dispose_size, color)
If you check the faulty frames, you'll notice that this dark green color of the unwanted boxes is located at position 0 of the palette. So, it seems, the wrong color is picked for the disposal, because – I don't know why, yet – the above else case is picked instead of using the transparency information – which would be there!
So, let's just override the possibly faulty stuff:
from PIL import Image, ImageSequence
# Open GIF
gif = Image.open('223vK.gif')
# Initialize list of extracted frames
frames = []
for frame in ImageSequence.Iterator(gif):
# If dispose is set, and color is set to 0, use transparency information
if frame.dispose is not None and frame.dispose[0] == 0:
frame.dispose = Image.core.fill('P', frame.dispose.size,
frame.info['transparency'])
# Convert frame to RGBA
frames.append(frame.convert('RGBA'))
# Visualization overhead
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 8))
for i, f in enumerate(frames, start=1):
plt.subplot(8, 8, i), plt.imshow(f), plt.axis('off')
plt.tight_layout(), plt.show()
The extracted frames look like this:
That seems fine to me.
If, by chance, the transparency information is actually set to 0, no harm should be done here, since we (re)set with the still correct transparency information.
I don't know, if (re)saving to GIF will work, since frames are now in RGBA mode, and saving to GIF from there is tricky as well.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.19041-SP0
Python: 3.9.1
PyCharm: 2021.1.3
Matplotlib: 3.4.2
Pillow: 8.3.1
----------------------------------------
You can try to use:
from PIL import Image, ImageSequence
im = Image.open(f"{pokemonName}.gif")
index = 1
for frame in ImageSequence.Iterator(im):
frame.save("frame%d.png" % index)
index += 1
I've found a solution that I like for unoptimizing gifs which might be of use to you.
It uses the gifsicle library, which is a command line tool for working with gifs. Crucially, gifsicle lets you unoptimize gifs like yours (I think the specific name of the optimization in your gif is "cumulative layers").
Once you install it with your package manager of choice, you can either call it within your code via Python's subprocess library, or use it yourself from the command line.
You specifically mentioned a way to bulk unoptimize, and you can do that very easily with gifsicle via something like:
gifsicle -U -b *.gif
This will overwrite every gif in the working directory with an unoptimized version simultaneously. If you want to keep optimized copies make backups. See the manual page for more info about how to use gifsicle.
Once the gif is unoptimized python should be able to open it normally.
Related
First I note that there are many related questions, but after a day of trying pyvip and cairo and the rest none of them work for me, even after installing other software that they seem to depend on. The exception is svglib with reportlab, it comes close but doesn't quite get there! This is the best post I found and may help some.
I have all my source images in SVG files. Most app stores require you to provide a set of PNGs with specific sizes and qualities. So I need to take an SVG and produce a PNG with width w and height h and specific dpi. I want to do this programmatically in python.
I have written a function that almost works, but scaling and dpi interact with each other in weird ways. I use svglib to convert the SVG to a ReportLab drawing then use reportlab to manipulate the drawing. The install went smoothly on Windows unlike some of the other options.
pip install svglib
pip install reportlab
The code is as follows. I inspected the above libraries to get the arguments, but added stuff to get specific size.
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
def svg_to_png(in_path,out_path,fmt="PNG",
scale=None,size=None,scale_x=1,size_x=None,scale_y=1,size_y=None,
dpi=72, bg=0xffffff):
# Convert SVG to ReportLab drawing.
drawing = svg2rlg(in_path)
# Work out scale factors
# Scale over-rides scale_x|y, ditto size
scale_x = scale if scale else scale_x
scale_y = scale if scale else scale_y
size_x = size if size else size_x
size_y = size if size else size_y
# Size over-rides scale
scaling_x = size_x/drawing.width if size_x else scale_x
scaling_y = size_y/drawing.height if size_y else scale_y
# Scale the drawing
drawing.width = drawing.minWidth() * scaling_x
drawing.height = drawing.height * scaling_y
drawing.scale(scaling_x, scaling_y)
# Render ReportLab drawing as a PNG
renderPM.drawToFile(drawing, out_path, fmt=fmt, dpi=dpi, bg=bg)
if __name__ == '__main__':
breakpoint()
in_path = 'C:\\Users\\...\\A.svg'
out_path = 'C:\\Users\\...\\A.png'
svg_to_png(in_path,out_path,scale=2,dpi=72)
The function works for resizing so long as I leave the dpi alone. The dpi=72 seems to be a universal default in reportlab and the source of the issues. I hoped that increasing the dpi would impove the quality of the PNG (reduce pixilation), but all it seems to do is expand the canvas.
The input SVG is of course pixel perfect. Here are four PNGs from the above function.
Scale 1 dpi 72 (dim 115x124 size 8.13kb), conversion with all defaults.
Scale 1 dpi 144 (dim 230x249 size 9.06kb), canvas doubled, pic in bottom-left quadrant, ignore black line bug.
Scale 2 dpi 72 (dim 230x249 size 17.5kb), scaled properly but pixelated (look at the eye)
Scale 2 dpi 144 (dim 461x497 size 19.8kb), canvas quadrupled? picture doubled.
Can someone please advise how to use reportlab properly to resize the picture to given scale or size and within that fixed size, increase the quality via the dpi.
Answering my own question, after lots of code inspections within svglib it appears it is impossible to do resize and increase dpi to predefined values. This is because svglib renders an SVG to a ReportLab drawing without any ability to tell it to render the SVG at the resolution required, which is a shame given the whole point of vector is arbitrary resolution. Once its in ReportLab you are stuck with the resolution of the drawing.
I switched to pyvips which makes use of libvips.
Its a bit fiddly to install, you can't just pip install pyvips. You have to download the libvips package and unzip it on disk somewhere you keep programs. You can then pip install pyvips in the usual way. In your python code you then add the libvips /bin to your path, assuming you don't want to do this permanently from the operating system.
The following function can then be used to convert an SVG to a PNG and set the resolution of the PNG and either scale it or set its horizontal width.
def svg_to_png(svg_path,png_path,dpi=72,scale=1,size=None):
# Documentation
# Ref: https://libvips.github.io/libvips/API/current/
# Ref: https://libvips.github.io/pyvips/
# Initialise
debug=False
import os
os.environ['path'] += r';C:\programs\vips\vips-dev-8.11\bin'
import pyvips
# Get the image
if size:
image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=1)
if debug: print({field:image.get(field) for field in image.get_fields()})
scale = size/image.get('width')
image = image.resize(scale)
else:
image = pyvips.Image.new_from_file(svg_path,dpi=dpi,scale=scale)
# Write the image
if debug: print({field:image.get(field) for field in image.get_fields()})
image.write_to_file(png_path)
The function works properly when scaling. When setting a fixed output size it works well but a little bit of fiddling with the input dpi may be required to get the exact right output dpi.
Here's my solution which I also posted to a relevant Github Issue. This uses pymupdf to convert the intermediary PDF generated with svglib and reportlab to an SVG.
The advantage of this solution is that it doesn't need any fiddling with external dependencies like poppler, cairo or libvips, as pymupdf has prebuild wheels for Linux, Windows and MacOS.
Another advantage is the support for a transparent background.
import fitz
from svglib import svglib
from reportlab.graphics import renderPDF
# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)
# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")
Cheers!
I'm using imageio in Python to read in jpg images and write them as a gif, using something resembling the code below.
import imageio
with imageio.get_writer('mygif.gif', mode='I') as writer:
for filename in framefiles: # iterate over names of jpg files I want to turn into gif frames
frame = imageio.imread(filename)
writer.append_data(frame)
I'm noticing that the image quality in the gifs I produce is quite poor; I suspect this is due to some form of compression. Is there a way to tell imageio not to use any compression? Or maybe a way to do this with opencv instead?
Real problem is that GIF can display only 256 colors (8-bits color) so it has to reduce 24-bits colors (RGB) to 256 colors or it has emulate more colors using dots with different colors - ditherring.
As for options:
Digging in source code I found that it can get two parameters quantizer, palettesize which can control image/animation quality. (There is also subrectangles to reduce file size)
But there are two plugins for GIF which use different modules Pillow or FreeImage and they need different value for quantizer
PIL needs integer 0, 1 or 2.
FI needs string 'wu' or 'nq' (but later it converts it to integer 0 or 1)
They also keep these values in different way so if you want get current value or change it after get_writer() then you also need different code.
You can select module with format='GIF-PIL' or format='GIF-FI'
with imageio.get_writer('mygif.gif', format='GIF-PIL', mode='I',
quantizer=2, palettesize=32) as writer:
print(writer)
#print(dir(writer))
#print(writer._writer)
#print(dir(writer._writer))
print('quantizer:', writer._writer.opt_quantizer)
print('palette_size:', writer._writer.opt_palette_size)
#writer._writer.opt_quantizer = 1
#writer._writer.opt_palette_size = 256
#print('quantizer:', writer._writer.opt_quantizer)
#print('palette_size:', writer._writer.opt_palette_size)
with imageio.get_writer('mygif.gif', format='GIF-FI', mode='I',
quantizer='nq', palettesize=32) as writer:
print(writer)
#print(dir(writer))
print('quantizer:', writer._quantizer)
print('palette_size:', writer._palettesize)
#writer._quantizer = 1
#writer._palettesize = 256
#print('quantizer:', writer._quantizer)
#print('palette_size:', writer._palettesize)
I tried to create animations with different settings but they don't look much better.
I get better result using external program ImageMagick in console/terminal
convert image*.jpg mygif.gif
but still it wasn't as good as video or static images.
You can run it in Python
os.system("convert image*.jpg mygif.gif")
subprocess.run("convert image*.jpg mygif.gif", shell=True)
Or you can try to do it with module Wand which is a wrapper on ImageMagick
Source code: GifWriter in pillowmulti.py and in freeimagemulti.py
* wu - Wu, Xiaolin, Efficient Statistical Computations for Optimal Color Quantization
* nq (neuqant) - Dekker A. H., Kohonen neural networks for optimal color quantization
Doc: GIF-PIL Static and animated gif (Pillow), GIF-FI Static and animated gif (FreeImage)
I'm trying to convert this list of images I have to text. The images are fairly small but VERY readable (15x160, with only grey text and a white background) I can't seem to get pytesseract to read the image properly. I tried to increase the size with .resize() but it didn't seem to do much at all. Here's some of my code. Anything new I can add to increase my chances? Like I said, I'm VERY surprised that pytesseract is failing me here, it's small but super readable compared to some of the things I've seem it catch.
for dImg in range(0, len(imgList)):
url = imgList[dImg]
local = "img" + str(dImg) + ".jpg"
urllib.request.urlretrieve(url, local)
imgOpen = Image.open(local)
imgOpen.resize((500,500))
imgToString = pytesseract.image_to_string(imgOpen)
newEmail.append(imgToString)
Setting the Page Segmentation Mode (psm) can probably help.
To get all the available psm enter tesseract --help-psm in your terminal.
Then identify the psm corresponding to your need. Lets say you want to treat the image as a single text line, in that case your ImgToString becomes:
imgToString = pytesseract.image_to_string(imgOpen, config = '--psm 7')
Hope this will help you.
You can perform several pre-processing steps in your code.
1) Use the from PIL import Image and use your_img.convert('L'). There are several other settings you can check.
2) A bit advanced method: Use a CNN. There are several pre-trained CNNs you can use. Here you can find a little bit more detailed information: https://www.cs.princeton.edu/courses/archive/fall00/cs426/lectures/sampling/sampling.pdf
tifi
I'm creating some SVGs in batches and need to convert those to a PDF document for printing. I've been trying to use svglib and its svg2rlg method but I've just discovered that it's absolutely appalling at preserving the vector graphics in my document. It can barely position text correctly.
My dynamically-generated SVG is well formed and I've tested svglib on the raw input to make sure it's not a problem I'm introducing.
So what are my options past svglib and ReportLab? It either has to be free or very cheap as we're already out of budget on the project this is part of. We can't afford the 1k/year fee for ReportLab Plus.
I'm using Python but at this stage, I'm happy as long as it runs on our Ubuntu server.
Edit: Tested Prince. Better but it's still ignoring half the document.
I use inkscape for this. In your django view do like:
from subprocess import Popen
x = Popen(['/usr/bin/inkscape', your_svg_input, \
'--export-pdf=%s' % your_pdf_output])
try:
waitForResponse(x)
except OSError, e:
return False
def waitForResponse(x):
out, err = x.communicate()
if x.returncode < 0:
r = "Popen returncode: " + str(x.returncode)
raise OSError(r)
You may need to pass as parameters to inkscape all the font files you refer to in your .svg, so keep that in mind if your text does not appear correctly on the .pdf output.
CairoSVG is the one I am using:
import cairosvg
cairosvg.svg2pdf(url='image.svg', write_to='image.pdf')
rst2pdf uses reportlab for generating PDFs. It can use inkscape and pdfrw for reading PDFs.
pdfrw itself has some examples that show reading PDFs and using reportlab to output.
Addressing the comment by Martin below (I can edit this answer, but do not have the reputation to comment on a comment on it...):
reportlab knows nothing about SVG files. Some tools, like svg2rlg, attempt to recreate an SVG image into a PDF by drawing them into the reportlab canvas. But you can do this a different way with pdfrw -- if you can use another tool to convert the SVG file into a PDF image, then pdfrw can take that converted PDF, and add it as a form XObject into the PDF that you are generating with reportlab. As far as reportlab is concerned, it is really no different than placing a JPEG image.
Some tools will do terrible things to your SVG files (rasterizing them, for example). In my experience, inkscape usually does a pretty good job, and leaves them in a vector format. You can even do this headless, e.g. "inkscape my.svg -A my.pdf".
The entire reason I wrote pdfrw in the first place was for this exact use-case -- being able to reuse vector images in new PDFs created by reportlab.
Just to let you know and for the future issue, I find a solution for this problem:
# I only install svg2rlg, not svglib (svg2rlg is inside svglib as well)
import svg2rlg
# Import of the canvas
from reportlab.pdfgen import canvas
# Import of the renderer (image part)
from reportlab.graphics import renderPDF
rlg = svg2rlg.svg2rlg("your_img.svg")
c = canvas.Canvas("example.pdf")
c.setTitle("my_title_we_dont_care")
# Generation of the first page
# You have a last option on this function,
# about the boundary but you can leave it as default.
renderPDF.draw(rlg, c, 80, 740 - rlg.height)
renderPDF.draw(rlg, c, 60, 540 - rlg.height)
c.showPage()
# Generation of the second page
renderPDF.draw(rlg, c, 50, 740 - rlg.height)
c.showPage()
# Save
c.save()
Enjoy a bit with the position (80, 740 - h), it is only the position.
If the code doesn't work, you can look at in the render's reportlab library.
You have a function in reportlab to create directly a pdf from your image:
renderPDF.drawToFile(rlg, "example.pdf", "title")
You can open it and read it. It is not very complicated. This code come from this function.
I am wondering if there are any python packages out there for taking multiple graphs, saved in a png format, and editing their dimensions, and saving them in a new image? I want to have multiple graphs in a constant form, for when I have to manually add them to slides.
The standard Python imaging library for edits like these is...
well, the Python Imaging Library!
As another poster said, I'd recommend using the PIL. You could do something like this:
from PIL import Image
in_filename = 'sample.png'
out_filename = 'sample_small.png'
output_res = (320, 240)
im = Image.open(in_filename)
new_im = im.resize(output_res)
new_im.save(out_filename)
If you want to preserve the aspect ratio, you can use Image.thumbnail() instead of Image.resize(). (Note that Image.thumbnail() directly modifies the image instead of making a copy.)