2

Following the link advised by @Chris, I am able to achieve the results but the time taken is too high. If an Image is in MBs and I'm trying to get it under 100KBs or so, with a fixed step, it'll take too much time. Is there any efficient method or algorithm that exists? For example a logarithmic scheme where you reduce the next size based on difference between the current size and desired?

Below is the current code:

def resize(image, new_width_height):
    '''
    Resize and return Given Image
    args:
    path: Image Path: Path of Imaghe relative to the code where it is being deployed
    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 
    save_image = Whether to save the image or not
    convert_RGB: Whether to Convert the RGBA image to RGB (by default backgroud is white)
    '''
    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.ANTIALIAS)

    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.ANTIALIAS) # Try Image.ANTIALIAS inplace of Image.NEAREST
    
    return image


buffer = BytesIO(requests.get("https://img.wallpapersafari.com/tablet/2048/2732/55/48/dbklzj.jpg").content)
image = Image.open(buffer)
mode = image.mode # RGB, RGBA, L
format_ = image.format # JPEG, PNG, JPG etc
w, h = image.size
size_kb = buffer.getbuffer().nbytes / 1024 # Original Image size in kb


MIN_SIZE = 32 # Smallest dimension must not be less than 32
DESIRED_SIZE_KB = 102 # Could be any number
STEP = 32 # Decrease the pixels by 32 

new_width_height = max(w,h) - STEP # Decrease the pixels for first pass

while MIN_SIZE < new_width_height and size_kb > DESIRED_SIZE_KB: # either the image reaches minimun dimension possible or the desired possible size
    image = resize(image, new_width_height)
    
    buffer = BytesIO()
    image.save(buffer, format="JPEG", quality=100, optimize = True) # Doesnot save but acts like an image saved to disc
    size_kb = (buffer.getbuffer().nbytes) / 1024
    
    w, h = image.size # Current resized pixels
    new_width_height = max(w,h) - STEP # Dimensions for next iteration
Deshwal
  • 3,436
  • 4
  • 35
  • 94
  • Does this answer your question? [How to reduce a jpeg size to a 'desired size'?](https://stackoverflow.com/questions/52259476/how-to-reduce-a-jpeg-size-to-a-desired-size) – Chris Apr 20 '22 at 14:53
  • For non-jpeg images, you will have to reduce the dimensions iteratively until you get your image file size down to where you want. For jpeg, you can reduce the quality (increase the compression) until you get to the file size you want. – fmw42 Apr 20 '22 at 16:19
  • See also... https://stackoverflow.com/a/71205362/2836621 – Mark Setchell Apr 20 '22 at 16:57
  • 3
    Note that the `quality` parameter has a **totally different** meaning when saving **PNG** images as opposed to **JPEG** images. **PNG** images are **always** lossless so reducing the quality parameter will not reduce the quality or the file size necessarily. Reducing the quality of a **JPEG** file, on the other hand, will pretty much always decrease the file size. – Mark Setchell Apr 20 '22 at 17:04
  • @MarkSetchell oh! That's some point I had no idea about. Thanks a lot. – Deshwal Apr 20 '22 at 17:15
  • 1
    Have you looked at [`imutils.resize`](https://github.com/PyImageSearch/imutils/blob/master/imutils/convenience.py#L65)? It resizes and keeps aspect ratio based on a given width or height, I've gotten MB sized files down to 100s of KB. – nathancy Apr 21 '22 at 03:23
  • @nathancy Yes, I'm familiar with the code. Resizing code here is also working fine without any issues but the problem is getting the image under a specific size within least amount of time possible. when you have desired height or width, then there's no issue at all. – Deshwal Apr 21 '22 at 03:52

1 Answers1

0

You can compress the image using fft. Try this code

from skimage.data import camera
import numpy as np
import matplotlib.pyplot as plt

img = camera()

Bt = np.fft.fft2(img)
Btsort = np.sort(np.abs(Bt.reshape(-1)))

for keep in (0.1, 0.05, 0.01, 0.002):
    thresh = Btsort[int(np.floor((1-keep)*len(Btsort)))]
    ind = np.abs(Bt)>thresh
    Atlow = Bt * ind
    Alow = np.fft.ifft2(Atlow).real
    plt.figure()
    plt.imsave(f'img_{keep}.png',Alow,cmap='gray')
    plt.imshow(Alow,cmap='gray')
    plt.axis('off')
    plt.title('Compressed image: keep = ' + str(keep*100)+'%')

Check this video: https://www.youtube.com/watch?v=uB3v6n8t2dQ

And this notebook: https://github.com/DavidClawson/Brunton/blob/master/Brunton_Image_Compression_FFT.ipynb