如何将 pixels/numpy-array 转换为矢量点?

How do I convert pixels/numpy-array into vector-dots?

我想把一张灰度图的像素值转换成矢量点,让一个像素的灰度值决定对应点的半径。 但是我完全卡住了,这个项目必须在星期天完成,我现在真的很绝望

背景: 对于我的大学课程 "introduction to python",我想用 "rasterbator" (https://rasterbator.net/) 构建一个无耻的副本 python(但以更原始的方式)。

我想如何解决这个问题?: 我使用 PIL 加载图像,将其设为灰度并将其转换为 numpy 数组。然后我将数组切成许多小方形数组(每个预期的点一个片段),计算每个数组的平均值并将其放回到一个数组中,该数组现在比原始数组小得多。到那时,我能够做到(但花了我很长时间)。 现在我想 "replace" 带点的像素并创建几个 PDF,这样你就可以打印它,将它粘在一起并制作一张大海报。

这种方法行得通吗?还是我找错树了?

我是个该死的 python 初学者。 python 的问题是对我来说,有太多我不知道的模块。答案可能很简单,但我根本不知道去哪里找。如果有人能告诉我,我的方向是否正确或指出正确的方向,我将不胜感激。

非常感谢

这里是我到目前为止所管理的代码(虽然不多)

from PIL import Image as img
import numpy as np

greyscale = np.asarray(img.open("test.jpg").convert("L")) #load picture into array and make it greyscale

end_width = 1500 # chosen width of final picture in mm (will be with kwargs later on)
dot_size = 13 #chosen dot-size of final pictutre in mm (will be with kwargs later on)
estimate_dot_count_x = int(np.ceil(end_width/dot_size)) # estimates the "horizontal resolution"

pixel_in_segment = int(np.ceil(greyscale.shape[1]/estimate_dot_count_x)) #calculates the edge length of a segment
W=pixel_in_segment #just for shorter formular later on 

estimate_dot_count_y = int(np.ceil(greyscale.shape[0]/pixel_in_segment)) # estimates the "vertical resolution"
final_dot_count_x=int(np.ceil(greyscale.shape[1]/W)) #final horizontal resolution for shape of new array
final_dot_count_y=int(np.ceil(greyscale.shape[0]/W)) #final vertical resolution for shape of new array
#slice array into multiple pieces
tiles = [greyscale[x:x+W,y:y+W] for x in range(0,greyscale.shape[0],W) for y in range(0,greyscale.shape[1],W)]
#calculate mean values of each segment an safe it to list
average_list = []
for pixel in tiles:
    result=int(np.mean(pixel))
    average_list.append(result)
#convert list back into an array
downscale=np.asarray(average_list, dtype=int).reshape(final_dot_count_y,final_dot_count_x)

编辑: 不知何故,我设法将数组绘制成矢量点:

#inverse and normalize gray value so That I can multiply with max dot size
for ix,iy in np.ndindex(downscale.shape):
    downscale[ix,iy]= float(1-downscale[ix,iy]*(1/255))

reportlab 是我一直在寻找的关键...

from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
#making dots
def printing(c):
    c.translate(spacing*0.5,imh-(spacing*0.5))
    for ix,iy in np.ndindex(downscale.shape):
       c.circle(iy*(spacing), ix*(-spacing), downscale[ix, iy]*max_dot_size, stroke=1, fill=1)
c = canvas.Canvas("hello.pdf", pagesize=(imwidth, imhight))
printing(c)
c.showPage()
c.save()

这就提出了一个问题: 我如何告诉 reportlab,我想以通用打印机格式('letter' 或 'A4')将这个大 canvas(尺寸大约为 2m x1.5m)打印到多个页面?

而不是 "slice the image up into little squares and calculate the mean of each square"...如果您想要 80 点横宽 60 点向下,只需使用 resize(),如下所示:

im_resized = im.resize((80, 60))

仅供参考,我可以重建 "Rasterbator" 代码可能有点乱,缺少错误处理,但我工作得很好,我 运行 没时间了。所以这就是我上传的内容。 抱歉,有些变量是德语的。我倾向于混合语言。必须改变它。

需要报告实验室模块

from PIL import Image as img
import numpy as np
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from math import sqrt


#load image to array and make it greyscale
input_file = input("Please enter the image file you want do convert: ")
greyscale = np.asarray(img.open(input_file).convert("L"))

print("\n"+"Image resolution is " + str(greyscale.shape[1]) + "x" + str(greyscale.shape[0]))
#defining width of poster
print("\n"+"please enter the target width of your poster")
print("remember, the dimensions of an A4 sheet is: 210mm x 297mm ")
end_width= int(input("target poster width in mm: "))
#defining grid size of poster
print('\n'+'The distance between 2 Points in the grid. Choose the grid size wisely in relation to the size of your poster '+'\n'+'recommended size is 7-12mm')
print('please notice, that the maximum dot size is higher than the grid size (factor 1.4) to allow pure black coverage')
grid_size = int(input("Please enter the target grid size in mm: "))
#select orientation
print("your sheets can be arranged in portrait or landscape orientation")
print_format = input("Please enter p for portrait or l for landscape :")

if print_format=="l":
    height, width = A4 #Landscape
elif print_format=="p":
    width, height = A4 #Portrait
else:
    print("-invalid input-  continuing with default (portrait)")
    width, height = A4 #Portrait



# calculates the "x-resolution" as a base for further calculations
estimate_dot_count_x = int(np.ceil(end_width/grid_size)) 

#calculates the size of a segment in array
pixel_in_segment = int(np.ceil(greyscale.shape[1]/estimate_dot_count_x))
W=pixel_in_segment #obsolete, just for shorter formulars later on

#final horizontal resolution for shape of new array
final_dot_count_x=int(np.ceil(greyscale.shape[1]/W))
#final vertical resolution for shape of new array
final_dot_count_y=int(np.ceil(greyscale.shape[0]/W))
#slice array into multiple pieces
tiles = [greyscale[x:x+W,y:y+W] for x in range(0,greyscale.shape[0],W) for y in range(0,greyscale.shape[1],W)]

#calculate mean values of each segment an safe it to list
average_list = []
for pixel in tiles:
    result=int(np.mean(pixel))
    average_list.append(result)

#convert list back into an array 
downscale=np.asarray(average_list, dtype=float).reshape(final_dot_count_y,final_dot_count_x)

print('\n'+'downscaling picture...')

#prepare data to work in point scale
spacing=grid_size*mm
#calculating final poster size
imw=downscale.shape[1]*spacing
imh=downscale.shape[0]*spacing
#scaling dots to allow complete coverage with black for very dark areas
max_dot_size=spacing*sqrt(2)/2

#inverse and normalize pixel value
for ix,iy in np.ndindex(downscale.shape):
    downscale[ix,iy]= float(1-downscale[ix,iy]*(1/255))


print('\n'+'printing image to pdf...')
#calculate numer of A4 sheets required for printing
pages_w = int(np.ceil(imw/width))
pages_h = int(np.ceil(imh/height))
#stuff for showing progress while printing
seitenzahl=0
gesamtseitenzahl = pages_w*pages_h


def printing(c):
    #auxillary variables for iterating over poster
    left=width*x
    top=height*y
    #iterate instructions
    c.translate(-left+spacing*0.5,imh-top+(spacing*0.5))
    #drawing the circles
    for ix,iy in np.ndindex(downscale.shape):
        c.circle(iy*(spacing), ix*(-spacing), downscale[ix, iy]*max_dot_size, stroke=1, fill=1)

#setting canvas properties
c = canvas.Canvas(str(input_file)+".pdf", pagesize=(width, height))
#make pages
for x in range(pages_w):
    for y in range(pages_h):
        #progress documentation
        seitenzahl = seitenzahl+1
        #call printing function
        printing(c)
        #progress documentation
        print("printing page " + str(seitenzahl)+ " of " + str(gesamtseitenzahl))
        c.showPage()

#save to disk        
print('\n'+'save to disk...')
c.save()
print('...PDF successfully saved as ' + str(input_file)+".pdf")