For an uni assignment I have been giving the task of making my own rotating algorithm in python. This is my code so far.

import cv2
import math
import numpy as np

class rotator:

    angle = 20.0
    x = 330
    y = 330

    radians = float(angle*(math.pi/180))
    img = cv2.imread('FNAF.png',0)
    width,height = img.shape

    def showImg(name, self):
        cv2.imshow(name, self.img)
        self.img = np.pad(self.img, (self.height) ,'constant', constant_values=0)
        self.width,self.height = self.img.shape

    def printWH(self):

    def rotate(self):
        emptyF = np.zeros((self.width,self.height),dtype="uint8")
        emptyB = np.zeros((self.width,self.height),dtype="uint8")
        emptyBB = np.zeros((self.width,self.height),dtype="uint8")

        for i in range(self.width):
            for j in range(self.height):
                temp = self.img[i,j]
                #forward mapping
                xf = (i-self.x)*math.cos(self.radians)-(j-self.y)*math.sin(self.radians)+self.x
                yf = (i-self.x)*math.sin(self.radians)+(j-self.y)*math.cos(self.radians)+self.x
                #backward mapping should change the forward mapping to the original image
                xbb = (i-self.x)*math.cos(self.radians)+(j-self.y)*math.sin(self.radians)+self.x
                ybb = -(i-self.x)*math.sin(self.radians)+(j-self.y)*math.cos(self.radians)+self.x
                xbb = int(xbb)
                ybb = int(ybb)
                if xf < 660 and yf < 660 and xf>0 and yf > 0:
                    emptyF[int(xf),int(yf)] = temp
                if xbb < 660 and ybb < 660 and xbb>0 and ybb > 0:
                    emptyBB[(xbb),(ybb)] = temp
        cv2.imshow('Forward', emptyF)
        cv2.imshow('Backward', emptyBB)
def main():
    rotator.showImg('normal', rotator)

if __name__ == '__main__':

Is there any way to improve this code or my algorithm? Any advise would be awesome.

NB. I also hav a problem with my backwards mapping. The output image has a lot of small black dots. Any suggestions to what that might be?

Please let me know.

  • 1
    \$\begingroup\$ I suspect that the black dots might be due to integer rounding of trigonometric results. \$\endgroup\$ – 200_success Nov 21 '18 at 1:25
  • \$\begingroup\$ Why have you removed the code? You are not allowed do change it after receiving answers. I rolledback the changes. \$\endgroup\$ – t3chb0t Dec 10 '18 at 10:39
  • 1
    \$\begingroup\$ I rolled back your last edit. After getting an answer you are not allowed to change your code anymore. This is to ensure that answers do not get invalidated and have to hit a moving target. If you have changed your code you can either post it as an answer (if it would constitute a code review) or ask a new question with your changed code (linking back to this one as reference). Refer to this post for more information \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Dec 11 '18 at 0:50

The most common method for getting rid of the black dots is to paint over each pixel of the entire destination image with pixels copied from computed positions in the source image, instead of painting the source image pixels into computed positions in the destination image.

for i in range(660):
    for j in range(660):
        xb = int(...)
        yb = int(...)
        if xb in range(self.width) and yb in range(self.height):
            emptyF[i, j] = self.img[xb, yb]

Computing destination pixels positions from the source pixel positions may result in tiny areas unpainted in the destination due to numeric errors/limits.

Computing source pixels positions from the destination pixel position may result in a pixel value being copied to two adjacent locations, and other pixels never being copied from, but this is usually/often unnoticeable.

  • \$\begingroup\$ Well that worked! Thanks man. How can you apply this method for both the forward and backwards mapping? \$\endgroup\$ – ACommonDane01 Nov 21 '18 at 11:41
  • \$\begingroup\$ However, I can't seem to get the range(660) right instead of my range(self.width). Any suggestions to that? \$\endgroup\$ – ACommonDane01 Nov 21 '18 at 11:47
  • \$\begingroup\$ Apply the same technique for both forward & reverse rotations. Ie, duplicate the last 4 lines and change xb/xf/emptyF to xf/yf/emptyBB. I don’t know what you mean by “get range(660) right” Even 660 is a magic number whose origin is a mystery. If code does not work properly, StackOverflow is a more appropriate forum to get help fixing code. \$\endgroup\$ – AJNeufeld Nov 21 '18 at 14:37
  • 1
    \$\begingroup\$ Going from destination pixels to source pixels is the right idea, so that each destination pixel is painted just once. But looping in native Python over the individual pixels is going to be slow — better to use numpy.meshgrid to generate the coordinates and then transform them all at once. See §2 of this answer for details. \$\endgroup\$ – Gareth Rees Nov 21 '18 at 16:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.