Ibrahim Ajarmeh
Ibrahim Ajarmeh

Reputation: 11

Calculating image resolution based on file size using PIL python

I want to resize an image using resize() method from PIL based on a desired file size. For example I have an image with file size of 14.1 MB, width x height of 3456 x 5184. What will be its resolution if the desired file size is 2 MB ? Tried some calculation my self like using this formula image_size = (width * height * bit_depth + headers) / 8 plus the aspect ration from the current status of the image, but It does not work as I expected.

Goal:

Finding desired height or width

known:

  1. Desired file size
  2. current file size
  3. current resolution (width x height)

Upvotes: 0

Views: 665

Answers (1)

pippo1980
pippo1980

Reputation: 3023

copying from here How to reduce a jpeg size to a 'desired size'? and here Python PIL: Find the size of image without writing it as a file

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Feb 27 18:26:54 2021

@author: Pietro

"""

import io
from PIL import Image

def SaveWithTargetFileSize(im, filename, target):
   
   x, y =im.size
   buffer = io.BytesIO()
   im.save(buffer, format=mod_img, quality=100)
   im_size = buffer.getbuffer().nbytes
  
   if im_size <= target:
       print('invalid target value ')
       return

   while im_size > target :
      
       
      buffer = io.BytesIO()
      im.save(buffer, format=mod_img, quality=100)
      im_size = buffer.getbuffer().nbytes
      
      im = im.resize((x,y))
      # print(x,y  ,'      ', im_size)
      x -= 1
      y -= 1
      

    
   print('image_size for target file_size :', im.size[0],' X ', im.size[1] ,'  image_file_size  :', im_size)
   im.save(filename+'.'+mod_img.lower(), format=mod_img, quality=100)
   

im = Image.open('Immagine_6.tif')
x, y =im.size
typ_img = im.mode
mod_img = im.format
print('image size : ',x,' X ',y,'   ',' image_type :', typ_img,'     image_format :', mod_img)


SaveWithTargetFileSize(im, "result_2", 400000) # here the target values in bytes

you need to figure out file size target in bytes (or add a converter inside the code)

it is slow because resize each time by just 1 pixel from initial to target values

maybe somebody else will use a fastest approach.

as suggested by @thebjorn my attempt to bisection search

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Feb 27 18:26:54 2021

@author: Pietro

"""

import io
from PIL import Image

from datetime import datetime

begin = datetime.now()


def SaveWithTargetFileSize(im, filename, target):

    xmax, ymax = im.size

    xmin = 0

    ymin = 0

    buffer = io.BytesIO()
    im.save(buffer, format=mod_img, quality=100)
    im_size = buffer.getbuffer().nbytes
    
    im_size_original = im_size

    if im_size <= target:
        print('invalid target value ')
        return

    numGuesses = 0

    while xmin < xmax and ymin < ymax:

        x_x, y_y = (xmax - xmin)//2, (ymax - ymin)//2

        im = im.resize((x_x, y_y))

        buffer = io.BytesIO()
        im.save(buffer, format=mod_img, quality=100)
        im_size = buffer.getbuffer().nbytes


        #print(im.size[0], im.size[1], '      ', im_size, '    guesses :',
        #      numGuesses, '   ', im_size,  target, x_x, y_y, xmax, ymax, xmin, #ymin)

        if im_size < target:
            xmax += x_x
            ymax += y_y
        elif im_size > target:
            xmax -= x_x
            ymax -= y_y
            
        if abs(im_size - target) < (target*1)//100:
            if im_size > target:
                approx = str(200-(im_size*100/target))
            if im_size < target:
                approx = str(im_size*100/target)
                
            return print('\nbest approximation : ',approx+'%\n\n',im.size[0],' ', im.size[1], '        ', im_size,
                         '     guesses : ', numGuesses,
              '     ', im_size,' ',  target,' ', x_x,' ', y_y,' ', xmax,' ', ymax,' ', xmin,' ', ymin,' ', im_size_original,'\n\n',sep='')


        print(im.size[0], im.size[1], '      ', im_size, '    guesses :', numGuesses,
              '   ', im_size,  target, x_x, y_y, xmax, ymax, xmin, ymin, im_size_original,'\n\n')

        numGuesses += 1

    print('image_size for target file_size :',
          im.size[0], ' X ', im.size[1], '  image_file_size  :', im_size)
    # im.save(filename+'.'+mod_img.lower(), format=mod_img, quality=100)


im = Image.open('Immagine_6.tif')
x, y = im.size
typ_img = im.mode
mod_img = im.format
print('image size : ', x, ' X ', y, '   ', ' image_type :',
      typ_img, '     image_format :', mod_img)


SaveWithTargetFileSize(im, "result_5", 558999)

# SaveWithTargetFileSize(im, "result_5", 568000)

# SaveWithTargetFileSize(im, "result_4", 558999000)

print('\nTIME : ', datetime.now() - begin)

with this image setting image: image size : 520 X 409 image_type : RGBA image_format : TIFF it takes less than half the time of first keeping the print lines

Upvotes: 1

Related Questions