Рисование диагональных линий на изображении

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

  width = getWidth(picture)
  height = getHeight(picture)
  for x in range(0, width):
    for y in range(0, height):
      pixel = getPixel(picture, x, y)
      setColor(pixel, black)

Спасибо


person user2194374    schedule 23.03.2013    source источник
comment
Вы должны упомянуть графическую библиотеку, которую вы используете...   -  person MartinStettner    schedule 23.03.2013
comment
я использую среду jython для студентов   -  person user2194374    schedule 23.03.2013


Ответы (3)


В большинстве графических библиотек есть способ напрямую рисовать линию.

В JES есть функция addLine, поэтому вы можете делать

addLine(picture, 0, 0, width, height)

Если вы застряли с установкой одиночных пикселей, вам следует взглянуть на алгоритм линии Брезенхема, который является одним из самых эффективных алгоритмов рисования линий.

Примечание к вашему коду: то, что вы делаете с двумя вложенными циклами, следующее

for each column in the picture
  for each row in the current column
     set the pixel in the current column and current row to black

так что в основном вы заполняете все изображение черными пикселями.

ИЗМЕНИТЬ

Чтобы нарисовать несколько диагональных линий по всему изображению (оставляя пространство между ними), вы можете использовать следующий цикл

width = getWidth(picture)
height = getHeight(picture)
space = 10
for x in range(0, 2*width, space):
  addLine(picture, x, 0, x-width, height)

Это дает вам изображение вроде (пример нарисован от руки...)

диагональные линии

При этом используется функция отсечения, предоставляемая большинством графических библиотек, т. е. части линии, которые не находятся внутри изображения, просто игнорируются. Обратите внимание, что без 2*width (т.е. если x идет только до with), будет нарисована только верхняя левая половина строк...

person MartinStettner    schedule 23.03.2013
comment
Я использовал addLine, но потом понял, что мне нужно, чтобы он был по всему изображению. - person user2194374; 23.03.2013
comment
А что не так с строкой, произведенной addLine? Если вы используете (0,0) и (ширину, высоту) в качестве конечных точек для линии, в конце концов, она должна проходить по всему изображению... - person MartinStettner; 23.03.2013
comment
Да, за исключением того, что он делает только толстую черную линию высотой изображения слева от изображения. Я могу добавить шаг к диапазону, который расширяет линии, но я не уверен, как сделать их диагональными. - person user2194374; 23.03.2013
comment
addLine делает только одну строку, мне нужно, чтобы это было несколько строк, равномерно распределенных по диагонали сверху справа и снизу слева. - person user2194374; 23.03.2013
comment
Есть только одна строка Справа сверху вниз слева :) Вы имеете в виду что-то вроде imgur.com/foYMBPe возможно? - person MartinStettner; 23.03.2013
comment
вот что делает addLine(picture,width,0,0,height) imgur.com/Zj5r8TV я хочу, но мне нужно, чтобы это проходило по всему изображению, но с пробелами между строками - person user2194374; 23.03.2013
comment
@MartinStettner Спасибо за прямую ссылку на линейный алгоритм Брезенхема. Это сработало как шарм! - person Gauthier Boaglio; 27.06.2013

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

(Просто потому, что грустно, что функция JES addLine рисует только черные линии и весьма ограничена...)

Примечание. В следующем коде используется алгоритм линии Брезенхэма, на который указал MartinStettner (спасибо ему).

Алгоритм линии Брезенхэма — это алгоритм, который определяет, в каком порядке формировать близкое приближение к прямой линии между двумя заданными точками. Поскольку пиксель является атомарным объектом, линию можно нарисовать на экране компьютера только с использованием некоторого приближения.

Примечание. Чтобы понять следующий код, вам нужно немного вспомнить базовые школьные курсы математики (уравнение прямой и тригонометрия).

Код:

# The following is fast implementation and contains side effects...

import random

# 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 


# Draw infinite line from segment
def drawInfiniteLine(pic, col, x0, y0, x1, y1):
   # y = m * x + b
   m = (y0-y1) / (x0-x1)
   # y0 = m * x0 + b   =>   b = y0 - m * x0
   b = y0 - m * x0

   x0 = 0
   y0 = int(m*x0 + b)
   # get a 2nd point far away from the 1st one
   x1 = getWidth(pic) 
   y1 = int(m*x1 + b)

   drawLine(pic, col, x0, y0, x1, y1)


# Draw infinite line from origin point and angle
# Angle 'theta' expressed in degres
def drawInfiniteLineA(pic, col, x, y, theta):

   # y = m * x + b
   dx = y * tan(theta * pi / 180.0)  # (need radians)
   dy = y

   if (dx == 0):
     dx += 0.000000001 # Avoid to divide by zero 

   m = dy / dx

   # y = m * x + b   =>   b = y - m * x
   b = y - m * x

   # get a 2nd point far away from the 1st one
   x1 = 2 * getWidth(pic)
   y1 = m*x1 + b

   drawInfiniteLine(pic, col, x, y, x1, y1)


# Draw multiple parallele lines, given offset and angle
def multiLines(pic, col, offset, theta, randOffset = 0):
   # Range is [-2*width, 2*width] to cover the whole surface
   for i in xrange(-2*getWidth(pic), 2*getWidth(pic), offset):
      drawInfiniteLineA(pic, col, i + random.randint(0, randOffset), 1, theta)

# Draw multiple lines, given offset, angle and angle offset
def multiLinesA(pic, col, offsetX, offsetY, theta, offsetA):
   j = 0
   # Range is [-2*width, 2*width] to cover the whole surface
   for i in xrange(-2*getWidth(pic), 2*getWidth(pic), offsetX):
      drawInfiniteLineA(pic, col, i, j, theta)
      j += offsetY
      theta += offsetA



file = pickAFile()
picture = makePicture(file)
color = makeColor(0, 65, 65) #pickAColor()
#drawline(picture, color, 10, 10, 100, 100)
#drawInfiniteLine(picture, color, 10, 10, 100, 100)
#drawInfiniteLineA(picture, color, 50, 50, 135.0)
#multiLines(picture, color, 20, 56.0)
#multiLines(picture, color, 10, 56.0, 15)
multiLinesA(picture, color, 10, 2, 1.0, 1.7) 

show(picture)


Результат (картина Пьера Сулажа):


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

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

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


Надеюсь, это повеселило и дало новые идеи студентам JES... И другим тоже...

person Gauthier Boaglio    schedule 27.06.2013
comment
@everyone Один важный побочный эффект появляется, когда m в начале drawInfiniteLine() стремится к бесконечности (переполнение с плавающей запятой). Этого можно избежать, выполнив что-то вроде: if (abs(m) > 100.0): m = 100.0 сразу после вычисления m... - person Gauthier Boaglio; 27.06.2013
comment
Я не редактирую этот ответ еще раз, но это FIX, о котором я говорю, можно увидеть здесь: stackoverflow.com/a/17288511/1715716 - person Gauthier Boaglio; 02.07.2013

Откуда взялся ваш объект picture? Что это такое? Что пока не работает? И какую библиотеку для доступа к изображениям вы пытаетесь использовать? (Я имею в виду, откуда вы берете или собираетесь получить «getWidth, getHeight, getPixel, setColor)?

Я думаю, что не существует библиотеки, которая дает вам «пиксель» как цельный объект, который можно использовать в вызове setColor, и если это произойдет, это будет самая медленная вещь в мире - может быть, во всей галактике.

С другой стороны, если бы эти методы существовали и ваше изображение, приведенный выше код покрывал бы все изображение черным цветом — вы получаете все возможные значения «y» (от 0 до высоты) внутри всех возможных значений x (от 0 до ширины). ) изображения и окрашивание каждого в черный цвет.

Рисование линии потребует от вас одновременного изменения x и y, например:

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

for x, y in zip(range(0, width), range(0, height)):
   picture.setPixel((x,y), Black) )

Это вроде бы сработало, но линия не была бы идеальной, если бы изображение не было идеально квадратным — в противном случае пиксели были бы пропущены в самом широком направлении изображения. Чтобы решить эту проблему, необходим более совершенный алгоритм, но во-вторых, у вас есть реальный способ доступа к пикселям на изображении, например, с использованием библиотеки изображений Python (PIL или Pillow), или pygame, или какой-либо другой библиотеки.

person jsbueno    schedule 23.03.2013
comment
Я использую среду jython для студентов, изображение создается раньше, и да, это очень медленно, чтобы получить все пиксели. Да, я тоже думал, что он будет полностью черным, но он делает толстую черную линию с левой стороны сверху вниз. - person user2194374; 23.03.2013
comment
Фактически, JES (Jython Environmen для студентов) предоставляет вам объект Pixel в качестве возвращаемого значения для getPixel... - person MartinStettner; 23.03.2013
comment
Спасибо, что разъяснили, что @MartinStettner - я бы никогда не догадался, что такое может быть на самом деле. - person jsbueno; 23.03.2013