Как я могу преобразовать пиксели / numpy-массив в векторные точки? - PullRequest
0 голосов
/ 18 марта 2020

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

Справочная информация: Для моего университетского курса "введение в python" я хочу сделать конструкцию бесстыдная копия "растербатора" (https://rasterbator.net/) с python (но в гораздо более примитивном ключе).

Как я хочу подойти к этому? : Я загружаю изображение с помощью PIL, делаю его в градациях серого и преобразую его в массив numpy. Затем я разрезаю массив на множество маленьких квадратных массивов (по одному сегменту на каждую намеченную точку), вычисляю средние значения для каждого массива и собираю его обратно в один массив, который теперь намного меньше исходного. До этого момента я мог это сделать (но это заняло у меня много времени). Теперь я хочу «заменить» пиксели точками и создать несколько 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, что я хочу напечатать этот большой холст (размером 2 x 1,5 м) на нескольких страницах в общий формат принтера («письмо» или «A4»)?

Ответы [ 2 ]

0 голосов
/ 26 марта 2020

Только для вашей информации, я мог бы перестроить "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")
0 голосов
/ 18 марта 2020

Вместо "нарежьте изображение на маленькие квадраты и рассчитайте среднее значение для каждого квадрата" ... если вы хотите, чтобы 80 точек проходили на 60 точек вниз, просто используйте resize () как это:

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