【问题标题】:How to Concatenate 5 Images Out Of Multiple Images In a Folder如何连接文件夹中多个图像中的 5 个图像
【发布时间】:2021-03-06 22:11:00
【问题描述】:

我有一个包含多个图像的文件夹,可以说图像名称是----

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

这里我的目标是将所有图像连接到 _5 或小于 _5 作为一个图像....

所以基本上我要说的是—— AB_1.jpg, AB_2.jpg, AB_3.jpg, AB_4.jpg, AB_5.jpg

将垂直连接,文件名可以说是 AB_Concat.jpg

_6,_7,_8 被跳过,因为我们只想将图像连接到 _5 为一个....

同样,, BC_1.jpg, BC_2.jpg, BC_3.jpg

将垂直连接,文件名可以说是 BC_Concat.jpg,如您在此处看到的,BC_3 是最后一个小于 _5 的文件,因此所有文件都将垂直连接.....

所以如果有人对如何实现相同的想法有任何想法,请帮助......

这个问题与连接两个图像的一次不相似,因为这里我们是根据图像中下划线后的值来连接图像....

这是我尝试过的东西---


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)
'''

【问题讨论】:

  • 这能回答你的问题吗? Combining Two Images with OpenCV
  • @InyoungKim 不,上面的解决方案对我没有用,因为它绑定了 2 个图像......因为我在一个目录中有多个图像,所以我想将所有图像与一些文件名结合起来,直到 _5作为一个连接的单个图像,其他图像也类似......
  • 这些答案是否有助于解决您的复杂用例?如果是这样,请accept one of the answers。如果没有,请具体跟进,以便解决任何未解决的问题。谢谢

标签: python opencv image-processing concatenation python-imaging-library


【解决方案1】:

您的问题有很多变化的部分,所以我将尝试解决所有这些问题。希望代码对您的用例有所帮助。

下面的代码是这样做的:

  1. 确定目录 X 中存在哪些图像

  2. 根据文件名对图像进行分组

  3. 创建一个临时目录来存储基于组的图像。该目录将被自动删除。

  4. 复制本地文件到临时目录

  5. 将文件分块(最多 5 个文件)

  6. 将这些卡盘合并成一个带有边框分隔符的新图像

特别说明

在测试期间,我注意到任何不包含所需 5 项的列表的格式都不会正确。为了解决这个问题,我创建了一个 blank_check.jpg,它允许我正确地格式化新图像。

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')

合并图像文件示例

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

【讨论】:

    【解决方案2】:

    要迭代文件夹中的所有文件,请使用 Python 的 os.walk 方法。对于每个文件,使用 Python 的 str.split 获取文件的前缀和编号。对5 上方的前缀更改和数字进行一些检查,然后将循环内的图像连接起来。由于您只连接五个图像,因此使用矢量化方法(例如通过 NumPy)这样做只会产生更多不可读的代码,从而获得较小的性能提升。

    这是我的完整代码:

    import cv2
    import numpy as np
    import os
    
    # Iterate all files in folder
    for root, dirs, files in os.walk("your/folder/somewhere/"):
    
        current_prefix = ''
        output = None
    
        for file in files:
    
            # Split filename
            splits = file.split('_')
    
            # Get prefix and number of filename
            prefix = splits[0]
            number = int(splits[1].split('.')[0])
    
            # If there's a prefix change...
            if prefix != current_prefix:
    
                # ... save last output image...
                if current_prefix != '' and output is not None:
                    cv2.imwrite(root + current_prefix + '_Concat.jpg', output)
    
                # ... and set new prefix and initialize new output image
                current_prefix = prefix
                output = cv2.imread(root + file)
    
            # As long the current number is less or equal 5, concatenate
            elif number <= 5:
                output = np.concatenate([output, cv2.imread(root + file)], axis=0)
    
        # Save last output image separately
        cv2.imwrite(root + current_prefix + '_Concat.jpg', output)
    

    我使用包含文件 AB_1.jpg ... AB_8.jpgBC_1.jpg ... BC_3.jpg 的文件夹测试了该代码。结果我得到AB_Concat.jpg 与前五张图像垂直连接,BC_Concat.jpg 与现有三张图像。

    ----------------------------------------
    System information
    ----------------------------------------
    Platform:    Windows-10-10.0.16299-SP0
    Python:      3.8.5
    NumPy:       1.19.4
    OpenCV:      4.4.0
    ----------------------------------------
    

    【讨论】:

    • 出现错误--ValueError: 零维数组无法连接
    • 没有上下文的错误消息,就minimal reproducible example而言,是没有用的。使用我在答案中描述的有效图像的最小示例设置一个文件夹,然后再次测试我的代码。我很确定,它会起作用的。然后,比较那个最小的例子和你的带有图像的实际文件夹之间的不同之处。很可能,并非您文件夹中的所有图像都遵循建议的模式。
    • 我正在测试的图像是.tif图像,具有以下图像细节----水平分辨率200 dpi,垂直分辨率200 dpi,位深1,压缩LZW,分辨率单位2 以上数据效果无论如何你的代码....
    • 我用一些tif 图像重新测试了我的代码,它仍然有效。 1)如果你的实际设置使用tif图像,你为什么要jpg图像? 2) 你还没有提供minimal reproducible example。在你提供之前,我不会在这里进一步支持。也许,cv2.imread 无法读取您的一张图像,因此稍后将其连接起来。但同样,如果没有看到包含一些真实图像的minimal reproducible example,这里没有人可以帮助您。
    • 我在上面添加了一个工作正常的代码,但我遇到的唯一问题是我只想连接带有 _5 的图像,但上面的代码可以说一些图像 _10 变体正在连接所有 _10 张图片...
    【解决方案3】:

    我有 10 分钟的时间来写一些代码 - 之后解释:

    #!/usr/bin/env python3
    
    import glob, re
    import numpy as np
    import cv2
    
    # Get list of filename prefixes by looking for "XX_1.jpg" images
    prefixes = [ re.split('_', f)[0] for f in glob.glob('[A-Z][A-Z]_1.jpg') ]
    
    # Iterate over prefixes forming sequentially numbered filenames
    for prefix in prefixes:
       stack = cv2.imread(f'{prefix}_1.jpg', cv2.IMREAD_COLOR)
       for N in range(2,6):
          # Load image and concatenate onto stack
          im = cv2.imread(f'{prefix}_{N}.jpg', cv2.IMREAD_COLOR)
          stack = np.concatenate((stack, im), axis=0)
       # Save concatenated stack
       cv2.imwrite(f'{prefix}_concat.jpg', stack)
    

    最简单的方法是查找所有 XX_1.jpg 文件以获取文件名的词干(即前缀)。然后构造 5 个编号的版本并列一个列表,然后使用 Numpy np.concat() 将它们堆叠在一起。

    不写所有代码,如下:

    import glob, re
    import numpy as np
    
    # iterate over all "XX_1.jpg" files
    for f in glob.glob('[A-Z][A-Z]_1.jpg'):
        print(f)
        stem = re.split('_', f)[0]
        print(stem)
    

    这将生成所有前缀的列表:

    DE_1.jpg
    DE
    BC_1.jpg
    BC
    AB_1.jpg
    AB
    

    【讨论】:

    • 这里选择文件名至少为 _5 但我想要所有图像直到 _5 或小于 _5....
    • 我的预测:出现错误--ValueError:无法连接零维数组
    • @Ani 如果cv2.imread() 调用之一失败,请退出循环。
    • @HansHirse :-) :-)
    【解决方案4】:
    nums = [1, 2, 3, 4, 5]
    for img in img_dir:
        for i in nums:
            if str(i) in img:
                img = img.replace('i', 'Concat')
    
            
    

    【讨论】:

    • 能否请您详细说明上述如何垂直连接图像......
    • 请通过重命名上述文件名在某些图像上尝试上述解决方案,看看您是否得到与我在问题陈述中要求的相同的输出......
    • @InyoungKim 我在重命名图像名称后尝试了上述方法我没有得到我要求的解决方案......你能测试一下吗......
    【解决方案5】:

    要么有,要么没有。累了,去洗澡。

    #! /usr/bin/env python3
    
    import os
    import cv2
    import numpy as np
    from PIL import Image
    
    path = r'D:\split'
    out = r'D:\concat_test'
    
    for root, _, files in os.walk( path ):
        heads = {}
        for file in files:
            head, tail = file.split( '_', 1 )
            version, extension = tail .split( '.', 1 )
            if head not in heads or version > heads [head] ['vers']:
                heads [head] = { 'vers':version, 'ext':extension }
    
        for h in heads:  ##  AB, BC, CA
            head = heads [h]  ##  local reference
            images = []
            for i in range( head ['vers'] )
                imagename = h +i +head ['ext']
                img = cv2.imread( os.path.join( root, imagename ), 0 )  ##  color, grey, alpha
                images .append( img )
    
            concat = cv2.vconcat( images )
            concat_name = os.path.join( out,  h +'concat' +head ['ext'] )
            cv2.imwrite( concat_name, concat )
    

    【讨论】:

      猜你喜欢
      • 2017-02-22
      • 2021-05-28
      • 2018-09-07
      • 2017-10-07
      • 1970-01-01
      • 2020-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多