Search code examples
pythonmatlabffmpegoctavescikit-image

Different filesizes for images generated using octave and python


I am using python (scikit-image) and octave to generate 200 images as follows

Python3

import numpy as np
from skimage.io import imsave

images = [255*np.ones((100,100), dtype=np.uint8),  # white
             np.zeros((100,100), dtype=np.uint8)]  # black

for i in range(200): # save alternating black and white images
    imsave('%04d.png'%(i+1), images[i%2])

Octave

pkg load image;

im1 = 255*ones(100,100); # white
im2 = zeros(100,100);    # black
for i=1:200
    name = sprintf('%04d.png', i);
    if mod(i,2) == 0
        imwrite(im1, name);
    else
        imwrite(im2, name);
    end
end

Next, I use ffmpeg to generate two videos (alternating white and black frames) from these two sets of images using the following command

ffmpeg -r 10 -loglevel quiet \
       -i ./%04d.png -c:v libx264 \
       -preset ultrafast -crf 0 ./out.mkv
  1. Sizes of image files generated by both these codes are different.
  • Octave {white: 192 bytes, black: 98 bytes}
  • Python {white: 120 bytes, black: 90 bytes}
  1. Sizes of video files generated from these octave and python images are significantly different from each other.
  • Octave {filesize: 60 kilobytes}
  • Python {filesize: 116 kilobytes}

Why do we have this apparently very strange behavior?

EDIT

Since it was suggested that the behavior might be due to octave and python using different bit-depths to store the images, I changed the octave code to use 8 bit numbers

im1 = uint8(255*ones(100,100)); # white
im2 = uint8(zeros(100,100));    # black

and now the image file sizes are nearly the same

  • Octave {white: 118 bytes, black: 90 bytes}
  • Python {white: 120 bytes, black: 90 bytes}

but the problem is still the same for video files, octave: 60K, python: 116K


Solution

  • scikit-image (using PIL underneath the hood) saves the PNGs in 8-bit format, while octave uses 16-bit (as mentioned by @carandraug below, this happens because floating point data is provided in Octave, which PNG does not support directly, so the data is converted to uint16 instead). This alone would explain the difference in sizes (although their may be other subtle differences in the ways their encoders work).

    scikit-image:

    $ identify 000*.png
    0001.png PNG 100x100 100x100+0+0 8-bit RGB 256c 120B 0.000u 0:00.000
    0002.png[1] PNG 100x100 100x100+0+0 8-bit RGB 256c 90B 0.000u 0:00.000
    0003.png[2] PNG 100x100 100x100+0+0 8-bit RGB 256c 120B 0.000u 0:00.000
    0004.png[3] PNG 100x100 100x100+0+0 8-bit RGB 256c 90B 0.000u 0:00.000
    0005.png[4] PNG 100x100 100x100+0+0 8-bit RGB 256c 120B 0.000u 0:00.000
    0006.png[5] PNG 100x100 100x100+0+0 8-bit RGB 256c 90B 0.000u 0:00.000
    0007.png[6] PNG 100x100 100x100+0+0 8-bit RGB 256c 120B 0.000u 0:00.000
    0008.png[7] PNG 100x100 100x100+0+0 8-bit RGB 256c 90B 0.000u 0:00.000
    0009.png[8] PNG 100x100 100x100+0+0 8-bit RGB 256c 120B 0.000u 0:00.000
    

    Octave:

    $ identify 000*.png
    0001.png PNG 100x100 100x100+0+0 16-bit RGB 98B 0.000u 0:00.000
    0002.png[1] PNG 100x100 100x100+0+0 16-bit RGB 192B 0.000u 0:00.000
    0003.png[2] PNG 100x100 100x100+0+0 16-bit RGB 98B 0.000u 0:00.000
    0004.png[3] PNG 100x100 100x100+0+0 16-bit RGB 192B 0.000u 0:00.000
    0005.png[4] PNG 100x100 100x100+0+0 16-bit RGB 98B 0.000u 0:00.000
    0006.png[5] PNG 100x100 100x100+0+0 16-bit RGB 192B 0.000u 0:00.000
    0007.png[6] PNG 100x100 100x100+0+0 16-bit RGB 98B 0.000u 0:00.000
    0008.png[7] PNG 100x100 100x100+0+0 16-bit RGB 192B 0.000u 0:00.000
    0009.png[8] PNG 100x100 100x100+0+0 16-bit RGB 98B 0.000u 0:00.000
    

    On my machine (ffmpeg 2.8.11-0ubuntu0.16.04.1), both videos end up being 116KB.