Naman Bansal
Naman Bansal

Reputation: 81

Matplotlib: Inconsistent results with images

I am trying to plot multiple images in a figure using matplotlib.

Basically, I read the images using PIl library, convert it to numpy array and do some operation on it (setting the elements in a row to zero). Everything works fine till this point. But when I try to save the results using matplotlib, I get inconsistent results.

Please have a look at my code.

Importing the libraries

import numpy as np
import matplotlib.pyplot as plt
import PIL.Image as PI

Loading the file

fileName = 'n01978287_43.jpg'
img = PI.open(fileName)
size = 224
img = img.resize((size, size))
img = np.asarray(img, dtype=np.uint8).astype(np.float32)
img = img/255

Result 1

temp_img = np.copy(img)
temp_img[51, :, :] = 0*temp_img[51, :, :]
fig = plt.figure()
ax1 = plt.subplot(1, 6, 1)
ax1.imshow(img, interpolation='none')
ax2 = plt.subplot(1, 6, 2)
ax2.imshow(temp_img, interpolation='none')
plt.savefig('test_516.png') 
plt.close(fig) 

Result 2

temp_img = np.copy(img)
temp_img[52, :, :] = 0*temp_img[52, :, :]
fig = plt.figure()
ax1 = plt.subplot(1, 6, 1)
ax1.imshow(img, interpolation='none')
ax2 = plt.subplot(1, 6, 2)
ax2.imshow(temp_img, interpolation='none')
plt.savefig('test_526.png') 
plt.close(fig) 

Result 3

temp_img = np.copy(img)
temp_img[51, :, :] = 0*temp_img[51, :, :]
fig = plt.figure()
ax1 = plt.subplot(1, 2, 1)
ax1.imshow(img, interpolation='none')
ax2 = plt.subplot(1, 2, 2)
ax2.imshow(temp_img, interpolation='none')
plt.savefig('test_512.png') 
plt.close(fig)

Result 4

temp_img = np.copy(img)
temp_img[56, :, :] = 0*temp_img[56, :, :]
fig = plt.figure()
ax1 = plt.subplot(1, 2, 1)
ax1.imshow(img, interpolation='none')
ax2 = plt.subplot(1, 2, 2)
ax2.imshow(temp_img, interpolation='none')
plt.savefig('test_562.png') 
plt.close(fig)

Now, if you look at the results, you would notice the inconsistency.

System Setup - Python3, Matplotlib3, PIL, Numpy

Update:

After looking for ways to save a figure with the desired resolution (224*224 in this case), I wrote the following code (using multiple resources from web).

Importing libraries and loading the image file

import numpy as np
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont

fileName = 'n01978287_43.jpg'
img = Image.open(fileName)
size = 224
img = img.resize((size, size))
img = np.asarray(img, dtype=np.uint8).astype(np.float32)
img = img/255

Function to plot the grid of images

def plt_save(grid, idx, name):

    nRows = len(grid)
    nCols = len(grid[0])
    print('Clearing figure')
    plt.rcParams.update({'font.size': 8})
    wFig = (nCols+2) # Figure width (two more than nCols because I want to add ylabels on the very left and very right of figure)
    hFig = (nRows+1) # Figure height (one more than nRows becasue I want to add xlabels to the top of figure)
    fig = plt.figure(figsize=( wFig, hFig )) 
    fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)

    fig.patch.set_facecolor('grey')

    for r in range(nRows):
        for c in range(nCols):

            ax = plt.subplot2grid( shape=[hFig, wFig], loc=[r+1, c+1] )  
            im= ax.imshow(grid[r][c], interpolation='none')
            ax.spines['bottom'].set_visible(False)
            ax.spines['top'].set_visible(False)
            ax.spines['right'].set_visible(False)
            ax.spines['left'].set_visible(False)
            ax.set_xticks([])
            ax.set_yticks([])
            #fig.colorbar(im, ax=ax)
            #ax.set_aspect('auto')

            if not r:
                ax.set_title('Image',
                             rotation=22.5,
                             horizontalalignment='left',
                             verticalalignment='bottom')

            if not c:
                ax.set_ylabel('leftLabel',
                             rotation=0,
                             horizontalalignment='right',
                             verticalalignment='center')

            if c == wFig-3:
                ax2 = ax.twinx()
                #ax2.axis('off')
                ax2.set_xticks([])
                ax2.set_yticks([])
                ax2.spines['top'].set_visible(False)
                ax2.spines['right'].set_visible(False)
                ax2.spines['bottom'].set_visible(False)
                ax2.spines['left'].set_visible(False)
                ax2.set_ylabel( 'rightLabel',
                                rotation=0,
                                verticalalignment='center',
                                horizontalalignment='left' )

    print('Saving file')
    plt.savefig( ( str(idx) + '_' + name + '_' + fileName.split('.')[0] + '.png'), 
                 orientation='landscape', 
                 #bbox_inches='tight', 
                 facecolor = fig.get_facecolor(),
                 dpi=224, # DPI is 224 becasue the axis size is 1x1 inch and I want 224x224 pixels in each axis
                 transparent=True, 
                 frameon=False )
    plt.close(fig)

Loop to zero out the rows of the image (one at a time)

for i in range(0, 224):
    temp_img = np.copy(img)
    temp_img[i, :, :] = 0*temp_img[i, :, :]
    # 1*4 Grid of images (can vary based on the requirement)
    grid = [img, temp_img, img, temp_img]
    grid = [grid, grid] #2*4 grid of images
    plt_save(grid, i, 'PLT_')

Here is how one of the 224 images looks like. Image with 7th row zero

The thing is that it works perfectly as long as I stick with this kind of plot. But the moment I try to make some changes (like adding a colorbar, having some spaces between each axis etc), the image resolution changes. If I use bbox_inches = 'tight' while saving my figure, it adjusts everything but changes the original resolution while keeping the figure size constant.

Is there any other way similar to bbox_inches='tight' such that it can keep the axis resolution fixed while adjusting the figure size accordingly. Or if there is no such thing in matplotlib, could you suggest me any other way to incorporate colorbar (small spaces between axis, ylabel for each axis etc) while keeping the image resolution fixed.

Upvotes: 3

Views: 348

Answers (1)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339695

The image you start with has 224 pixels in height.

In the first two cases you distribute those over 72 pixels in the resulting image. This means any row of the image has a 72/224=32% chance of showing up in the final plot. In row number 52 you are lucky and hit this one third chance.

In the second two cases the resulting image is 226 pixels in height (i.e. just slightly larger than the original). Here you have a 2/224=0.9% chance that one row will occupy two pixels. In the case of row no. 56 you hit that unlucky chance.

Upvotes: 2

Related Questions