[python-2.7] Python으로 여러 이미지를 가로로 결합

Python에서 일부 JPEG 이미지를 가로로 결합하려고합니다.

문제

3 개의 이미지가 있습니다. 각각은 148 x 95입니다. 첨부 참조. 방금 같은 이미지를 3 개 복사했습니다. 그래서 같은 이미지입니다.

여기에 이미지 설명 입력여기에 이미지 설명 입력여기에 이미지 설명 입력

내 시도

다음 코드를 사용하여 가로로 조인하려고합니다.

import sys
from PIL import Image

list_im = ['Test1.jpg','Test2.jpg','Test3.jpg']
new_im = Image.new('RGB', (444,95)) #creates a new empty image, RGB mode, and size 444 by 95

for elem in list_im:
    for i in xrange(0,444,95):
        im=Image.open(elem)
        new_im.paste(im, (i,0))
new_im.save('test.jpg')

그러나 이것은 첨부 된 출력을 생성합니다 test.jpg.

여기에 이미지 설명 입력

질문

test.jpg의 하위 이미지에 추가 부분 이미지가 표시되지 않도록 이러한 이미지를 가로로 연결하는 방법이 있습니까?

추가 정보

n 이미지를 가로로 연결하는 방법을 찾고 있습니다. 이 코드를 일반적으로 사용하고 싶습니다.

  • 가능한 경우 이미지 치수를 하드 코딩하지 마십시오.
  • 쉽게 변경할 수 있도록 치수를 한 줄로 지정


답변

다음과 같이 할 수 있습니다.

import sys
from PIL import Image

images = [Image.open(x) for x in ['Test1.jpg', 'Test2.jpg', 'Test3.jpg']]
widths, heights = zip(*(i.size for i in images))

total_width = sum(widths)
max_height = max(heights)

new_im = Image.new('RGB', (total_width, max_height))

x_offset = 0
for im in images:
  new_im.paste(im, (x_offset,0))
  x_offset += im.size[0]

new_im.save('test.jpg')

Test1.jpg

Test1.jpg

Test2.jpg

Test2.jpg

Test3.jpg

Test3.jpg

test.jpg

여기에 이미지 설명 입력


중첩 for i in xrange(0,444,95):은 각 이미지를 95 픽셀 간격으로 5 번 붙여 넣는 것입니다. 이전에 붙여 넣는 각 외부 루프 반복.

for elem in list_im:
  for i in xrange(0,444,95):
    im=Image.open(elem)
    new_im.paste(im, (i,0))
  new_im.save('new_' + elem + '.jpg')

여기에 이미지 설명 입력
여기에 이미지 설명 입력
여기에 이미지 설명 입력


답변

나는 이것을 시도 할 것이다 :

import numpy as np
import PIL
from PIL import Image

list_im = ['Test1.jpg', 'Test2.jpg', 'Test3.jpg']
imgs    = [ PIL.Image.open(i) for i in list_im ]
# pick the image which is the smallest, and resize the others to match it (can be arbitrary image shape here)
min_shape = sorted( [(np.sum(i.size), i.size ) for i in imgs])[0][1]
imgs_comb = np.hstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )

# save that beautiful picture
imgs_comb = PIL.Image.fromarray( imgs_comb)
imgs_comb.save( 'Trifecta.jpg' )

# for a vertical stacking it is simple: use vstack
imgs_comb = np.vstack( (np.asarray( i.resize(min_shape) ) for i in imgs ) )
imgs_comb = PIL.Image.fromarray( imgs_comb)
imgs_comb.save( 'Trifecta_vertical.jpg' )

모든 이미지가 동일한 다양성 (모든 RGB, 모든 RGBA 또는 모든 회색조)이면 작동합니다. 몇 줄의 코드를 추가하면 이것이 사실인지 확인하는 것이 어렵지 않습니다. 다음은 내 예제 이미지와 결과입니다.

Test1.jpg

Test1.jpg

Test2.jpg

Test2.jpg

Test3.jpg

Test3.jpg

Trifecta.jpg :

결합 된 이미지

Trifecta_vertical.jpg

여기에 이미지 설명 입력


답변

편집 : DTing의 답변은 PIL을 사용하기 때문에 귀하의 질문에 더 적합하지만 numpy에서 수행하는 방법을 알고 싶다면 이것을 남겨 두겠습니다.

다음은 모든 크기 / 모양의 N 개 이미지 (컬러 이미지 만)에 대해 작동해야하는 numpy / matplotlib 솔루션입니다.

import numpy as np
import matplotlib.pyplot as plt

def concat_images(imga, imgb):
    """
    Combines two color image ndarrays side-by-side.
    """
    ha,wa = imga.shape[:2]
    hb,wb = imgb.shape[:2]
    max_height = np.max([ha, hb])
    total_width = wa+wb
    new_img = np.zeros(shape=(max_height, total_width, 3))
    new_img[:ha,:wa]=imga
    new_img[:hb,wa:wa+wb]=imgb
    return new_img

def concat_n_images(image_path_list):
    """
    Combines N color images from a list of image paths.
    """
    output = None
    for i, img_path in enumerate(image_path_list):
        img = plt.imread(img_path)[:,:,:3]
        if i==0:
            output = img
        else:
            output = concat_images(output, img)
    return output

다음은 사용 예입니다.

>>> images = ["ronda.jpeg", "rhod.jpeg", "ronda.jpeg", "rhod.jpeg"]
>>> output = concat_n_images(images)
>>> import matplotlib.pyplot as plt
>>> plt.imshow(output)
>>> plt.show()

여기에 이미지 설명 입력


답변

DTing의 답변에 따라 사용하기 더 쉬운 함수를 만들었습니다.

from PIL import Image


def append_images(images, direction='horizontal',
                  bg_color=(255,255,255), aligment='center'):
    """
    Appends images in horizontal/vertical direction.

    Args:
        images: List of PIL images
        direction: direction of concatenation, 'horizontal' or 'vertical'
        bg_color: Background color (default: white)
        aligment: alignment mode if images need padding;
           'left', 'right', 'top', 'bottom', or 'center'

    Returns:
        Concatenated image as a new PIL image object.
    """
    widths, heights = zip(*(i.size for i in images))

    if direction=='horizontal':
        new_width = sum(widths)
        new_height = max(heights)
    else:
        new_width = max(widths)
        new_height = sum(heights)

    new_im = Image.new('RGB', (new_width, new_height), color=bg_color)


    offset = 0
    for im in images:
        if direction=='horizontal':
            y = 0
            if aligment == 'center':
                y = int((new_height - im.size[1])/2)
            elif aligment == 'bottom':
                y = new_height - im.size[1]
            new_im.paste(im, (offset, y))
            offset += im.size[0]
        else:
            x = 0
            if aligment == 'center':
                x = int((new_width - im.size[0])/2)
            elif aligment == 'right':
                x = new_width - im.size[0]
            new_im.paste(im, (x, offset))
            offset += im.size[1]

    return new_im

배경색과 이미지 정렬을 선택할 수 있습니다. 재귀를 수행하는 것도 쉽습니다.

images = map(Image.open, ['hummingbird.jpg', 'tiger.jpg', 'monarch.png'])

combo_1 = append_images(images, direction='horizontal')
combo_2 = append_images(images, direction='horizontal', aligment='top',
                        bg_color=(220, 140, 60))
combo_3 = append_images([combo_1, combo_2], direction='vertical')
combo_3.save('combo_3.png')

연결된 이미지의 예


답변

다음은 이전 접근 방식을 일반화하여 PIL에서 이미지 그리드를 만드는 함수입니다.

from PIL import Image
import numpy as np

def pil_grid(images, max_horiz=np.iinfo(int).max):
    n_images = len(images)
    n_horiz = min(n_images, max_horiz)
    h_sizes, v_sizes = [0] * n_horiz, [0] * (n_images // n_horiz)
    for i, im in enumerate(images):
        h, v = i % n_horiz, i // n_horiz
        h_sizes[h] = max(h_sizes[h], im.size[0])
        v_sizes[v] = max(v_sizes[v], im.size[1])
    h_sizes, v_sizes = np.cumsum([0] + h_sizes), np.cumsum([0] + v_sizes)
    im_grid = Image.new('RGB', (h_sizes[-1], v_sizes[-1]), color='white')
    for i, im in enumerate(images):
        im_grid.paste(im, (h_sizes[i % n_horiz], v_sizes[i // n_horiz]))
    return im_grid

그리드의 각 행과 열을 최소로 축소합니다. pil_grid (images)를 사용하여 행만 포함하거나 pil_grid (images, 1)를 사용하여 열만 가질 수 있습니다.

numpy-array 기반 솔루션보다 PIL을 사용하는 한 가지 이점은 다르게 구조화 된 이미지 (예 : 회색조 또는 팔레트 기반 이미지)를 처리 할 수 ​​있다는 것입니다.

예제 출력

def dummy(w, h):
    "Produces a dummy PIL image of given dimensions"
    from PIL import ImageDraw
    im = Image.new('RGB', (w, h), color=tuple((np.random.rand(3) * 255).astype(np.uint8)))
    draw = ImageDraw.Draw(im)
    points = [(i, j) for i in (0, im.size[0]) for j in (0, im.size[1])]
    for i in range(len(points) - 1):
        for j in range(i+1, len(points)):
            draw.line(points[i] + points[j], fill='black', width=2)
    return im

dummy_images = [dummy(20 + np.random.randint(30), 20 + np.random.randint(30)) for _ in range(10)]

pil_grid(dummy_images):

line.png

pil_grid(dummy_images, 3):

여기에 이미지 설명 입력

pil_grid(dummy_images, 1):

여기에 이미지 설명 입력


답변

모든 이미지의 높이가 같으면

imgs = [‘a.jpg’, b.jpg’, c.jpg’]
concatenated = Image.fromarray(
  np.concatenate(
    [np.array(Image.open(x)) for x in imgs],
    axis=1
  )
)

이렇게 연결하기 전에 이미지 크기를 조정할 수 있습니다.

imgs = [‘a.jpg’, b.jpg’, c.jpg’]
concatenated = Image.fromarray(
  np.concatenate(
    [np.array(Image.open(x).resize((640,480)) for x in imgs],
    axis=1
  )
)


답변

내 해결책은 다음과 같습니다.

from PIL import Image


def join_images(*rows, bg_color=(0, 0, 0, 0), alignment=(0.5, 0.5)):
    rows = [
        [image.convert('RGBA') for image in row]
        for row
        in rows
    ]

    heights = [
        max(image.height for image in row)
        for row
        in rows
    ]

    widths = [
        max(image.width for image in column)
        for column
        in zip(*rows)
    ]

    tmp = Image.new(
        'RGBA',
        size=(sum(widths), sum(heights)),
        color=bg_color
    )

    for i, row in enumerate(rows):
        for j, image in enumerate(row):
            y = sum(heights[:i]) + int((heights[i] - image.height) * alignment[1])
            x = sum(widths[:j]) + int((widths[j] - image.width) * alignment[0])
            tmp.paste(image, (x, y))

    return tmp


def join_images_horizontally(*row, bg_color=(0, 0, 0), alignment=(0.5, 0.5)):
    return join_images(
        row,
        bg_color=bg_color,
        alignment=alignment
    )


def join_images_vertically(*column, bg_color=(0, 0, 0), alignment=(0.5, 0.5)):
    return join_images(
        *[[image] for image in column],
        bg_color=bg_color,
        alignment=alignment
    )

이 이미지의 경우 :

images = [
    [Image.open('banana.png'), Image.open('apple.png')],
    [Image.open('lime.png'), Image.open('lemon.png')],
]

결과는 다음과 같습니다.


join_images(
    *images,
    bg_color='green',
    alignment=(0.5, 0.5)
).show()

여기에 이미지 설명 입력


join_images(
    *images,
    bg_color='green',
    alignment=(0, 0)

).show()

여기에 이미지 설명 입력


join_images(
    *images,
    bg_color='green',
    alignment=(1, 1)
).show()

여기에 이미지 설명 입력