Academic
Academic

Reputation: 295

Doing naive affine_transforms (shear numpy image using numpy)

enter image description here enter image description here

from scipy import ndimage

height, width, colors = image.shape

transform = [[1, 0, 0],
             [0.5, 1, 0],
             [0, 0, 1]]
sheared_array = ndimage.affine_transform(image,
                                         transform,
                                         offset=(0, -height*0.7, 0),
                                         output_shape=(height, width*2, colors))

plt.imshow(sheared_array)

My current code does this. My aim is to shear the image by any degree X.

I want to do the same thing with a naive approach. As in, without any pre-defined functions. Just python/numpy code from scratch.

Upvotes: 0

Views: 506

Answers (1)

user7711283
user7711283

Reputation:

Given the image: sourceImage the following code should do what you want to achieve. It copies y-rows of pixels from the numpy array representing the source image to a new created wider image at appropriate x-offsets calculated from the given shear angle. The variable names in a following code are chosen in a way explaining what they are used for providing further details about what the code does:

from PIL import Image
import numpy as np

shearAngleDegrees = 30
PILimg = Image.open('shearNumpyImageByAngle.jpg')
#PILimg.show()
npImg = np.asarray(PILimg)

def shearNpImgByAngle(numpyImageArray, shearAngleDegrees, maxShearAngle=75): 
    import numpy as np
    from math import tan, radians
    assert -maxShearAngle <= shearAngleDegrees <= maxShearAngle
    ccw = True if shearAngleDegrees > 0 else False # shear counter-clockwise?
    imgH, imgW, imgRGBtplItems = npImg.shape
    shearAngleRadians = radians(shearAngleDegrees)
    imgWplus2imgH = abs(tan(shearAngleRadians)) # (plus in width)/(image height)
    imgWplus      = int((imgH-1)*imgWplus2imgH)     # image width increase in pixels
    npImgOut = np.zeros((imgH, imgW+imgWplus, imgRGBtplItems), dtype='uint8')
    Wplus, Wplus2H = (0, -imgWplus2imgH) if ccw else (imgWplus,imgWplus2imgH) 
    for y in range(imgH):
        shiftX = Wplus-int(y*Wplus2H)
        npImgOut[y][shiftX:shiftX+imgW] = npImg[y]
    return npImgOut
#:def

npImgOut = shearNpImgByAngle(npImg, shearAngleDegrees)

PILout = Image.fromarray(npImgOut)
PILout.show()
PILout.save('shearNumpyImageByAngle_shearedBy30deg.jpg')

gives: imageSheared



As a nice add-on to the above code an extension filling the black edges of the sheared image mirroring the source picture around its sides:

def filledShearNpImgByAngle(npImg, angleDeg, fill=True, maxAngle=75): 
    import numpy as np
    from math import tan, radians
    assert -maxAngle <= angleDeg <= maxAngle
    ccw = True if angleDeg > 0 else False # shear counter-clockwise?
    imgH, imgW, imgRGBtplItems = npImg.shape
    angleRad = radians(angleDeg)
    imgWplus2imgH = abs(tan(angleRad)) # (plus in width)/(image height)
    imgWplus      = int((imgH-1)*imgWplus2imgH) # image add. width in pixels
    npImgOut = np.zeros((imgH, imgW+imgWplus, imgRGBtplItems), 
                                 dtype=npImg.dtype) # 'uint8')
    Wplus, Wplus2H = (0, -imgWplus2imgH) if ccw else (imgWplus, imgWplus2imgH) 
    for y in range(imgH):
        shiftXy = Wplus-int(y*Wplus2H)
        npImgOut[y][shiftXy:shiftXy+imgW] = npImg[y]
        if fill:
            assert imgW > imgWplus
            npImgOut[y][0:shiftXy] = np.flip(npImg[y][0:shiftXy], axis=0)
            npImgOut[y][imgW+shiftXy:imgW+imgWplus] = np.flip(npImg[y][imgW-imgWplus-1+shiftXy:imgW-1], axis=0)
[imgW-x-2]
    return npImgOut
#:def
from PIL import Image
import numpy as np
PILimg = Image.open('shearNumpyImageByAngle.jpg')
npImg = np.asarray(PILimg)
shearAngleDegrees = 20
npImgOut = filledShearNpImgByAngle(npImg, shearAngleDegrees)#, fill=False)
shearAngleDegrees = 10
npImgOut = filledShearNpImgByAngle(npImgOut, shearAngleDegrees)#, fill=False)
PILout = Image.fromarray(npImgOut)
PILout.show()
PILout.save('shearNumpyImageByAngle_filledshearBy30deg.jpg')

gives: filledShearImage or other way around: filledShearmageCW

Upvotes: 4

Related Questions