3

I'm taking a programming class on python, and we're working on mirroring images by defining a mirror point and then copying a pixel from one side to the other using nested for loops. For example, mirroring 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'm currently working on an assignment question asking us to mirror an image diagonally so that the top left side gets reflected onto the bottom right side. Every example and answer that I've found so far only works for square images, and I need to be able to apply this to any image, preferably by defining a diagonal mirror point. I've been trying to define the mirror point using a y = mx + b style equation, but I can't figure out how to tell Python to make that a line. Any help not specific to square images would be appreciated!

Note: since I'm brand new here, I can't post images yet, but the diagonal mirror point would run from the bottom left to the top right. The image in the top left triangle would be reflected to the bottom right.

Gauthier Boaglio
  • 10,054
  • 5
  • 48
  • 85
user2054546
  • 31
  • 1
  • 1
  • 2

4 Answers4

3

You can swap upper left with lower right of a non-square array 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 mirroring along the diagonal. You appear to imply that you may want to mirror along some arbitrary location. In that case, you will need to decide how to fill in pixels that don't have a matching pixel on the opposite side of the mirror line.

You mention you are working on an assignment so you probably are required to 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 functions fliplr, flipud, and transpose.

Note also that in your code example (mirroring left/right), you are copying the left pixel to the right side, but not vice versa so you are not actually mirroring the image.

bogatron
  • 18,639
  • 6
  • 53
  • 47
2

Just to share one way of mirroring diagonally, bottom-left to top-right :

The student is in charge to adapt, if he needs to mirror from "top-right to bottom-left" or to 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 were operating a vertical mirror for each line of pixels independently, along the vertical axis passing by the mirrorPt point.


Output (Painting by Antoni Tapies):


.................enter image description here.................enter image description here..................






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's 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)




......................................................enter image description here......................................................


.......enter image description here..........enter image description here..........enter image description here..........enter image description here.......



Gauthier Boaglio
  • 10,054
  • 5
  • 48
  • 85
  • 1
    Hey you! ;) Glad that you like it. I was precisely grouping my newly posted answers links, to point you to, in order to send you an email... But you already found them... – Gauthier Boaglio Jun 29 '13 at 13:11
  • 1
    Ok, I'll write to you in French then ! – Gauthier Boaglio Jun 29 '13 at 13:13
  • It should. Are you sure you are using exactly "squared" pictures as input? What's the error you get? I added a check to reject non squared pictures to my answer. Cheers. – Gauthier Boaglio Jun 30 '13 at 20:11
  • @Yve If you are still interested in flipping and/or mirroring images, I enhanced my answer above with a more complete algorithm, allowing all the 4 combination when mirroring diagonally. Cheers. – Gauthier Boaglio Jul 03 '13 at 15:57
0

I assume you want to mirror according to a 45 degree line, not the rectangle diagonal.

You have to create a new image, it's width is height of original, and it's height is width of original.

If origin of your coordinate system is bottom left, copy the point (x, y) in the original in the (y, x) position in the new image. If it's another corner, you have to think a bit more ;)

MatthieuW
  • 2,292
  • 15
  • 25
0

Here's a relatively straightforward solution that works with rectangular images. This example code is assuming that the sourceImage is from the Python Pillow library, but the concept is universal.

## WORKS WITH RECTANGLES! 
def mirrorDiagonal(sourceImage):
    copyImage = sourceImage
    width = copyImage.width
    height = copyImage.height
    for y in range(0,height):
        ##The for loop for X is bounded by a slope of width to height.
        mirrorPoint = int((width/height)*y)
        for x in range(0, mirrorPoint):
            leftPixel = copyImage.getpixel((x,y))
            percentageX = float(x / width)
            percentageY = float(y / height)
            rightPixel = sourceImage.getpixel((width*percentageY, height*percentageX))
            copyImage.putpixel((x,y),rightPixel)
    return copyImage

Essentially, you do what other people mentioned, to swap the x and the y coordinates. However, with a rectangle, if you just swap coordinates, you'll get a diagonal line that doesn't go from corner to corner. For example, with an image that is 1000 by 2000 pixels, swapping the corner (1000, 0) would give you (0, 1000), only half the image.

What I would assume you actually want is the the bottom left corner of (0, 2000) to be reflected to the top right corner of (1000, 0). To remedy this, you need to work with percentages of the width and height.

Fuehnix
  • 1
  • 1