在图像上绘制对角线

2 投票
3 回答
14924 浏览
提问于 2025-04-17 20:03

你好,我正在尝试在一张图片上画从右上角到左下角的对角线,这是我目前的代码。

  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)

谢谢!

3 个回答

1

你的 picture 对象是从哪里来的?它是什么?到目前为止有什么不工作的吗?你想用哪个库来访问图像?(我想知道你是从哪里获取“getWidth, getHeight, getPixel, setColor”这些方法的?)

我觉得没有哪个库能提供一个完整的“像素”对象来用于 setColor 调用,如果有的话,那可能是世界上最慢的东西——也许在整个银河系里都是。

另一方面,如果这些方法真的存在,你的 Picture 代码会把整张图像涂成黑色——你会获取所有可能的“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,或者其他一些库。

2

我想在讨论中加入一些数学方面的考虑...

(只是因为很遗憾,JES的addLine函数只能画黑色的线,而且功能相当有限...)

注意:下面的代码使用了Bresenham的线算法,这是MartinStettner提到的(感谢他)。

Bresenham的线算法是一种算法,用来确定如何在两个给定点之间形成一条接近直线的路径。因为在计算机屏幕上,像素是最小的单位,所以画一条线只能通过某种方式来近似实现。

注意:要理解下面的代码,你需要记住一些基础的学校数学知识(直线方程和三角函数)。

代码:

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


输出(画作来自Pierre Soulages):


在这里输入图片描述

在这里输入图片描述

在这里输入图片描述


希望这能给JES的学生们带来一些乐趣和灵感... 也希望对其他人有帮助...

4

大多数图形库都有直接画线的方法。

JES中,有一个叫addLine的函数,你可以这样使用:

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

如果你只能设置单个像素,那你可以看看Bresenham线算法,这是画线最有效的算法之一。

关于你的代码:你用两个嵌套循环做的事情是:

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)

这样你就能得到像这样的图像(这个例子是手绘的...)

diagonal lines

这利用了大多数图形库提供的裁剪功能,也就是说,线条中不在图像范围内的部分会被忽略。注意,如果没有2*width(也就是如果x只到width),那么只会画出线条的左上半部分...

撰写回答