6
\$\begingroup\$

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):
 print(self.width)
 print(self.height)
 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
 else:
 pass
 if xbb < 660 and ybb < 660 and xbb>0 and ybb > 0:
 emptyBB[(xbb),(ybb)] = temp
 else:
 pass
 cv2.imshow('Forward', emptyF)
 cv2.imshow('Backward', emptyBB)
def main():
 rotator.showImg('normal', rotator)
 rotator.printWH(rotator)
 rotator.rotate(rotator)
 cv2.waitKey(0)
 cv2.destroyAllWindows
if __name__ == '__main__':
 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.

Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges201 bronze badges
asked Nov 21, 2018 at 1:16
\$\endgroup\$
3
  • 1
    \$\begingroup\$ I suspect that the black dots might be due to integer rounding of trigonometric results. \$\endgroup\$ Commented Nov 21, 2018 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\$ Commented Dec 10, 2018 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\$ Commented Dec 11, 2018 at 0:50

1 Answer 1

3
\$\begingroup\$

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.

answered Nov 21, 2018 at 4:58
\$\endgroup\$
4
  • \$\begingroup\$ Well that worked! Thanks man. How can you apply this method for both the forward and backwards mapping? \$\endgroup\$ Commented Nov 21, 2018 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\$ Commented Nov 21, 2018 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\$ Commented Nov 21, 2018 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\$ Commented Nov 21, 2018 at 16:59

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.