2

I am testing to see how I can save the data table in 8bits (0-255) into an image format to store raw data files and load the image to get retrieve the same data.
Below code shows what I’m currently trying. With a 32x32 data table (random_data).
I want to plot this data in grayscale as a 32x32 pixel image, then open/retrieve it to see which image format best retains that data. If this can be done, I would like to scale it to a larger size of data/image.

Here are a couple of my challenges:

  • Question 1:
    Plt.figure and plt.savefig does not have direct options to plot and save 32x32 data table into 32x32 pixel images, I’ve added and manually adjusted dpi settings and figsize options and parameters to get to 32x32 pixel. However, when I open the saved image file and then convert it to an array, it does not give the same values as the input. (See below)

  • Question 2:
    Is it possible to save figures to some other newer image formats than PNG and JPEG? (e.g AVIF, WebP, HEIF, JPEG XL, JPEG XS, etc)?

  • Question 3:
    If Question 2 is not achievable, what would be the best image format to save and retain the same values of data?

import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
from numpy import asarray
import sys
import numpy

numpy.set_printoptions(threshold=sys.maxsize)

# Randomly generated data table in 0-255
random_data=([179,22,29,72,118,117,88,182,155,114,95,62,75,67,30,252], 
[161,88,76,74,70,99,136,246,178,113,233,125,177,135,94,72], 
[46,123,106,28,192,240,85,164,75,183,160,126,157,140,182,98], 
[6,147,229,193,224,103,31,133,19,176,194,171,223,123,173,204], 
[228,25,207,39,93,119,34,157,150,186,161,242,2,89,187,226], 
[109,198,151,25,122,136,48,250,245,102,205,180,74,229,243,27], 
[135,116,223,40,233,243,95,126,54,153,246,57,120,162,8,143], 
[78,249,58,237,80,237,99,92,67,157,64,200,0,249,31,33], 
[154,111,170,120,143,81,97,237,249,85,154,135,41,163,147,8], 
[137,195,189,167,196,240,185,117,199,179,57,170,87,253,89,152], 
[183,250,88,189,143,232,4,213,110,254,246,240,100,103,99,229], 
[155,205,45,236,60,87,121,216,99,3,243,61,107,104,180,58], 
[240,52,238,143,99,51,230,139,49,3,175,70,160,226,85,108], 
[98,131,157,38,120,4,9,87,122,179,118,41,79,120,119,246], 
[120,131,21,48,225,191,149,144,133,46,56,170,225,207,45,139], 
[118,59,128,238,228,110,70,247,132,225,223,77,53,161,115,197]) 

# Trying to plot the data in 32x32 pixel image then saving it
plt.figure(figsize=(.32, .32), dpi=100, frameon=False)
plt.imshow(random_data, interpolation='none', vmin=0, vmax=255, cmap='gray') 
plt.axis('off')
plt.savefig('test.png',  dpi=133, bbox_inches='tight', pad_inches=0)

# Calling saved image then converting it to array to compare it against the original input data
test_output=Image.open('test.png') 
test_output_gray=test_output.convert('L') 
test_output_gray=asarray(test_output_gray) 
print(test_output_gray.shape) 

print(test_output_gray)

Output below:

[[179 179  22  22  29  29  72  72 118 118 117 117  88  88 182 182 155 155 
  155 113 113  95  95  62  62  75  75  67  67  30  30 252] 
 [161 161  88  88  76  76  73  73  70  70  99  99 136 136 246 246 178 178 
  178 113 113 233 233 125 125 177 177 135 135  94  94  72] 
 [161 161  88  88  76  76  73  73  70  70  99  99 136 136 246 246 178 178 
  178 113 113 233 233 125 125 177 177 135 135  94  94  72] 
 [ 46  46 123 123 105 105  28  28 192 192 240 240  85  85 163 163  75  75 
   75 183 183 160 160 126 126 157 157 140 140 182 182  97] 
 [ 46  46 123 123 105 105  28  28 192 192 240 240  85  85 163 163  75  75 
   75 183 183 160 160 126 126 157 157 140 140 182 182  97] 
 [  6   6 147 147 229 229 193 193 224 224 103 103  31  31 133 133  19  19 
   19 176 176 194 194 171 171 223 223 123 123 173 173 204] 
 [  6   6 147 147 229 229 193 193 224 224 103 103  31  31 133 133  19  19 
   19 176 176 194 194 171 171 223 223 123 123 173 173 204] 
 [227 227  25  25 207 207  39  39  93  93 119 119  34  34 157 157 150 150 
  150 186 186 161 161 242 242   2   2  89  89 187 187 226] 
 [227 227  25  25 207 207  39  39  93  93 119 119  34  34 157 157 150 150 
  150 186 186 161 161 242 242   2   2  89  89 187 187 226] 
 [109 109 198 198 151 151  25  25 121 121 136 136  48  48 250 250 245 245 
  245 102 102 205 205 179 179  73  73 229 229 243 243  27] 
 [109 109 198 198 151 151  25  25 121 121 136 136  48  48 250 250 245 245 
  245 102 102 205 205 179 179  73  73 229 229 243 243  27] 
 [135 135 116 116 223 223  40  40 233 233 243 243  95  95 126 126  54  54 
   54 153 153 246 246  56  56 120 120 162 162   8   8 143] 
 [135 135 116 116 223 223  40  40 233 233 243 243  95  95 126 126  54  54 
   54 153 153 246 246  56  56 120 120 162 162   8   8 143] 
 [ 78  78 249 249  58  58 237 237  80  80 237 237  99  99  92  92  67  67 
   67 157 157  64  64 200 200   0   0 249 249  31  31  32] 
 [ 78  78 249 249  58  58 237 237  80  80 237 237  99  99  92  92  67  67 
   67 157 157  64  64 200 200   0   0 249 249  31  31  32] 
 [154 154 111 111 170 170 120 120 143 143  81  81  97  97 237 237 249 249 
  249  85  85 154 154 135 135  40  40 163 163 147 147   8] 
 [154 154 111 111 170 170 120 120 143 143  81  81  97  97 237 237 249 249 
  249  85  85 154 154 135 135  40  40 163 163 147 147   8] 
 [154 154 111 111 170 170 120 120 143 143  81  81  97  97 237 237 249 249 
  249  85  85 154 154 135 135  40  40 163 163 147 147   8] 
 [137 137 195 195 189 189 167 167 195 195 240 240 185 185 117 117 199 199 
  199 179 179  56  56 170 170  87  87 253 253  89  89 152] 
 [137 137 195 195 189 189 167 167 195 195 240 240 185 185 117 117 199 199 
  199 179 179  56  56 170 170  87  87 253 253  89  89 152] 
 [183 183 250 250  88  88 189 189 143 143 232 232   4   4 213 213 110 110 
  110 254 254 246 246 240 240 100 100 103 103  99  99 229] 
 [183 183 250 250  88  88 189 189 143 143 232 232   4   4 213 213 110 110 
  110 254 254 246 246 240 240 100 100 103 103  99  99 229] 
 [155 155 205 205  44  44 236 236  60  60  87  87 121 121 216 216  99  99 
   99   3   3 243 243  60  60 107 107 104 104 179 179  58] 
 [155 155 205 205  44  44 236 236  60  60  87  87 121 121 216 216  99  99 
   99   3   3 243 243  60  60 107 107 104 104 179 179  58] 
 [240 240  52  52 238 238 143 143  99  99  51  51 230 230 139 139  48  48 
   48   3   3 175 175  70  70 160 160 226 226  85  85 108] 
 [240 240  52  52 238 238 143 143  99  99  51  51 230 230 139 139  48  48 
   48   3   3 175 175  70  70 160 160 226 226  85  85 108] 
 [ 97  97 131 131 157 157  38  38 120 120   4   4   9   9  87  87 121 121 
  121 179 179 118 118  40  40  79  79 120 120 119 119 246] 
 [ 97  97 131 131 157 157  38  38 120 120   4   4   9   9  87  87 121 121 
  121 179 179 118 118  40  40  79  79 120 120 119 119 246] 
 [120 120 131 131  21  21  48  48 225 225 191 191 149 149 144 144 133 133 
  133  46  46  56  56 170 170 225 225 207 207  44  44 139] 
 [120 120 131 131  21  21  48  48 225 225 191 191 149 149 144 144 133 133 
  133  46  46  56  56 170 170 225 225 207 207  44  44 139] 
 [118 118  59  59 128 128 238 238 227 227 110 110  70  70 247 247 131 131 
  131 225 225 223 223  77  77  52  52 161 161 115 115 197] 
 [118 118  59  59 128 128 238 238 227 227 110 110  70  70 247 247 131 131 
  131 225 225 223 223  77  77  52  52 161 161 115 115 197]]

Thank you.

Rotem
  • 30,366
  • 4
  • 32
  • 65

1 Answers1

2

Instead of saving the figure, we have to save the image.

Since the figure is adjusted for display, it is usually different from the original image.
In your example, the original image size is 16x16, and the figure size is 32x32 so the image and the figure must be different.

Note: We may display a resized image, without resizing the source image (keeping the original image unmodified).


Example for saving the image as PNG using Pillow:

  • Convert random_data from list to NumPy array in uint8 type.

  • Convert the NumPy array to Image object, and save the image.

     random_data_arr = np.array(random_data, np.uint8)  # Convert random_data to NumPy array of type 'uint8'
     pil_image = Image.fromarray(random_data_arr)  # Convert the NumPy array to PIL image
     pil_image.save('test.png')  # Save the image in PNG format
    

Example for loading the PNG image and comparing with random_data_arr:

  • Use Image.open('test.png') to open the image.

  • Convert the image object to NumPy array.

  • Compare the arrays using np.array_equal method

     test_output = Image.open('test.png')
     test_output_gray = np.array(test_output)
     are_equal = np.array_equal(random_data_arr, test_output_gray)  # True if random_data_arr and test_output_gray are equal
     print('are_equal = ' + str(are_equal))  # are_equal = True
    

Question 2

It is possible to use newer image formats after installing the suitable Python package.

There may be limitations - WebP packge doesn't support grayscale image for example, so we have to convert the image to RGB before saving, and convert back to grayscale after loading.

Example using WebP format:
Install WebP Python bindings package: pip install webp.

Example for saving, loading and comparing using WebP image format:

webp.save_image(pil_image.convert('RGB'), 'test.webp', lossless=True)

webp_output = webp.load_image('test.webp')  # Load test.webp to PIL image
webp_output_gray = np.array(webp_output.convert('L'))  # Convert to grayscale and to NumPy array

are_webp_equal = np.array_equal(random_data_arr, webp_output_gray)  # True if random_data_arr and webp_output_gray are equal
print('are_webp_equal = ' + str(are_webp_equal))  # are_webp_equal = True

Question 3

The best image format (in term of file size), is domain specific - the best format is dependent on the content of the image.
When using lossy image compression, the selected format is more relevant.
When using lossless image compression as in your question, in most cases the output file is not going to be much smaller (in most cases no less than half of the PNG file).
Note: Random data is not compressed well at all.


Complete code sample:

#import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import webp

#np.set_printoptions(threshold=sys.maxsize)

# Randomly generated data table in 0-255
random_data = ([179,22,29,72,118,117,88,182,155,114,95,62,75,67,30,252], 
[161,88,76,74,70,99,136,246,178,113,233,125,177,135,94,72], 
[46,123,106,28,192,240,85,164,75,183,160,126,157,140,182,98], 
[6,147,229,193,224,103,31,133,19,176,194,171,223,123,173,204], 
[228,25,207,39,93,119,34,157,150,186,161,242,2,89,187,226], 
[109,198,151,25,122,136,48,250,245,102,205,180,74,229,243,27], 
[135,116,223,40,233,243,95,126,54,153,246,57,120,162,8,143], 
[78,249,58,237,80,237,99,92,67,157,64,200,0,249,31,33], 
[154,111,170,120,143,81,97,237,249,85,154,135,41,163,147,8], 
[137,195,189,167,196,240,185,117,199,179,57,170,87,253,89,152], 
[183,250,88,189,143,232,4,213,110,254,246,240,100,103,99,229], 
[155,205,45,236,60,87,121,216,99,3,243,61,107,104,180,58], 
[240,52,238,143,99,51,230,139,49,3,175,70,160,226,85,108], 
[98,131,157,38,120,4,9,87,122,179,118,41,79,120,119,246],
[120,131,21,48,225,191,149,144,133,46,56,170,225,207,45,139], 
[118,59,128,238,228,110,70,247,132,225,223,77,53,161,115,197]) 

# Trying to plot the data in 32x32 pixel image then saving it
#plt.figure(figsize=(.32, .32), dpi=100, frameon=False)
#plt.imshow(random_data, interpolation='none', vmin=0, vmax=255, cmap='gray') 
#plt.axis('off')
#plt.savefig('test.png', dpi=133, bbox_inches='tight', pad_inches=0)

random_data_arr = np.array(random_data, np.uint8)  # Convert random_data to NumPy array of type 'uint8'
pil_image = Image.fromarray(random_data_arr)  # Convert the NumPy array to PIL image
pil_image.save('test.png')  # Save the image in PNG format

# Loading saved image then converting it to array to compare it against the original input data
test_output = Image.open('test.png')
#test_output_gray = test_output.convert('L')
test_output_gray = np.array(test_output)
print('shape = ' + str(test_output_gray.shape))

are_equal = np.array_equal(random_data_arr, test_output_gray)  # True if random_data_arr and test_output_gray are equal
print('are_equal = ' + str(are_equal))

################################################################################
# Save image in WebP format.
# WebP does not support Grayscale - convert to RGB before saving
webp.save_image(pil_image.convert('RGB'), 'test.webp', lossless=True)

webp_output = webp.load_image('test.webp')  # Load test.webp to PIL image
webp_output_gray = np.array(webp_output.convert('L'))  # Convert to grayscale and to NumPy array

are_webp_equal = np.array_equal(random_data_arr, webp_output_gray)  # True if random_data_arr and webp_output_gray are equal
print('are_webp_equal = ' + str(are_webp_equal))
################################################################################
Rotem
  • 30,366
  • 4
  • 32
  • 65