0

Tl;DR: Is it possible to manipulate size of image in KB to the desired size?

There are 4 ways to get an image size as answered by Mark in my another question and I'm wondering if there's any relation of ANY of the size to the number of pixels?

(7460,24300,1),(2549,6075,2),(1303,2700,3),(808,1518,4), (552,972,5)

These are the (os_stat_size, numpy_array_size, image_factor) image_factor == factor by which the image has been resized As you can see the resizing factor is not proportional to the size in KB.

Code given below is working perfectly for resizing. I am experimenting on manipulating image size in KBs instead of pixels. I did an experiment to reduce the overall image size by 50%. The idea is that given any depth, it should be constant and the pixels should correspond to the image size in linear fashion (thinking metadata remains constant)

I did the experiment and did not get results with jpeg. So I thought there might be some problem with the extension as I found this second answer saying :

Jpeg files don't have bit depth in the same manner as GIF or PNG files. The transform used to create the Jpeg data renders a continuous colour spectrum on decompression.

So I tried the same experiment with the PNG and it was same. Below are my experiments with both the versions with commented out exchange between jpeg and png

Helper code and imports: Working fine

from PIL import Image
import numpy as np
import requests
from io import BytesIO
from os import stat
import os
import sys

def resize(image:Image, current_size:tuple, new_width_height:float)->Image:
    '''
    Resize and return Given Image
    args:
        image: Image object
        current_size: Image size as (width, height)
        new_width_height = Reshaped image's width and height. If integer is given, it'll keep the aspect ratio as it is by shrinking the Bigger dimension (width or height) to the max of new_width_height  and then shring the smaller dimension accordingly 
    '''
    fixed_size = True if isinstance(new_width_height, int) else False
    w, h = current_size

    if fixed_size:
        if h > w:
            fixed_height = new_width_height
            height_percent = (fixed_height / float(h))
            width_size = int((float(w) * float(height_percent)))
            image = image.resize((width_size, fixed_height), Image.Resampling.LANCZOS) # # Try Image.NEAREST

        else:
            fixed_width = new_width_height
            width_percent = (fixed_width / float(w))
            height_size = int((float(h) * float(width_percent)))
            image = image.resize((fixed_width, height_size), Image.Resampling.LANCZOS) 

    else:
        w, h = int(new_width_height[0]), int(new_width_height[1])
        image = image.resize((w,h), Image.Resampling.LANCZOS)


    return image

Experiment:

  1. Actual image stats:
url = "https://wallpaperaccess.com/full/508751.jpg"
buffer = BytesIO(requests.get(url).content)
size_buffer = buffer.getbuffer().nbytes//1024
image = Image.open(buffer)

size_uncompressed = len(image.tobytes()) // 1024

# image.save('./wallpaper.jpeg', format = 'jpeg', quality = 'keep', subsampling = False)
# size_os = os.stat('./wallpaper.jpeg').st_size // 1024
image.save('./wallpaper.PNG', format = 'PNG', quality = 'keep', subsampling = False)
size_os = os.stat('./wallpaper.PNG').st_size // 1024
numpy_size = sys.getsizeof(np.array(image)) // 1024 # same as len(image.tobytes())

print(image.size, image.format, image.mode, str(size_buffer)+' KB (buffer.getbuffer().nbytes//1024)', str(size_os)+' KB (os.stat().st_size // 1024)', str(size_uncompressed)+' KB (len(image.tobytes()) // 1024)', str(numpy_size)+' KB (sys.getsizeof(np.array(image)) // 1024)')
  1. Resized image stats:
image = resize(image, image.size, max(image.size)//2)
# image.format = "JPEG" # as it'll throw error when using quality='keep' with jpeg
image.format = "PNG" 

size_uncompressed = len(image.tobytes()) // 1024


buffer = BytesIO()
# image.save(buffer, format="JPEG", format = 'jpeg', quality = 'keep', subsampling = False)
image.save(buffer, format="PNG")
size_buffer = buffer.getbuffer().nbytes//1024

# image.save('./resized_wallpaper.jpeg', format = 'jpeg', quality = 'keep', subsampling = False)
# size_os = os.stat('./resized_wallpaper.jpeg').st_size // 1024

image.save('./resized_wallpaper.PNG', format = 'PNG')
size_os = os.stat('./resized_wallpaper.PNG').st_size // 1024

numpy_size = sys.getsizeof(np.array(image)) // 1024

print(image.size, image.format, image.mode, str(size_buffer)+' KB (buffer.getbuffer().nbytes//1024)', str(size_os)+' KB (os.stat().st_size // 1024)', str(size_uncompressed)+' KB (len(image.tobytes()) // 1024)',str(numpy_size)+' KB (sys.getsizeof(np.array(image)) // 1024)')

None of them are actually linear. Is it normal or I'm doing something wrong?

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Deshwal
  • 3,436
  • 4
  • 35
  • 94
  • There's already a way of reducing an image to a desired size in kB here... https://stackoverflow.com/a/52281257/2836621 and here... https://stackoverflow.com/a/54713865/2836621 – Mark Setchell Jul 03 '22 at 09:33
  • If you have a question about something specific, please show the results your code produces and explain what you don't understand, e.g. *"I reduced the image height and width by a factor of 2, and expected..."*. Thank you. – Mark Setchell Jul 03 '22 at 09:39
  • @MarkSetchell Hey Mark. thanks again. I actually created my own version of Binary Search. Exact same thing but since I was regressing the test boundaries, a 7MB image takes too much time if I want to keep it under say, 300 KB.. – Deshwal Jul 03 '22 at 09:41
  • @MarkSetchell, Thanks, I have updated the findings too. – Deshwal Jul 03 '22 at 09:44
  • If your factor is 2 for height and width, you'll have 1/4 of the number of pixels and 24300/4=6075, so that's correct. If your factor is 3, you'll have 1/9 of the number of pixels and 24300/9=2700, so that's correct. So, I presume your question is about the size in kB of the JPEG, so how about plotting a graph of that to show what's upsetting you? – Mark Setchell Jul 03 '22 at 09:56
  • Oh! Now that you have mentioned it, just realised it. Also, the examples you have mentioned, they compress the quality with a factor. However I on the other hand, want to keep the quality same (as you might have notices `quality='keep`) by reducing the width and height to a particular size. – Deshwal Jul 03 '22 at 10:03

0 Answers0