Divide an image into 5x5 blocks in python and compute histogram for each block
Asked Answered
C

5

16

Using Python, I have to:

  • Divide a Test_Image and Reference_image into 5x5 blocks,
  • Compute a histogram for each block, and compare it with the same block in the other image.
    For Example: image1(1,1) with image2(1,1).
  • Compare the similarity between two images (should be transform invariant).

So far, I have calculated the histogram of the whole image using hist=numpy.histogram(image,bins=256)

I want to divide an image, and later compute the histogram for all those blocks . I also want to use Bhattacharya's coefficient to measure the similarity.

Can anyone guide me with how to go through this one? Thanks in advance :)

Chalcanthite answered 27/3, 2014 at 10:47 Comment(5)
It's unclear what you are asking. Are you asking how to calculate the histogram of a 5x5 area or are you asking how to use Bhattacharya s coefficient to measure the similarity? You will have a higher chance of getting a good answer if you write a clear and specific question (with one question only) that includes a minimal working example people can play with.Minimal
@kkuilla- I have to compute similarity between two images for that i have to divide those images into 5 x 5 regions and calculate histograms for each region. Both the Images will be divided into 5 x 5 regions. Then i will compare each block in Image 1 with corresponding block on the other Image. Did i make it Clear?Chalcanthite
Well, I sort of understand what you are trying to do but I still don't know what your question is.Minimal
Well Thanks@Minimal i need an example of how to divide an image into multiple parts.Chalcanthite
See this answer https://mcmap.net/q/247175/-resize-a-2d-numpy-array-excluding-nan. It includes a function and a link to the source for producing windows from a numpy array. Searching SO for numpy and sliding windows should produce similar results.Impassion
M
23

Not sure if it is something like this you are looking for, This is the brute-force version.and it's probably quite slow.but it does the job You have to decide what to do with the boundaries though. This will not include the boundary unless the window fits exactly

import numpy as numpy

grey_levels = 256
# Generate a test image
test_image = numpy.random.randint(0,grey_levels, size=(11,11))

# Define the window size
windowsize_r = 5
windowsize_c = 5

# Crop out the window and calculate the histogram
for r in range(0,test_image.shape[0] - windowsize_r, windowsize_r):
    for c in range(0,test_image.shape[1] - windowsize_c, windowsize_c):
        window = test_image[r:r+windowsize_r,c:c+windowsize_c]
        hist = numpy.histogram(window,bins=grey_levels)

Below is the result and the full image is at the end. r,c represents the topleft corner of the window

r=0,c=0
[[ 63 173 131 205 239]
 [106  37 156  48  81]
 [ 85  85 119  60 228]
 [236  79 247   1 206]
 [ 97  50 117  96 206]]

r=0,c=5
[[108 241 155 214 183]
 [202   2 236 183 225]
 [214 141   1 185 115]
 [  4 234 249  95  67]
 [232 217 116 211  24]]

r=5,c=0
[[179 155  41  47 190]
 [159  69 211  41  92]
 [ 64 184 187 104 245]
 [190 199  71 228 166]
 [117  56  92   5 186]]

r=5,c=5
[[ 68   6  69  63 242]
 [213 133 139  59  44]
 [236  69 148 196 215]
 [ 41 228 198 115 107]
 [109 236 191  48  53]]

[[ 63 173 131 205 239 108 241 155 214 183  42]
 [106  37 156  48  81 202   2 236 183 225   4]
 [ 85  85 119  60 228 214 141   1 185 115  80]
 [236  79 247   1 206   4 234 249  95  67 203]
 [ 97  50 117  96 206 232 217 116 211  24 242]
 [179 155  41  47 190  68   6  69  63 242 162]
 [159  69 211  41  92 213 133 139  59  44 196]
 [ 64 184 187 104 245 236  69 148 196 215  91]
 [190 199  71 228 166  41 228 198 115 107  82]
 [117  56  92   5 186 109 236 191  48  53  65]
 [177 170 114 163 101  54  80  25 112  35  85]]
Minimal answered 27/3, 2014 at 23:0 Comment(3)
helped me a tonWestsouthwest
Btw can you please tell me how I can build my image from my windows.Chrysanthemum
@Mohamed-AliElakhrass Apologies but I don't understand. Your windows are a subset of the image so the windows are just 5x5 pieces of the original image. You know the coordinates of each window so you just piece them back togetherMinimal
I
5

If your images are large, you can improve performance by manipulating the array's strides to produce the windows you need. The following will use a generalized sliding window function found at Efficient Overlapping Windows with Numpy - I will include it at the end.

import numpy as np
image1 = np.arange(100).reshape(10,10)
image2 = np.arange(100).reshape(10,10)

from itertools import izip
window_size = (5,5)
windows1 = sliding_window(image1, window_size)
windows2 = sliding_window(image2, window_size)
histograms = [(np.histogram(window1,bins=256),np.histogram(window2,bins=256))
              for window1, window2 in izip(windows1, windows2)]

for h1, h2 in histograms:
    print np.all(h1[0] == h2[0])

sliding window function(s):

from numpy.lib.stride_tricks import as_strided as ast
from itertools import product

def norm_shape(shape):
    '''
    Normalize numpy array shapes so they're always expressed as a tuple, 
    even for one-dimensional shapes.

    Parameters
        shape - an int, or a tuple of ints

    Returns
        a shape tuple
    '''
    try:
        i = int(shape)
        return (i,)
    except TypeError:
        # shape was not a number
        pass

    try:
        t = tuple(shape)
        return t
    except TypeError:
        # shape was not iterable
        pass

    raise TypeError('shape must be an int, or a tuple of ints')


def sliding_window(a,ws,ss = None,flatten = True):
    '''
    Return a sliding window over a in any number of dimensions

    Parameters:
        a  - an n-dimensional numpy array
        ws - an int (a is 1D) or tuple (a is 2D or greater) representing the size 
             of each dimension of the window
        ss - an int (a is 1D) or tuple (a is 2D or greater) representing the 
             amount to slide the window in each dimension. If not specified, it
             defaults to ws.
        flatten - if True, all slices are flattened, otherwise, there is an 
                  extra dimension for each dimension of the input.

    Returns
        an array containing each n-dimensional window from a

    from http://www.johnvinyard.com/blog/?p=268
    '''

    if None is ss:
        # ss was not provided. the windows will not overlap in any direction.
        ss = ws
    ws = norm_shape(ws)
    ss = norm_shape(ss)

    # convert ws, ss, and a.shape to numpy arrays so that we can do math in every 
    # dimension at once.
    ws = np.array(ws)
    ss = np.array(ss)
    shape = np.array(a.shape)


    # ensure that ws, ss, and a.shape all have the same number of dimensions
    ls = [len(shape),len(ws),len(ss)]
    if 1 != len(set(ls)):
        raise ValueError(\
        'a.shape, ws and ss must all have the same length. They were %s' % str(ls))

    # ensure that ws is smaller than a in every dimension
    if np.any(ws > shape):
        raise ValueError('ws cannot be larger than a in any dimension. a.shape was %s and ws was %s' % (str(a.shape),str(ws)))

    # how many slices will there be in each dimension?
    newshape = norm_shape(((shape - ws) // ss) + 1)
    # the shape of the strided array will be the number of slices in each dimension
    # plus the shape of the window (tuple addition)
    newshape += norm_shape(ws)
    # the strides tuple will be the array's strides multiplied by step size, plus
    # the array's strides (tuple addition)
    newstrides = norm_shape(np.array(a.strides) * ss) + a.strides
    strided = ast(a,shape = newshape,strides = newstrides)
    if not flatten:
        return strided

    # Collapse strided so that it has one more dimension than the window.  I.e.,
    # the new array is a flat list of slices.
    meat = len(ws) if ws.shape else 0
    firstdim = (np.product(newshape[:-meat]),) if ws.shape else ()
    dim = firstdim + (newshape[-meat:])
    # remove any dimensions with size 1
    dim = filter(lambda i : i != 1,dim)
    return strided.reshape(dim)

If you want to divide an image into four parts, you need to calculate the ws and ss paramaters. If both dimensions are divisible by two then ws and ss are the same value (ss defaults to ws when not specified). Numpy has the ability to treat array dimensions as (column, row) or (row, column) - I haven't changed any defaults and mine is (row, column). For an 18x26 picture, ws = (26/2, 18/2) - each window will be 13x9 and the adjacent windows are obtained by siliding the window by an equal amount, no overlap. If a dimension is not divisable by two, ss will also need to be determined and there will be some overlap in the windows. For an 18x33 image:

>>> 
>>> rows = 33
>>> columns = 18
>>> divisor = 2
>>> col_size, col_overlap = divmod(columns, divisor)
>>> row_size, row_overlap = divmod(rows, divisor)
>>> ws = (row_size, col_size)
>>> ss = (row_size - row_overlap, col_size - col_overlap)
>>> ws, ss
((16, 9), (15, 9))
>>> 

For 3d windows (data from images with a color dimension) ws and ss need to have three dimensions. A 15x15 image will have 9 5x5x3 windows

from PIL import Image
import numpy as np

img = Image.open('15by15.bmp')
a = np.asarray(img)
window_size = (5,5,3)
windows = sliding_window(a, window_size)
print windows.shape

>>> (9, 5, 5, 3)

for window in windows:
    print window.shape

>>> (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3) (5, 5, 3)
Impassion answered 30/3, 2014 at 20:30 Comment(5)
@wwii- Thanks for your help , but what i want is divide an image into 4 parts. I have two images to compare. i want to divide both the images into 4 parts and then compare histogram of each part with the corresponding other part. Here i have two images of same size. Suppose i want to dic=vide this image into 4 parts how can i do it?Chalcanthite
If you use the sliding_window() function above, you will need to calculate the correct values for the ws and ss parameters. I'll add an example to the answer.Impassion
@Impassion I'm using the initial part of your above code to get chunks of an image. Then I try the following: window_size = (100,100) windows = sw.sliding_window(image1, window_size) for w in windows: wi = Image.fromarray(w) io.imshow(wi) But it doesn't work. I've heart there's a bug in windows. Is there another to display the image patch generated by the window?Overeat
@Overeat - You should post that as a separate question, I can't tell which modules you are using, but basically you would need to convert each window ndarray to an image and show it.Impassion
@Overeat - Using PIL I just tried Image.fromarray(w).save('foo.png') in a loop and it save all the windows that I could look at later. to see them on the fly you might have to paint them on a canvas of some sort maybe matplotlib. This may help - scipy-lectures.github.io/advanced/image_processingImpassion
T
1

I have write this code two automatically split image into n rows and m columns. m and n are arguments and it is easily modifiable. After that it is easy compute histogram for each block which are also saving into the folder named patches.

# Image path, number of rows 
# and number of columns 
# should be provided as an arguments
import cv2
import sys
import os


if not os.path.exists('patches'):
    os.makedirs('patches')



nRows = int(sys.argv[2])
# Number of columns
mCols = int(sys.argv[3])

# Reading image
img = cv2.imread(sys.argv[1])
#print img

#cv2.imshow('image',img)

# Dimensions of the image
sizeX = img.shape[1]
sizeY = img.shape[0]

print(img.shape)


for i in range(0,nRows):
    for j in range(0, mCols):
        roi = img[i*sizeY/nRows:i*sizeY/nRows + sizeY/nRows ,j*sizeX/mCols:j*sizeX/mCols + sizeX/mCols]
        cv2.imshow('rois'+str(i)+str(j), roi)
                cv2.imwrite('patches/patch_'+str(i)+str(j)+".jpg", roi)



cv2.waitKey()
Tryptophan answered 26/2, 2020 at 21:47 Comment(0)
S
0

This worked for me. It has the ability to divide into n*m chunks. Pad your image accordingly.

def chunkify(img, block_width=4, block_height=4):
  shape = img.shape
  x_len = shape[0]//block_width
  y_len = shape[1]//block_height

  chunks = []
  x_indices = [i for i in range(0, shape[0]+1, block_width)]
  y_indices = [i for i in range(0, shape[1]+1, block_height)]

  shapes = list(zip(x_indices, y_indices))

  for i in range(len(shapes)):
      try:
        start_x = shapes[i][0]
        start_y = shapes[i][1]
        end_x = shapes[i+1][0]
        end_y = shapes[i+1][1]
        chunks.append( shapes[start_x:end_x][start_y:end_y] )
      except IndexError:
        print('End of Array')

  return chunks

https://github.com/QuantumNovice/ImageProcessing/blob/master/image_chunkify.py

Sophist answered 6/8, 2019 at 10:51 Comment(0)
H
-1

To divide a square image into square blocks (same number of blocks per axis), I used this method (full repository here):

def blockDivide(img, blockNumber):

    imgArray = np.array(Image.open(img))

    # Define dimension of image
    dimension = imgArray.shape[0]

    # Set number of slices per axis
    axisSlice = int(math.sqrt(blockNumber))

    # Size of each block
    arraySize = int(dimension / axisSlice)

    # Shape of numpy array to be filled
    blocksArray = np.zeros((arraySize, arraySize, blockNumber))

    # Split the image into vertical blocks
    split_a = np.split(imgArray, axisSlice, axis = 0)

    # Set counter to zero
    counter = 0

    for i in range(axisSlice):
        for j in range(axisSlice):

            # Split vertical blocks into square blocks
            split_b = np.split(split_a[i], axisSlice, axis = 1)

            # Fill array with blocks
            blocksArray[:, :, counter] = split_b[j]

            # Increase counter
            counter += 1

    return blocksArray
Have answered 19/10, 2020 at 10:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.