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.
-
1\$\begingroup\$ I suspect that the black dots might be due to integer rounding of trigonometric results. \$\endgroup\$200_success– 200_success2018年11月21日 01:25:16 +00:00Commented 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\$t3chb0t– t3chb0t2018年12月10日 10:39:22 +00:00Commented 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\$Sᴀᴍ Onᴇᴌᴀ– Sᴀᴍ Onᴇᴌᴀ ♦2018年12月11日 00:50:08 +00:00Commented Dec 11, 2018 at 0:50
1 Answer 1
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– ACommonDane012018年11月21日 11:41:37 +00:00Commented 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\$ACommonDane01– ACommonDane012018年11月21日 11:47:25 +00:00Commented 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\$AJNeufeld– AJNeufeld2018年11月21日 14:37:37 +00:00Commented 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\$Gareth Rees– Gareth Rees2018年11月21日 16:59:35 +00:00Commented Nov 21, 2018 at 16:59