在Python中将图像分成5x5块并计算每块的直方图

16 投票
5 回答
43590 浏览
提问于 2025-04-18 00:07

我在用Python做一个项目,具体需要做以下几件事:

  • 把一张叫做Test_Image的图片和另一张叫做Reference_image的图片,分成5x5的小块。
  • 为每个小块计算一个直方图,并把它和另一张图片中相同位置的小块进行比较。
    比如说:比较image1(1,1)image2(1,1)这两个小块。
  • 比较这两张图片的相似度(这个比较应该不受图片变换的影响)。

到目前为止,我已经用hist=numpy.histogram(image,bins=256)计算了整张图片的直方图。

我想把图片分块,然后为每个小块计算直方图。我还想用Bhattacharya系数来衡量相似度。

有没有人能指导我该怎么做呢?提前谢谢大家!:)

5 个回答

-1

为了把一张正方形的图片分成相同数量的小正方形块,我用了这个方法(完整的代码库可以在这里找到):

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
0

这个方法对我有效。它可以把图像分成 n*m 的小块。记得根据需要调整你的图像。

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

1

我写了这段代码,可以自动把图片分成n行m列。m和n是可以调整的参数,修改起来很简单。之后,我们可以很容易地计算每个小块的直方图,并把它们保存到一个叫做“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()
5

如果你的图片很大,可以通过调整数组的步幅来提高性能,从而生成你需要的窗口。下面的内容将使用一个通用的滑动窗口函数,详细信息可以在Efficient Overlapping Windows with Numpy找到,我会在最后附上这个函数。

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])

滑动窗口函数:

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)

如果你想把一张图片分成四个部分,你需要计算wsss这两个参数。如果两个维度都能被二整除,那么wsss的值就是一样的(如果没有特别指定,ss默认等于ws)。Numpy可以把数组的维度看作(列,行)或者(行,列)——我没有改变任何默认设置,我的是(行,列)。对于一张18x26的图片,ws = (26/2, 18/2)——每个窗口的大小是13x9,相邻的窗口通过滑动窗口得到,滑动的距离是相同的,没有重叠。如果某个维度不能被二整除,就需要确定ss,这样窗口之间会有一些重叠。对于一张18x33的图片:

>>> 
>>> 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))
>>> 

对于三维窗口(包含颜色维度的图片数据),wsss需要有三个维度。比如一张15x15的图片会有9个5x5x3的窗口。

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)
23

不确定你是不是在找这样的东西,

这是一个蛮力版本,可能会比较慢,但能完成任务。

不过你得决定怎么处理边界的问题。

如果窗口不完全合适的话,边界是不会被包含在内的。

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)

下面是结果,完整的图像在最后。

r,c 代表窗口的左上角。

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]]

撰写回答