0

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)
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/229419/discussion-on-question-by-ibrahim-ajarmeh-calculating-image-resolution-based-on). – Samuel Liew Mar 03 '21 at 00:53

1 Answers1

1

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

pippo1980
  • 2,181
  • 3
  • 14
  • 30
  • you can use a binary search to make the algorithm much faster... – thebjorn Feb 27 '21 at 18:24
  • you sure ? how ? (buffer of resize (0,0) + buffer of (original_x,original_y)/)2 and so on ?? write it down then I'll compare with process_time() – pippo1980 Feb 27 '21 at 18:27
  • ok I'll give it a try as soon as I got time !!! I checked your profile if you say it can be done I am sure it can be done. Cheers – pippo1980 Feb 27 '21 at 18:30
  • instead of the the `x -= 1` you'll do `x //= 2` and if it's too small then `x += (old_x - x) // ` etc., each time halving the interval. Instead of linear time you get O(log n). – thebjorn Feb 27 '21 at 23:38
  • @pippo1980 Thank your help, but I have recursive function which will do the work. my point is never to use the recursion. How to find desired from one formula – Ibrahim Ajarmeh Feb 28 '21 at 09:11
  • can you use the binary iterative instead ? – pippo1980 Feb 28 '21 at 11:56
  • dont know lot about images, but from here https://stackoverflow.com/questions/10423942/what-is-the-header-size-of-png-jpg-jpeg-bmp-gif-and-other-common-graphics-for/10424014 seems to me that jpeg headers are not fixed so you need to get them from originAL image, and I dont know if they change with the image resolution or not – pippo1980 Feb 28 '21 at 11:58
  • your formula seems fine accorfing to this https://4nsi.com/faq/how-do-i-calculate-the-file-size-for-a-digital-image – pippo1980 Feb 28 '21 at 12:00