基于文件名指定行列拼接JPEG图片的代码报错排查
按文件名指定行列拼接JPEG图片的问题排查与解决
需求背景
我有一批JPEG图片,希望根据文件名指定的行列位置进行拼接。文件名规则是行_列.jpeg(例如7_1.jpeg对应第7行第1列,15_5.jpeg对应第15行第5列),文件名范围覆盖1_1.jpeg至40_20.jpeg,需支持任意数量图片拼接,最终允许存在空白单元格。
已尝试的代码与问题
基础拼接实现(已成功)
首先实现了纵向拼接的基础代码:
import cv2 import numpy import glob import os dir = "." # current directory ext = ".jpg" # whatever extension you want pathname = os.path.join(dir, "*" + ext) images = [cv2.imread(img) for img in glob.glob(pathname)] height = sum(image.shape[0] for image in images) width = max(image.shape[1] for image in images) output = numpy.zeros((height,width,3)) y = 0 for image in images: h,w,d = image.shape output[y:y+h,0:w] = image y += h cv2.imwrite("test.jpg", output)
尝试按行列定位拼接(出现错误)
接着尝试根据文件名提取行列位置来拼接,代码如下:
import cv2 import numpy import glob import os import re # 补充原代码缺失的导入 dir = "." ext = ".jpeg" pathname = os.path.join(dir, "*" + ext) images = [cv2.imread(img) for img in glob.glob(pathname)] names = [img for img in glob.glob(pathname)] #Get filenames from paths files = [re.sub("/.*/", "", x) for x in names] #Get positions position = [re.sub(".jpeg", "", x) for x in files] #Get hight position and tranform to int height = [re.sub("_.*", "", x) for x in position] height2 = [int(i) for i in height] #Get width position and tranform to int width = [re.sub(".*_", "", x) for x in position] width2 = [int(i) for i in width] #Generate background image height_tot = 280*max(height2) #All images are 280*280 width_tot = 280*max(width2) output = numpy.zeros((height_tot,width_tot,3)) #Locate images for name, image in zip(names,images): files = re.sub("/.*/", "", name) position = re.sub(".jpeg", "", files) height = re.sub("_.*", "", position) height2 = int(height)*280 width = re.sub(".*_", "", position) width2 = int(width)*280 output[height2:height2+280,width2:width2+280] = image cv2.imwrite("test.jpg", output)
运行时抛出错误:
ValueError Traceback (most recent call last) <ipython-input-70-529c37a03571> in <module>() 6 width = re.sub(".*_", "", position) 7 width2 = int(width)*280 ----> 8 output[height2:height2+280,width2:width2+280] = image 9 # print(height2) 10 cv2.imwrite("test.jpg", output) ValueError: could not broadcast input array from shape (280,280,3) into shape (0,280,3)
修改后仍有问题
将280改为图片实际高度262后无报错,但图片排列混乱且存在重复粘贴,修改后的循环代码:
for name, image in zip(names,images): h,w,d = image.shape files = re.sub("/.*/", "", name) position = re.sub(".jpeg", "", files) height = re.sub("_.*", "", position) height2 = int(height)*262 width = re.sub(".*_", "", position) width2 = int(width)*262 output[height2:height2+h,width2:width2+w,:] = image cv2.imwrite("test.jpg", output)
问题原因分析
初始错误的核心原因:索引越界与行列起始值错误
- 文件名中的行号是从1开始的(比如
1_1.jpeg是第1行),但你直接用int(height)*280作为起始位置,会导致第1行的起始位置是280而非0。当行号等于max(height2)时,height2+280就会超过output的高度(因为output高度是280*max(height2)),从而出现切片起始位置大于等于结束位置的shape (0,280,3)错误。
- 文件名中的行号是从1开始的(比如
排列混乱与重复的原因
- 列号同样从1开始,直接乘以图片宽度会导致第一列起始位置偏移,后续列的位置全部错位;
glob.glob返回的文件顺序不确定,可能导致图片被乱序处理;- 循环内重复处理文件名的逻辑冗余,且未做位置映射校验,可能导致同一区域被多次赋值。
修正后的完整代码
import cv2 import numpy as np import glob import os def main(): dir_path = "." # 当前目录 ext = ".jpeg" # 图片扩展名 path_pattern = os.path.join(dir_path, f"*{ext}") # 获取所有图片路径并按文件名排序,保证处理顺序稳定 image_paths = sorted(glob.glob(path_pattern)) # 提取图片行列信息、记录尺寸,同时更新最大行/列数 img_info = [] max_row = 0 max_col = 0 img_h, img_w = None, None for path in image_paths: # 提取纯文件名(去掉路径和扩展名) filename = os.path.basename(path) row_str, col_str = os.path.splitext(filename)[0].split("_") row = int(row_str) col = int(col_str) # 读取图片并校验 img = cv2.imread(path) if img is None: print(f"警告:无法读取图片 {path}") continue # 记录图片尺寸(默认所有图片尺寸一致) if img_h is None: img_h, img_w = img.shape[:2] img_info.append((row, col, img)) # 更新最大行/列数 max_row = max(max_row, row) max_col = max(max_col, col) # 创建输出画布(匹配图片的uint8数据类型) canvas_h = max_row * img_h canvas_w = max_col * img_w output = np.zeros((canvas_h, canvas_w, 3), dtype=np.uint8) # 将图片放到对应位置 for row, col, img in img_info: # 转换为画布的起始坐标:行/列号从1→0偏移 start_y = (row - 1) * img_h start_x = (col - 1) * img_w output[start_y:start_y+img_h, start_x:start_x+img_w] = img # 保存结果 cv2.imwrite("final拼接图.jpg", output) print("拼接完成,结果已保存为final拼接图.jpg") if __name__ == "__main__": main()
关键修正点说明
- 行列索引转换:将文件名中从1开始的行/列号,转换为画布上从0开始的起始坐标(
(row-1)*img_h),彻底解决越界和位置偏移问题; - 稳定排序:使用
sorted()对图片路径排序,确保处理顺序稳定; - 类型匹配:创建
output时指定dtype=np.uint8,和OpenCV读取的图片格式一致,避免潜在类型错误; - 简化逻辑:统一提取图片信息到列表,避免循环内重复处理文件名,代码更清晰易维护;
- 错误处理:增加图片读取失败的警告,提升代码健壮性。
内容的提问来源于stack exchange,提问作者Tato14
相关产品推荐
相关产品推荐

