Зеркальное отображение по диагонали в Python

Я беру курс программирования на python, и мы работаем над зеркалированием изображений, определяя точку зеркала, а затем копируя пиксель с одной стороны на другую, используя вложенные циклы for. Например, для вертикального зеркального отображения изображения будет использоваться следующий код:

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)

В настоящее время я работаю над заданием, в котором нам предлагается отразить изображение по диагонали так, чтобы верхняя левая сторона отражалась в нижней правой части. Каждый пример и ответ, которые я нашел до сих пор, работают только для квадратных изображений, и мне нужно иметь возможность применить это к любому изображению, желательно, определив диагональную точку зеркала. Я пытался определить точку зеркала, используя уравнение стиля y = mx + b, но я не могу понять, как сказать Python сделать это линией. Будем признательны за любую помощь, не относящуюся к квадратным изображениям!

Примечание: поскольку я здесь новенький, я пока не могу публиковать изображения, но точка диагонального зеркала будет проходить снизу слева вверх справа. Изображение в верхнем левом треугольнике будет отражаться в правом нижнем углу.


person user2054546    schedule 08.02.2013    source источник


Ответы (4)


Вы можете поменять местами верхний левый с нижним правым неквадратным массивом следующим образом:

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

Это работает для зеркального отображения по диагонали. Вы, кажется, подразумеваете, что можете отражать в каком-то произвольном месте. В этом случае вам нужно будет решить, как заполнить пиксели, у которых нет соответствующего пикселя на противоположной стороне зеркальной линии.

Вы упоминаете, что работаете над заданием, поэтому вам, вероятно, необходимо явно выполнять циклы, но обратите внимание, что если вы поместите свои данные в массив numpy, вы можете легко (и более эффективно) достичь того, что вы хотите с комбинацией функций numpy fliplr, flipud и transpose.

Также обратите внимание, что в вашем примере кода (отражение влево/вправо) вы копируете левый пиксель в правую сторону, но не наоборот, поэтому на самом деле вы не зеркально отражаете изображение.

person bogatron    schedule 08.02.2013

Просто поделимся одним способом зеркального отображения по диагонали, снизу слева направо вверх:

Учащийся должен адаптироваться, если ему нужно зеркально отразить "правый верхний угол в левый нижний" или использовать противоположную диагональ.

# !! 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)

Примечание. Это ведет себя так, как если бы мы работали с вертикальным зеркалом для каждой строки пикселей независимо, вдоль вертикальной оси, проходящей через точку mirrorPt.


Результат (Картина Антони Тапиеса):


.................введите здесь описание изображения... ..............введите здесь описание изображения...... ............






Следующее является экспериментальным и предназначено только для развлечения...

# 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)




................................................................ ........введите здесь описание изображения............ ..........................................


.......введите здесь описание изображения..........введите здесь описание изображения........введите здесь описание изображения..........введите здесь описание изображения.......



person Gauthier Boaglio    schedule 29.06.2013
comment
Эй, ты! ;) Рад, что вам нравится. Я как раз собирал ссылки на мои недавно опубликованные ответы, чтобы указать вам на них, чтобы отправить вам электронное письмо... Но вы их уже нашли... - person Gauthier Boaglio; 29.06.2013
comment
Хорошо, тогда я напишу тебе по-французски! - person Gauthier Boaglio; 29.06.2013
comment
Должно. Вы уверены, что используете в качестве входных данных точно квадратные изображения? Какую ошибку вы получаете? Я добавил к своему ответу галочку, чтобы отклонить не квадратные изображения. Ваше здоровье. - person Gauthier Boaglio; 01.07.2013
comment
@Yve Если вы все еще заинтересованы в переворачивании и/или зеркальном отражении изображений, я дополнил свой ответ выше более полным алгоритмом, позволяющим использовать все 4 комбинации при отражении по диагонали. Ваше здоровье. - person Gauthier Boaglio; 03.07.2013

Я предполагаю, что вы хотите отражать по линии 45 градусов, а не по диагонали прямоугольника.

Вы должны создать новое изображение, его ширина равна высоте оригинала, а его высота равна ширине оригинала.

Если начало вашей системы координат находится внизу слева, скопируйте точку (x, y) в оригинале в положение (y, x) на новом изображении. Если это другой угол, вам нужно подумать еще немного ;)

person MatthieuW    schedule 08.02.2013

Вот относительно простое решение, которое работает с прямоугольными изображениями. В этом примере кода предполагается, что исходное изображение взято из библиотеки Python Pillow, но эта концепция универсальна.

## 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

По сути, вы делаете то, что упоминали другие люди, чтобы поменять местами координаты x и y. Однако в случае с прямоугольником, если вы просто поменяете координаты, вы получите диагональную линию, которая не идет из угла в угол. Например, для изображения размером 1000 на 2000 пикселей замена угла (1000, 0) даст вам (0, 1000) только половину изображения.

Я предполагаю, что вы действительно хотите, чтобы нижний левый угол (0, 2000) отражался в верхнем правом углу (1000, 0). Чтобы исправить это, вам нужно работать с процентами ширины и высоты.

person Fuehnix    schedule 10.05.2021