4

I use (under Windows) the following command

magick convert -units pixelsperinch file_in -density 600 file_out

to set the dpi (no resampling, as dpi basically is, as far as I understand, just a tag that specifies pixel size) of a JPG image. It works, but I don't understand why it increases file size by several kiloBytes (an image of mine originally of 1658 kB got to 1717 kB, which is a 59 kB increase), whereas I would expect an increase, if any, of just a few bytes.

Did I get something wrong? Is it possible to change by command line (tools other than ImageMagick are welcomed too) density/dpi of a JPG without increase in file size?

Thanks in advance for any clue.

mmj
  • 5,514
  • 2
  • 44
  • 51
  • 2
    See Mark Setchell's answer below. The reason the file size happens is that Imagemagick will decompress and recompress your JPG. That alone may change the file size, but especially if your input JPG does not specify its quality, so Imagemagick assigns and recompresses at a high quality level of 92. Different JPG compressions tables between he input and output may also cause differences as well as different sampling-factors. So you should check the input and output with `magick identify -verbose image.jpg`. Also note that in Imagemagick 7, use magick only, not magick convert nor convert. – fmw42 Aug 05 '18 at 17:54
  • If you zip your input and output images and host them elsewhere and then post the link so that this forum does not re-encode them and potentially change the images, then we could look at your image meta data using `magick identify -verbose image.jpg` – fmw42 Aug 05 '18 at 17:58
  • @fmw42 Here are the links to the image before and after dpi change: https://drive.google.com/open?id=1vK9JSH3CaCeufrmozJE_KWGAB3MNnX3Q https://drive.google.com/open?id=1DW7ATCgWczVCHzIKjRcU9XctEKtoamut ~ Also note that if I optimize the image with **jpegran**, the original dpi gets lost. – mmj Aug 06 '18 at 09:13
  • @fmw42 More tests show that if I change dpi with **Image Magick**, file increases in size and dpi is lost after a **jpegtran** lossless optimization, whereas if I change dpi with **Irfanview** (by command line), file decreases in size (probably because **Irfanview** converts colorspace from CMYK to sRGB) and dpi is NOT lost after a **jpegtran** lossless optimization. – mmj Aug 06 '18 at 09:33
  • `@mmj` As I said above, Imagemagick will decompress and recompress even if you just change the dpi. So you want to use some other tool that does not do that. There are many reasons that the file size will change when the JPG is decompressed and recompressed. – fmw42 Aug 06 '18 at 17:27

4 Answers4

5

You can change/set the density without re-encoding the file (and thereby possibly changing its size or quality) with the much smaller, lighter weight and easier-to-install exiftool which "just" a Perl script:

exiftool -jfif:Xresolution=300 -jfif:Yresolution=300 YourImage.jpg

Different people call different things the density/resolution, so if the above command doesn't do what you want/need/hope/expect, maybe try:

exiftool -XResolution=300 -YResolution=300 YourImage.jpg
Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • **exiftool** looks promising, but unfortunately with my image I got `Warning: Not creating JFIF in JPEG with Adobe APP14`. Surely it is my image fault (it seems to be an Adobe JPG instead of a JFIF JPG), nonetheless I was able to change the dpi of such image without increase in size using **Irfanview** by command line: `i_view32.exe file_in.jpg /dpi=(600,600) /convert=file_out.jpg`. Irfanview does some encoding/decoding, but is able to handle AdobeJPG and the resulting file is even smaller. – mmj Aug 06 '18 at 08:51
  • More tests show that **Irfanview** converts colorspace from CMYK to sRGB, and that seems to reduce file size and allows to optimize losslessly with **jpegtran** *keeping* dpi information (see also my comments to question). – mmj Aug 06 '18 at 09:37
3

Trying to reproduce your problem:

  • Gimp's interlaced/progressive JPEG is smaller than the non-interlaced version (also produced by Gimp)
  • The magick convert output JPEG is not interlaced, and is in fact a bit smaller than Gimp's own non-interlaced. That file is the same size whether it is produced from an interlaced or non-interlaced version.

So, I would think that you are converting an interlaced/progressive JPEG. Note that this also demonstrates that IM is re-encoding the file, and comparing the original and the re-encoded in Gimp shows significant differences.

In the JPEG format, the H/V definitions are encoded in 4 bytes in the header, patching that should be a SMOP in about any programming language.

xenoid
  • 8,396
  • 3
  • 23
  • 49
  • Can you give some (simple) reference on those bytes in the header, so I can try to write a snippet? – mmj Aug 05 '18 at 16:41
  • 1
    See [Wikipedia](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format) (APP0 XDensity/YDensity). – xenoid Aug 05 '18 at 16:48
0

Try using the online tool https://convert.town/image-dpi. On my JFIF jpg this only changed the DPI and did not resample the image. Other software suggestions can be found on this Super User question, but this was the only one that worked for me without changing anything else in the image.

However I only have a JFIF jpg and you appear to have an Adobe jpg from your other comments, so your mileage may vary.

I verified the DPI change and no resampling using the Windows hex editor HxD, just looking at the 4 bytes detailed in the Wikipedia article linked by xenoid. And once you know what bytes are changing, you can skip the website and use a hex editor directly to set the X and Y DPIs.

Micah Lindstrom
  • 325
  • 5
  • 14
  • Nice tool, thanks for the advice, but I need a Windows command line tool, not an online tool. – mmj Dec 12 '19 at 08:33
  • They offer a for-pay Windows version at https://convert.town/downloaddpi. It's not command-line, but considering they want you to ask them for Mac/Linux versions I'm sure they'd be happy to make a Windows command-line version as well. If you don't want to pay for it, the only remaining option might be to make your own script. My guess is you could write a pretty simple Python script for your case assuming your JPGs all come from the same camera/software and thus can all get their hex code edited in the same way. – Micah Lindstrom Mar 17 '20 at 07:20
0

As far as If I understand, there are several flavors of JPEG files. The details needed to change DPI metadata of JFIF flavor are explained here. Based on that I wrote my own Python script which allows to change DPI setting of a JFIF flavor JPEG without reencoding:

import sys,os

filename = sys.argv[1]
x_density = int(sys.argv[2])
y_density = int(sys.argv[3])

echo = True
if len(sys.argv) > 4:
    if sys.argv[4] == 'quiet':
        echo = False

assert x_density > 0 and x_density < 65536
assert y_density > 0 and y_density < 65536

# JPEG markers
APP0 = bytes.fromhex('FFD8FFE0') # JFIF
APP1 = bytes.fromhex('FFD8FFE1') # EXIF

with open(filename, 'rb+') as f: # 'w+b'
    chunk = f.read(4)
    if chunk == APP0:
        f.seek(2,1) # relative seek
        chunk = f.read(5)
        if chunk == bytes.fromhex('4A46494600'): # JFIF indentfier
            f.seek(2,1) # relative seek
            print('Setting density of ' + os.path.split(filename)[1] + ' to ' + str(x_density) + 'x' + str(y_density) + ' dpi.') if echo else None
            f.write(bytes.fromhex('01'))
            f.write(x_density.to_bytes(2,'big'))
            f.write(y_density.to_bytes(2,'big'))
        else:
            print('File hasn''t got the JFIF indentifier, nothing was done.')
    elif chunk == APP1:
        f.close() # needed otherwise exiftool cannot operate on file
        print('This is an EXIF-JPEG, using exiftool to set DPI...')
        os.system('exiftool -P -overwrite_original -XResolution={} -YResolution={} "{}"'.format(x_density,y_density,filename))
    else:
        print('File is not JFIF nor EXIF, cannot set DPI, nothing was done.')

print('Done.') if echo else None

Usage:

python this_script.py some-image.jpg Xdpi Ydpi [quiet]

The script does not read the full image nor change the file length, it just modifies a few bytes directly on the JPEG file. Moreover, no temporary/backup copy is made, because I wanted the script to work on hard-linked files, so the overall process is quite fast for JFIF JPEGs.

The script is able to identify EXIF JPEGs and use exiftool to change DPI. If you don't have exiftool installed in your computer remember to adjust the script accordingly. A reason to use this script even if you have exiftool installed is speed; in my test this script is much faster than exiftool.

JFIF and EXIF are the most common flavors of JPEG files, but I hope someone is able to improve this script or report a way to set DPI (without reencoding) also for Adobe JPEGs with APP14 marker, which are not so rare.

mmj
  • 5,514
  • 2
  • 44
  • 51