user650654
user650654

Reputation: 6128

Convert greyscale JPEG to rgb JPEG, retaining quality

I have a few JPEG files containing images in grayscale. I wish to convert these images to RGB (i.e. three channels) and save them as new JPEG files and with the same (or almost the same) quality as the original images. I wish to use the Python Pillow package.

I tried the following steps, but it does not retain the quality of the original image:

$ jhead gray.JPEG
File name    : gray.JPEG
File size    : 68741 bytes
File date    : 2021:10:25 04:54:30
Resolution   : 500 x 333
Color/bw     : Black and white
JPEG Quality : 96
gray = PIL.Image.open("gray.JPEG")
rgb = gray.convert(mode="RGB")
rgb.save("rgb.JPEG")
$ jhead rgb.JPEG
File name    : rgb.JPEG
File size    : 25622 bytes
File date    : 2021:10:25 05:04:20
Resolution   : 500 x 333
JPEG Quality : 75

How can I do the conversion using PIL.Image and retain the quality of the JPEG image?

Upvotes: 2

Views: 424

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207465

Updated Answer

Assuming you are focussed on getting the job done, rather than having a specifically PIL/Pillow-based answer like below, you can do what you want with ImageMagick without writing any code just in Terminal as follows. Make a copy of your files first and work in a separate directory as mogrify is a VERY powerful command:

magick mogrify -colorspace srgb -type truecolor *.jpg

If you want to avoid overwriting your original files, specify a path for output files:

mkdir OUTPUT
magick mogrify -path OUTPUT -colorspace srgb -type truecolor *.jpg

This should retain the dpi, quality and other metadata too. If you like these results but really insist on Python, I am pretty certain you can do the above in Python with wand which is derived from ImageMagick.

Original Answer

This seems to be the topic of some discussion and confusion. Basically, the quality is not actually directly embedded in a JPEG. I think the safest bet for the moment is to extract the quality of the original and write using the same value, something like this:

import subprocess
from PIL import Image

# Load input image and convert to RGB
filename = 'input.jpg'
im = Image.open(filename).convert('RGB')

# Get quality estimate of original using "exiftool"
proc = subprocess.run(["exiftool","-s3","-JPEGQualityEstimate",filename], capture_output=True)
q=int(proc.stdout)
print(f'File: {filename}, estimated quality: {q}')

# Save with that quality
im.save('result.jpg', quality=q)

For production quality, you should check result and handle errors/exceptions and so on.


If you don't have exiftool installed, you could use this command with ImageMagick to extract the quality:

magick input.jpg -format %Q info:

If you want to stick with jhead, you could use something like:

import re

# Guesstimate quality using "jhead"
proc = subprocess.run(["jhead",filename], capture_output=True)
it = re.search(r'JPEG Quality\s*:\s*(\d+)', str(proc.stdout))
q = int(it.group(1))

Upvotes: 3

Related Questions