Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
215 views
in Technique[技术] by (71.8m points)

python - How to Concatenate 5 Images Out Of Multiple Images In a Folder

I have a folder which has multiple images lets say images names are----

AB_1.jpg, AB_2.jpg, AB_3.jpg, AB_4.jpg, AB_5.jpg, AB_6.jpg, AB_7.jpg, AB_8.jpg, BC_1.jpg, BC_2.jpg, BC_3.jpg, CA_1.jpg, CA_2.jpg

Here my objective is to concatenate all the images till _5 or less than _5 as one image....

So basically what I am saying is that--- AB_1.jpg, AB_2.jpg, AB_3.jpg, AB_4.jpg, AB_5.jpg

will get concatenated vertically and file name lets say will be AB_Concat.jpg

_6,_7,_8 are skipped because we want to concatenate only images till _5 as one....

Similarly,, BC_1.jpg, BC_2.jpg, BC_3.jpg

will get concatenated vertically and file name lets say will be BC_Concat.jpg as you can see here BC_3 is last one which is less than _5 so all will be concatenated vertically.....

So if anyone have any idea on how to achieve the same please help....

This question is not similar to the once where two images are concatenated since here we are concatenating images on the basis of value after underscore in a image....

This is something which I tried---


import cv2

import shutil

import numpy as np   
        
import os   

from PIL import Image  
     
path = r"D:split"

out = r"D:concat_test"

os.makedirs(out,exist_ok=True)

imag_name =[]

imag_name1 = []

for img in os.listdir(path):

    folder  = img.rsplit("_",1)[0]  

    imag_name1.append((folder,img))

    images = cv2.imread(os.path.join(path,img),0)

    imag_name.append((folder,images))

# to create a folder for multiple images contain same name  
   
for image,name in zip(imag_name,imag_name1):

    if image[0] == name[0]:

        folder_n = os.path.join(out,name[0])

        final_fldr = os.path.join(folder_n,name[1])

        os.makedirs(folder_n,exist_ok=True)

        cv2.imwrite(final_fldr,image[1])

# to concatinate the images

for root,dirs,files in os.walk(out):

    np_images = [cv2.imread(os.path.join(root,file),0) for file in files]

    np_name = list(set([file.rsplit("_",1)[0] for file in files]))
    

    if len(np_images) != 0:

        print(np_name)

        concat = cv2.vconcat(np_images)

        concatinated_images = os.path.join(out,"concatinated_images")

        os.makedirs(concatinated_images,exist_ok=True)

        cv2.imwrite(os.path.join(concatinated_images,*np_name)+".tif",concat)

#to remove the unwanted folder

for remove_folder in os.listdir(out):

    if remove_folder != "concatinated_images":

        folders_to_remove = os.path.join(out,remove_folder)

        shutil.rmtree(folders_to_remove)
'''
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Your question has lots of moving parts, so I will attempt to address all of them. Hopefully the code helps with your use case.

The code below does this:

  1. Determine what images exist in directory X

  2. Group images based on file name

  3. Create a temporary directory to store images based on group. This directory will be removed automatically.

  4. Copy local files to temporary directory

  5. Chunk the files into blocks (5 files max)

  6. Merge these chucks into a new image with a border separator

Special Note

During testing I noted that any list that did not contained the required 5 items would not format correctly. To fix this problem I created a blank_check.jpg, which allowed me to format the new image correctly.

import os
import shutil
import tempfile
import itertools
from PIL import Image
from PIL import ImageOps

def create_temp_directory(name):
    """
    This function create a temporary directory, which will
    be used to store files based on specific parts of a filename.
    :param name:
    :return:
    """
    temp_dir = tempfile.TemporaryDirectory(suffix=None, prefix=f'temp_directory_{name}', dir=None)
    return temp_dir


def divide_list_into_chunks(input_list, len_of_chunk):
    """
    This function will divide a list into chunks.
    :param input_list:
    :param len_of_chunk:
    :return:
    """
    for i in range(0, len(input_list), len_of_chunk):
       yield input_list[i:i + len_of_chunk]
 

def add_blank_checks(input_list, temp_dir):
    """
   :param input_list: 
   :param temp_dir: 
   :return: 
   """
   number_of_images = (5 - len(input_list))
   for _ in range(number_of_images):
      shutil.copy(f'blank_check.jpg', f'{temp_dir.name}/blank_check.jpg')
      input_list.append(f'{temp_dir.name}/blank_check.jpg')
   return input_list


def merge_images_vertically(list_of_images, file_name):
    """
    This function is designed to merge images vertically
    :param list_of_images: current list of images to process
    :param file_name: file name for the new image
    :return: 
    """
    # open images using Pillow
    images = [Image.open(im) for im in list_of_images]
    # Create separate lists to store the heights and widths
    # of the images
    widths, heights = zip(*(i.size for i in images))
    width_of_new_image = min(widths)
    height_of_new_image = sum(heights)
    # create a new output image
    new_im = Image.new('RGB', (width_of_new_image, height_of_new_image))
    new_pos = 0
    counter = 0
    for im in images:
       if counter == 0:
          new_im.paste(im, (0, new_pos))
          new_pos += im.size[1]
          counter += 1
       else:
          color = "black"
          border = (0, 10, 0, 0)
          img_with_border = ImageOps.expand(im, border=border, fill=color)
          new_im.paste(img_with_border, (0, new_pos))
          new_pos += im.size[1]
     new_im.save(f'{file_name}', "JPEG", quality=75, optimize=True, progressive=True)
     return


image_directory = "sample_images"
image_directory_abspath = os.path.abspath(image_directory)
images = os.listdir(image_directory_abspath)
accepted_extensions = ('.bmp', '.gif', '.jpg', '.jpeg', '.png', '.svg', '.tiff')
valid_image_extensions = [im for im in images if im.endswith(accepted_extensions)]
image_groups = [list(g) for _, g in itertools.groupby(sorted(valid_image_extensions), lambda x: x[0:2])]
for image_group in image_groups:
   count = 0
   name_group = image_group[0][:2]
   temp_directory = create_temp_directory(name_group)
   image_list = [shutil.copy(f'{image_directory_abspath}/{item}', f'{temp_directory.name}/{item}') for item in image_group]
   max_number_of_items = 5
   chunks = list(divide_list_into_chunks(image_list, max_number_of_items))
   for chunk in chunks:
      count += 1
      if len(chunk) == 5:
        merge_images_vertically(chunk, f'{name_group}_merged_{count}.jpeg')
      else:
        new_checks = add_blank_checks(chunk, temp_directory)
        merge_images_vertically(new_checks, f'{name_group}_merged_{count}.jpeg')

Sample of merged image files

enter image description here

----------------------------------------
System information
----------------------------------------
Platform:    macOS Catalina
Python:      3.8.0
Pillow:      8.0.1
----------------------------------------

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...