Different filesize when changing heic to jpeg in python - python

I could easily convert HEIC to JPEG in python with a pyheic library.
But the filesize gets larger when it's converted to JPEG. (About 4 times). Can I reduce the size of the file saving it?
How can I get base64-encoded string instead of saving JPEG image?
My code is as follows:
# -*- coding: utf-8 -*-
import sys
import os
from PIL import Image # pip3 install pillow
import pyheif # pip3 install pyheif
def call(oriPath, defPath):
try:
# if defPath is webp or heic
fileType = oriPath.split(".")[-1]
if fileType == "heic":
heif_file = pyheif.read(oriPath)
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
heif_file.mode,
heif_file.stride,
)
image.save(defPath, "JPEG")
except:
print(False)
return
print(True)

Since you are converting from one data type to the other the format is changing. I am not too familiar with HEIC but it could be the case that it is simply a smaller file size with the compression parameters that you have set for the JPEG.
wikipedia has the following to say about it
A HEIF image using HEVC requires less storage space than the equivalent quality JPEG.
So unless you want to drop the quality it might not even be possible to get a smaller image (note: I have no idea what type the HEIC/HEIF is).
As for the base64 encoded string, you could just read the binary data and convert it to base64 per this question: Python: How do I convert from binary to base 64 and back?

Related

Cannot convert base64 string into image

Can someone help me turn this base64 data into image? I don't know if it's because the data was not decoded properly or anything else. Here is how I decoded the data:
import base64
c_data = { the data in the link (string type) }
c_decoded = base64.b64decode(c_data)
But it gave the error Incorrect Padding so I followed some tutorials and tried different ways to decode the data.
c_decoded = base64.b64decode(c_data + '=' * (-len(c_data) % 4))
c_decoded = base64.b64decode(c_data + '=' * ((4 - len(c_data) % 4) % 4)
Both ways decoded the data without giving the error Incorrect Padding but now I can't turn the decoded data into image.
I have tried creating an empty png then write the decoded data into it:
from PIL import Image
with open('c.png', 'wb') as f:
f.write(c_decoded)
image = Image.open('c.png')
image.show()
It didn't work and gave the error: cannot identify image file 'c.png'
I have tried using BytesIO:
from PIL import Image
import io
from io import BytesIO
image = Image.open(io.BytesIO(c_decoded))
image.show()
Now it gave the error: cannot identify image file <_io.BytesIO object at 0x0000024082B20270>
Please help me.
Not sure if you definitely need a Python solution, or you just want help decoding your image like the first line of your question says, and you thought Python might be needed.
If the latter, you can just use ImageMagick in the Terminal:
cat YOURFILE.TXT | magick inline:- result.png
Or equivalently and avoiding "Useless Use of cat":
magick inline:- result.png < YOURFILE.TXT
If the former, you can use something like this (untested):
from urllib import request
with request.urlopen('data:image/png;base64,iVBORw0...') as response:
im = response.read()
Now im contains a PNG-encoded [^1] image, so you can either save to disk as such:
with open('result.png','wb') as f:
f.write(im)
Or, you can wrap it in a BytesIO and open into a PIL Image:
from io import BytesIO
from PIL import Image
pilImage = Image.open(BytesIO(im))
[^1]: Note that I have blindly assumed it is a PNG, it may be JPEG, so you should ideally look at the start of the DataURI to determine a suitable extension for saving your file.
Credit to #jps for explaining why my code didn't work. Check out #Mark Setchell solution for the reliable way of decoding base64 data (his code fixes my mistake that #jps pointed out)
So basically remove the [data:image/png;base64,] at the beginning of the base64 string because it is just the format of the data.
Change this:
c = "data:image/png;base64,iVBORw0KGgoAAAANSUh..."
to this:
c = "iVBORw0KGgoAAAANSUh..."
and now we can use
c_decoded = base64.b64decode(c)

How to decode this binary image file if none of image processing python libraries (Pillow and cv2) are unable to read?

I am trying to read this file using Pillow and cv2 in python. However, both libraries are failing to do so.
File is here on google drive.
Pillow raises an "OSError: cannot identify image file 0.jpg"
cv2 returns None
This binary file has the extension ".jpg" and can be easily viewed using Window`s Photos viewer.
However, I want to read and decode this binary file as an image in python.
Any idea?
Your file is actually a JXR file, you can see the first few bytes are:
49 49 BC
if you load it into a hex editor like http://hexed.it
The signature is IANA registered, see here.
You can read it with imagecodecs like this:
import imagecodecs
from pathlib import Path
# Slurp entire image data as binary
jxr = Path('0.jpg').read_bytes()
# Make into Numpy array
na = imagecodecs.jpegxr_decode(jxr)
print(na.shape) # prints (256, 512, 3)
Alternatively, you can convert JXR to something less Microsofty with ImageMagick in your Terminal with:
magick input.jxr output.jpg
Or, if you have old v6 ImageMagick, use:
convert input.jxr output.jpg

conveting bytes to image using tinytag and PIL

I am using tinytags module in python to get the cover art of a mp3 file and want to display or store it. The return type of the variable is showing to be bytes. I have tried fumbling around with PIL using frombytes but to no avail. Is there any method to convert the bytes to image?
from tinytag import TinyTag
tag = TinyTag.get("03. Me, Myself & I.mp3", image=True)
img = tag.get_image()
I actually got a PNG image when I called tag.get_image() but I guess you might get a JPEG. Either way, you can wrap it in a BytesIO and open it with PIL/Pillow or display it. Carrying on from your code:
from PIL import Image
import io
...
im = tag.get_image()
# Make a PIL Image
pi = Image.open(io.BytesIO(im))
# Save as PNG, or JPEG
pi.save('cover.png')
# Display
pi.show()
Note that you don't have to use PIL/Pillow. You could look at the first few bytes and if they are a PNG signature (\x89PNG) save data as binary with PNG extension. If the signature is JPEG (\xff \xd8) save data as binary with JPEG extension.

python set maximum file size when converting (pdf) to jpeg using e.g. Wand

For subsequent processing purposes, in python I am converting a multi-page PDF (f) into JPEGs (temp?.jpg):
import os
from wand.image import Image as wimage
with wimage(filename=f,resolution=300) as img:
for i in range(len(img.sequence)):
ftemp=os.path.abspath('temp%i.jpg'%i)
img_to_save=wimage(img.sequence[i])
img_to_save.compression_quality = 100
img_to_save.format='jpeg'
img_to_save.save(filename=ftemp)
I am using wand because of its ability to sequence the PDF pages, but am open to PIL etc.
I need the resolution and compression_quality to be as high as possible, but I want each JPEG to be no larger than (say) 300 kb in size.
How can I set a limit to the size of the JPEG file?
On the command line I would just do (see https://stackoverflow.com/a/11920384/1021819):
convert original.jpeg -define jpeg:extent=300kb -scale 50% output.jpg
Thanks!
The wand library has wand.image.OptionDict for managing -define attributes, but unfortunately all options are locked by wand.image.Option frozenset. IMHO, this renders the whole feature as unusable.
Luckily, you can create a quick sub-class to handle this via the wand.api.
import os
from wand.image import Image
from wand.api import library
from wand.compat import binary
class wimage(Image):
def myDefine(self, key, value):
""" Skip over wand.image.Image.option """
return library.MagickSetOption(self.wand, binary(key), binary(value))
with wimage(filename=f, resolution=300) as img:
for i in range(len(img.sequence)):
ftemp=os.path.abspath('temp%i.jpg'%i)
with wimage(img.sequence[i]) as img_to_save:
img_to_save.myDefine('jpeg:extent', '300kb')
img_to_save.compression_quality = 100
img_to_save.format='jpeg'
img_to_save.save(filename=ftemp)
In the near future. The wand.image.Option would be deprecated, and you could simply call img_to_save.options['jpeg:extent'] = '300kb'.

How to decode JPG/PNG in python?

This is a code of a JPG/PNG(I don't know exactly)
Here's on google docs
I need to decode it in Python to complete image and show it using Pillow or something like that. Do you know any libraries or ways how to decode it? Thanks!
(for Python 3)
If the image is stored as a binary file, open it directly:
import PIL
# Create Image object
picture = PIL.Image.open('picture_code.dat')
#display image
picture.show()
# print whether JPEG, PNG, etc.
print(picture.format)
If the image is stored as hex in a plaintext file picture_code.dat similar to your Google Docs link, it needs to first be converted to binary data:
import binascii
import PIL
import io
# Open plaintext file with hex
picture_hex = open('picture_code.dat').read()
# Convert hex to binary data
picture_bytes = binascii.unhexlify(picture_hex)
# Convert bytes to stream (file-like object in memory)
picture_stream = io.BytesIO(picture_bytes)
# Create Image object
picture = PIL.Image.open(picture_stream)
#display image
picture.show()
# print whether JPEG, PNG, etc.
print(picture.format)

Categories

Resources