Комбинация изображений подушки Python

#python #image #numpy #python-imaging-library

#python #изображение #numpy #python-imaging-library

Вопрос:

Я пытаюсь объединить список изображений на странице с заданной высотой, чтобы они перемещались сначала вниз по странице, затем поперек, например:

 Image1 Image4 Image7
Image2 Image5 Image8
Image3 Image6 Image9
  

Максимальное количество столбцов равно 3. Проблема в том, что эти изображения передаются динамически, но все они имеют фиксированную ширину, т.Е. Они могут занимать только 1/2/3 столбца, поэтому может произойти что-то подобное:

 Image1 Image4-Image4
Image2-Image2-Image2
Image3 Image5 Image6
  

Кроме того, высота каждого изображения в моем списке является переменной, что означает, что количество строк не задано. Итак, если изображение должно было превышать страницу или перекрывать другое изображение. это также должно быть сохранено на потом. Допускаются пробелы, например:

 Image1        Image4
Image2-Image2 Image4
Image2-Image2 Image5
  

В приведенном выше примере Image3 занял всю страницу или, возможно, ~ 3/4 страницы, что означает, что он не поместится, поэтому он сохраняется для другой новой страницы.

Как я могу добиться такого метода объединения изображений?

Комментарии:

1. Важен ли порядок, в котором они отображаются на странице? Например, могу ли я сначала вывести все изображения шириной в 3 столбца, затем все изображения шириной в 2 столбца, каждое из которых сопровождается изображением в 1 столбец, и, наконец, строки с тремя изображениями шириной в 1 столбец, пока они не закончатся?

2. @MarkSetchell Нет, порядок важен, спасибо :).

3. Итак, почему изображение-3 разрешено после Image5 в вашем последнем примере, пожалуйста?

4. Кроме того, почему изображение-4 предшествует изображению-2 в вашем последнем примере, потому что изображение-2 поместилось бы перед изображением-4 в этой первой строке?

5. Итак, порядок важен, но поскольку изображение 3 было слишком большим, его можно переместить на другую страницу, но если следующее изображение может поместиться, оно может просто заполнить пустой слот, имеет ли это смысл?

Ответ №1:

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

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

 #!/usr/bin/python3

import sys
import math
import pyvips

column_width = 256
row_height = 256
background_colour = 255 # you could use eg. [128, 255, 128] as well

# pop enough tiles from the argument to fill a page
def layout(tiles):
    # we insert an image (at its top-left), or "x" if a tile is covered by an
    # image up and left of itself
    page = [[None for x in range(3)] for y in range(3)]

    # where we put the next tile
    current_x = 0
    current_y = 0

    # loop to fill page
    page_filled = False
    while not page_filled:
        # used up all the tiles?
        if tiles == []:
            break

        this_tile = tiles[0]
        tiles_across = math.ceil(this_tile.width / column_width)
        tiles_down = math.ceil(this_tile.height / row_height)

        # image too large for page
        if tiles_across > 3 or tiles_down > 3:
                raise Exception(f"tile too large - {this_tile}")

        # loop to find the next free space this tile fits
        while True:
            # too tall for this column?
            if tiles_down > 3 - current_y:
                current_y = 0
                current_x  = 1

            # too wide for this row? 
            if tiles_across > 3 - current_x:
                # we've filled the page
                page_filled = True
                break

            # is this set of tiles clear?
            all_clear = True
            for y in range(tiles_down):
                for x in range(tiles_across):
                    if page[current_y   y][current_x   x]:
                        all_clear = False

            if all_clear:
                break

            # try the next slot down
            current_y  = 1

        # did we find a spot?
        if not page_filled:
            # place the tile here and mark the spaces it covers in the page
            for y in range(tiles_down):
                for x in range(tiles_across):
                    page[current_y   y][current_x   x] = "x"
            page[current_y][current_x] = this_tile
            tiles.pop(0)

    # the page has filled -- draw!
    image = pyvips.Image.black(3 * column_width, 3 * row_height) 
              background_colour
    for y in range(3):
        for x in range(3):
            if isinstance(page[y][x], pyvips.Image):
                image = image.insert(page[y][x], 
                        x * column_width, y * row_height)

    return image

# a source of tiles .. we just load the command-line arguments
all_tiles = [pyvips.Image.new_from_file(filename, access="sequential")
             for filename in sys.argv[1:]]

page_number = 0
while all_tiles != []:
    filename = f"page-{page_number}.jpg"
    print(f"generating {filename} ...")
    page = layout(all_tiles)
    page.write_to_file(filename)
    page_number  = 1
  

Вы можете запустить его следующим образом:

 $ ./layout.py ~/pics/shark.jpg ~/pics/k2.jpg ~/pics/shark.jpg ~/pics/shark.jpg ~/pics/shark.jpg ~/pics/shark.jpg ~/pics/shark.jpg ~/pics/shark.jpg
generating page-0.jpg ...
generating page-1.jpg ...
$
  

Кажется, это работает для меня и реализует все ваши правила (я думаю). Я использовал pyvips для рендеринга страницы, потому что я знаком с этим, но было бы просто заменить его на что-то другое.

Комментарии:

1. Вау, это потрясающе! Именно то, что я искал, большое вам спасибо!

Ответ №2:

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

 convert logo3.jpg lena2.jpg hatching.jpg zelda3.jpg -transpose miff:- |
montage - -geometry  2 2 -tile 2x2 miff:- |
convert - -transpose montage_columns.jpg
  

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

Комментарии:

1. Спасибо за ответ! Я скоро попробую это

Ответ №3:

Если вас не волнует порядок изображений в результате или номера сетки, и вы просто хотите, чтобы они наилучшим образом соответствовали заданному размеру вывода, то в ImageMagick есть новая функция для этого. Смотрите ASHLAR:ouput.png в https://imagemagick.org/script/formats.php#pseudo

 magick lena.jpg barn.jpg mandril3.jpg monet2.jpg zelda1.jpg redhat.jpg -background black -define ashlar:best-fit=true ASHLAR:x.png[600x400 0 0]
  

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

Ответ №4:

Вы можете сделать это в Python как вызов подпроцесса ImageMagick montage следующим образом:

 import subprocess

cmd = '/usr/local/bin/montage lena.jpg house.jpg boats3_small.jpg barn.jpg cameraman.jpg mandril3.jpg monet2.jpg zelda1.jpg redhat.jpg -tile 3x3 -geometry  0 0 -gravity north -background black montage.jpg'

subprocess.call(cmd, shell=True)
  

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

Комментарии:

1. Фред, я не думаю, что это сработает, если некоторые изображения имеют тройную или двойную ширину. Я думаю, что он всегда будет размещать 3 изображения, тогда как он должен просто размещать изображение тройной ширины самостоятельно. И изображение двойной ширины должно быть сопряжено с одним. Я думаю 😉

2. Спасибо за быстрый ответ! Могут ли изображения передаваться по столбцам, а не по строкам? Я вижу, ты положил lena.jpg -> house.jpg -> boat.jpg , она течет горизонтально, но мне нужно, чтобы она текла вертикально.