Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating image tiles (m*n) of original image using Python and Numpy

I am using numpy to create tiles of (224*224) from my 16-bit tiff image (13777*16004). I was able to crop/slice into equal tiles of 224*224 along the rows and columns. I ran into problems while trying to create new tiles shifting by half of the tile size... For instance: A rough algorithm of what i am trying to achieve

(1:224, 1:224)

(1:224, 112:336)

( , 224:448)

The goal is to retain tile size (224*224) while shifting by half of tile size to obtain more image tiles...

Snippet of code written to perform task

row_x =  img.shape[0]
column_y = img.shape[1]

tile_size_x = 224
tile_size_y = 224


range_x = mpz(ceil(row_x/tile_size_x))
range_y = mpz(ceil(column_y/tile_size_y))

for x in range(range_x, row_x):

    for y in range(range_y, column_y): 

        x0 = x * tile_size_x 

        x1 = int(x0/2) + tile_size_x

        y0 = y * tile_size_y 

        y1 = int(y0/2) + tile_size_y



        z = img[x0:x1, y0:y1]
        print (z.shape,z.dtype)

I keep getting wrong results, can anyone help ???

like image 561
Victor Alhassan Avatar asked Aug 30 '17 00:08

Victor Alhassan


4 Answers

You went a little off while calculating the range of your for loop. The number of slices to be made, must be calculated using the offset between two slices, which is x0/2 in your case, I have simplified your code and defined some meaningful variables which you can configure to get desired tiles from a given image:

enter image description here

import cv2
import math

img = cv2.imread("/path/to/lena.png") # 512x512

img_shape = img.shape
tile_size = (256, 256)
offset = (256, 256)

for i in xrange(int(math.ceil(img_shape[0]/(offset[1] * 1.0)))):
    for j in xrange(int(math.ceil(img_shape[1]/(offset[0] * 1.0)))):
        cropped_img = img[offset[1]*i:min(offset[1]*i+tile_size[1], img_shape[0]), offset[0]*j:min(offset[0]*j+tile_size[0], img_shape[1])]
        # Debugging the tiles
        cv2.imwrite("debug_" + str(i) + "_" + str(j) + ".png", cropped_img)

As current offset if exact multiple of image dimensions, which is 512x512, hence we will get 4 tiles of same size:

enter image description here enter image description here enter image description here enter image description here

Changing the value of offset, would get you tiles of irregular size, if the offset if not exact multiple of the image dimensions, you may later filter those tiles if not required by changing the math.ceil to math.floor in the for loop.

like image 151
ZdaR Avatar answered Oct 05 '22 14:10

ZdaR


You can use as_strided for this pretty efficiently I think.

def window_nd(a, window, steps = None):
    ashp = np.array(a.shape)
    wshp = np.array(window).reshape(-1)
    if steps:
        stp = np.array(steps).reshape(-1)
    else:
        stp = np.ones_like(ashp)
    astr = np.array(a.strides)
    assert np.all(np.r_[ashp.size == wshp.size, wshp.size == stp.size, wshp <= ashp])
    shape = tuple((ashp - wshp) // stp + 1) + tuple(wshp)
    strides = tuple(astr * stp) + tuple(astr)
    as_strided = np.lib.stride_tricks.as_strided
    aview = as_strided(a, shape = shape, strides = strides)
    return aview

EDIT: Generalizing the striding method as much as I can.

For your specific question:

aview = window_nd(a, (288, 288), (144, 144))
z = aview.copy().reshape(-1, wx, wy) #to match expected output
print(z.shape, z.dtype) # z.shape should be (num_patches, 288, 288)
like image 23
Daniel F Avatar answered Oct 05 '22 14:10

Daniel F


I think you can use this

def TileImage(image,rows,cols):
imagename = image
im = Image.open(imagename) 
width, height = im.size
indexrow = 0
indexcolum = 0
left = 0
top = 0
right = width/col
buttom = 0
while(right<=width):    
    buttom = height/rows
    top = 0
    indexrow=0  

    while(top<height):
        print(f"h : {height}, w : {width}, left : {left},top : {top},right : {right}, buttom   :  {buttom}")
        cropimg= im.crop((left, top, right, buttom)) 
        cropimg.save(imagename + str(indexrow) + str(indexcolum) +".jpg")
        top = buttom
        indexrow += 1
        buttom += height/rows   

    indexcolum+=1
    left = right
    right += width/col 
like image 28
Imran Ahmad Shahid Avatar answered Oct 05 '22 14:10

Imran Ahmad Shahid


If you do not mind using ImageMagick, then it is trivial using the -crop command. See https://imagemagick.org/Usage/crop/#crop_tile

You can call imagemagick using Python subprocess call.

Input:

enter image description here

Lets say you want 4 256x256 tiles for simplicity.

convert lena512.png -crop 256x256 lena512_%d.png

or by percent

convert lena512.png -crop 50x50% lena512_%d.png

enter image description here

enter image description here

enter image description here

enter image description here

like image 30
fmw42 Avatar answered Oct 05 '22 16:10

fmw42