Mirroring an image diagonally in Python
I am taking a python programming class and we are working on mirroring images by defining the point of the mirror and then copying the pixel from one side to the other using nested for loops. For example, flipping an image vertically would use the following code:
def mirrorVertical(source):
mirrorPoint = getWidth(source) / 2
width = getWidth(source)
for y in range(0,getHeight(source)):
for x in range(0,mirrorPoint):
leftPixel = getPixel(source,x,y)
rightPixel = getPixel(source,width - x - 1,y)
color = getColor(leftPixel)
setColor(rightPixel,color)
I am currently working on an assignment asking us to display the image diagonally so that the upper left is reflected on the lower right side. Every example and answer I've found so far only works for square images, and I need to be able to apply that to any image, preferably by specifying a diagonal mirror point. I tried to determine the mirror point using the y = mx + b style equation, but I can't figure out how to tell Python to make this line. Any help not related to square images would be appreciated!
Note. Since I'm a newbie here, I can't post images, but the diagonal point of the mirror will work from bottom-left to top-right. The image in the upper left triangle will be displayed in the lower right corner.
You can swap the top left bottom right rectangle like this:
height = getHeight(source)
width = getWidth(source)
for i in range(height - 1):
for j in range(int(width * float(height - i) / height)):
# Swap pixel i,j with j,i
This works for diagonal mirroring. You seem to be implying that you might want to reflect in some arbitrary place. In this case, you will need to decide how to fill in pixels that do not have a corresponding pixel on the opposite side of the specular line.
You mentioned that you are working on assignment, so you should probably do the loops explicitly, but note that if you put your data into a numpy array, you can easily (and more efficiently) achieve what you want with a combination of the numpy function fliplr
, flipud
and transpose
.
Note that in the example code (left / right mirroring) you are copying the left pixel to the right side, but not vice versa, so you are not actually mirroring the image.
Just to split one way of mirroring diagonally, bottom-left to top-right :
The student must adapt if he needs to mirror "top right to left" or use the opposite diagonal.
# !! Works only with squared pictures... !!
def diagMirrorPicture(picture):
height = getHeight(picture)
width = getWidth(picture)
if (height != width):
printNow("Error: The input image is not squared. Found [" + \
str(width) + " x " + str(height) + "] !")
return None
newPicture = makeEmptyPicture(width, height)
mirrorPt = 0
for x in range(0, width, 1):
for y in range(mirrorPt, height, 1):
sourcePixel = getPixel(picture, x, y)
color = getColor(sourcePixel)
# Copy bottom-left as is
targetPixel = getPixel(newPicture, x, y)
setColor(targetPixel, color)
# Mirror bottom-left to top right
targetPixel = getPixel(newPicture, y, x)
# ^^^^ (simply invert x and y)
setColor(targetPixel, color)
# Here we shift the mirror point
mirrorPt += 1
return newPicture
file = pickAFile()
picture = makePicture(file)
picture = diagMirrorPicture(picture)
if (picture):
writePictureTo(picture, "/home/mirror-diag2.png")
show(picture)
Note. This behaves as if we are controlling a vertical mirror for each line of pixels independently of each other along a vertical axis passing through a point mirrorPt
.
Exit (painting by Anthony Tapies ):
................. ................. ................ ..
The following is experimental and just for fun ...
# Draw point, with check if the point is in the image area
def drawPoint(pic, col, x, y):
if (x >= 0) and (x < getWidth(pic)) and (y >= 0) and (y < getHeight(pic)):
px = getPixel(pic, x, y)
setColor(px, col)
# Draw line segment given two points
# From Bresenham line algorithm :
# http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
def drawLine(pic, col, x0, y0, x1, y1):
dx = abs(x1-x0)
dy = abs(y1-y0)
sx = sy = 0
#sx = 1 if x0 < x1 else -1
#sy = 1 if y0 < y1 else -1
if (x0 < x1):
sx = 1
else:
sx = -1
if (y0 < y1):
sy = 1
else:
sy = -1
err = dx - dy
while (True):
drawPoint(pic, col, x0, y0)
if (x0 == x1) and (y0 == y1):
break
e2 = 2 * err
if (e2 > -dy):
err = err - dy
x0 = x0 + sx
if (x0 == x1) and (y0 == y1):
drawPoint(pic, col, x0, y0)
break
if (e2 < dx):
err = err + dx
y0 = y0 + sy
# Works only with squared cropped areas :
# i.e. in [(x0, y0), (x1, y1)], abs(x1-x0) must be equal to abs(y1-y0)
#
# USAGE :
# * To get bottom reflected to top use x0 > x1
# * To get top reflected to bottom use x0 < x1
def diagCropAndMirrorPicture(pic, startPt, endPt):
w = getWidth(pic)
h = getHeight(pic)
if (startPt[0] < 0) or (startPt[0] >= w) or \
(startPt[1] < 0) or (startPt[1] >= h) or \
(endPt[0] < 0) or (endPt[0] >= w) or \
(endPt[1] < 0) or (endPt[1] >= h):
printNow("Error: The input points must be in the image range !")
return None
new_w = abs(startPt[0] - endPt[0])
new_h = abs(startPt[1] - endPt[1])
if (new_w != new_h):
printNow("Error: The input points do not form a square !")
return None
printNow("Given: (" + str(startPt[0]) + ", " + str(endPt[0]) + ") and (" \
+ str(startPt[1]) + ", " + str(endPt[1]) + ")")
newPicture = makeEmptyPicture(new_w, new_h)
if (startPt[0] < endPt[0]):
offsetX = startPt[0]
switchX = False
switchTB = True
else:
offsetX = endPt[0]
switchX = True
switchTB = False
if (startPt[1] < endPt[1]):
offsetY = startPt[1]
switchY = False
else:
offsetY = endPt[1]
switchY = True
# (switchX XOR switchY)
changeDiag = (switchX != switchY)
mirror_pt = 0
for x in range(0, new_w, 1):
for y in range(mirror_pt, new_h, 1):
#for y in range(0, new_h, 1):
oldX = x
oldY = y
if (switchTB):
sourcePixel = getPixel(picture, offsetX+new_w-1- oldX, offsetY+new_h-1- oldY)
else:
sourcePixel = getPixel(picture, offsetX+oldX, offsetY+oldY)
color = getColor(sourcePixel)
if (changeDiag):
newX = new_w-1 - x
newY = new_h-1 - y
#printNow("Change Diag !")
else:
newX = x
newY = y
# Copied half
if (switchTB):
targetPixel = getPixel(newPicture, new_w-1- x, new_h-1- y)
else:
targetPixel = getPixel(newPicture, x, y)
setColor(targetPixel, color)
# Mirror half (simply invert x and y)
if (switchTB):
targetPixel = getPixel(newPicture, new_h-1- newY, new_w-1- newX)
else:
targetPixel = getPixel(newPicture, newY, newX)
setColor(targetPixel, color)
# Here we shift the mirror point
if (not changeDiag):
mirror_pt += 1
return newPicture
file = pickAFile()
pic = makePicture(file)
picture = makePicture(file)
# Draw working area
drawLine(pic, white, 30, 60, 150, 180)
drawLine(pic, white, 30, 180, 150, 60)
drawLine(pic, black, 30, 60, 30, 180)
drawLine(pic, black, 30, 60, 150, 60)
drawLine(pic, black, 150, 60, 150, 180)
drawLine(pic, black, 30, 180, 150, 180)
show(pic)
writePictureTo(pic, "D:\\pic.png")
# Build cropped and mirrored areas
pic1 = diagCropAndMirrorPicture(picture, (150, 60), (30, 180))
pic2 = diagCropAndMirrorPicture(picture, (30, 180), (150, 60))
pic3 = diagCropAndMirrorPicture(picture, (150, 180), (30, 60))
pic4 = diagCropAndMirrorPicture(picture, (30, 60), (150, 180))
# Show cropped and mirrored areas
if (pic1):
writePictureTo(pic1, "D:\\pic1.png")
show(pic1)
if (pic2):
writePictureTo(pic2, "D:\\pic2.png")
show(pic2)
if (pic3):
writePictureTo(pic3, "D:\\pic3.png")
show(pic3)
if (pic4):
writePictureTo(pic4, "D:\\pic4.png")
show(pic4)
.................................................. .... .............................................. ........
....... .......... .......... .......... .......
I am assuming that you want to flip the 45 degree line and not the diagonal of the rectangle.
You need to create a new image, width is the height of the original and height is the width of the original.
If your origin is at the bottom left, copy point (x, y) in the original to position (y, x) in the new image. If it's a different angle, you need to think a little more;)