【目标检测】图像裁剪/标签可视化/图像拼接处理脚本

前言

无人机拍摄的图像分辨率往往较大,做目标检测时,需要进行裁剪再标注。
本文就来记录从图像裁剪到图像拼接的处理脚本思路。

图像裁剪

图像编码规则设定

因为后面需要将标注好的图片进行融合拼接,因此需要对图片方位进行编码,我这里直接将图片裁剪时左上角的宽高坐标写在文件名中,裁剪结果如下图所示:

在这里插入图片描述

裁剪脚本

我这里以每张小图为1280x1280进行裁剪,同时记录原图宽高信息,写入yaml文件,后续拼接时会用到。

import shutil
from pathlib import Path

import yaml
from PIL import Image
import os.path

from tqdm import tqdm

rootdir = r'D:\Data\i1'  # 原始图片文件夹
savedir = r'D:\Data\i2'  # 保存图片文件夹
ConfigPath = "../config.yaml"  # 配置文件(记录图片尺寸)
dis = 1280
leap = 1280

if __name__ == '__main__':

    # 创建输出文件夹
    if Path(savedir).exists():
        shutil.rmtree(savedir)
    os.mkdir(savedir)

    for parent, dirnames, filenames in os.walk(rootdir):  # 遍历每一张图片
        filenames.sort()
        for filename in tqdm(filenames):
            currentPath = os.path.join(parent, filename)
            suffix = currentPath.split('.')[-1]
            if suffix == 'jpg' or suffix == 'png' or suffix == 'JPG' or suffix == 'PNG':
                img = Image.open(currentPath)
                width = img.size[0]
                height = img.size[1]
                i = j = 0
                for i in range(0, width, leap):
                    for j in range(0, height, leap):
                        box = (i, j, i+dis, j+dis)
                        image = img.crop(box)  # 图像裁剪
                        image.save(savedir + '/' + filename.split(".")[0] + "__" + str(i) + "__" + str(j) + ".jpg")

    # 将图片长宽写入配置文件
    pic_context = {
                    'width': width,
                    'height': height
                    }
    with open(ConfigPath, "w", encoding="utf-8") as f:
        yaml.dump(pic_context, f)

标签可视化

标签可视化在我之前的博文有写到过,这里又进行了一些优化,可以将txt格式的YOLO标签映射回裁剪之后的小图,并进行中文标签显示,下面根据是否需要舍弃无目标的图片,分成两个版本。

小图标签可视化:舍弃无目标的图片

'''
直接在小图上还原标签(舍弃无目标图片)
'''
import os
import random
import shutil
from pathlib import Path
import numpy as np
import cv2
from PIL import ImageDraw, ImageFont
from PIL import Image
from tqdm import tqdm

input_folder = "D:/Data/i2"
output_folder = "D:/Data/i3"
input_list = os.listdir(input_folder)
input_list.sort()

labels = ['类别一', '类别二']
# colormap = [(0, 255, 0), (132, 112, 255), (255, 255, 255)]  # 色盘,可根据类别添加新颜色
colors = [[random.randint(0, 255) for _ in range(3)] for _ in labels]


def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
    # 图像从OpenCV格式转换成PIL格式
    if isinstance(img, np.ndarray):  # 判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontText = ImageFont.truetype("Font/simhei.ttf", textSize, encoding="utf-8")
    draw.text((left, top - 2), text, textColor, font=fontText)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)


# 坐标转换
def xywh2xyxy(x, w1, h1, img):
    label, x, y, w, h = x
    label = int(label)
    # print("原图宽高:\nw1={}\nh1={}".format(w1, h1))
    # 边界框反归一化
    x_t = x * w1
    y_t = y * h1
    w_t = w * w1
    h_t = h * h1
    # print("反归一化后输出:\n第一个:{}\t第二个:{}\t第三个:{}\t第四个:{}\t\n\n".format(x_t, y_t, w_t, h_t))
    # 计算坐标
    top_left_x = x_t - w_t / 2
    top_left_y = y_t - h_t / 2
    bottom_right_x = x_t + w_t / 2
    bottom_right_y = y_t + h_t / 2
    # print('标签:{}'.format(labels[int(label)]))
    # print("左上x坐标:{}".format(top_left_x))
    # print("左上y坐标:{}".format(top_left_y))
    # print("右下x坐标:{}".format(bottom_right_x))
    # print("右下y坐标:{}".format(bottom_right_y))
    p1, p2 = (int(top_left_x), int(top_left_y)), (int(bottom_right_x), int(bottom_right_y))
    # 绘制矩形框
    color_index = label
    # colors = [random.randint(0, 255) for _ in range(3)]
    cv2.rectangle(img, p1, p2, colors[color_index], thickness=2, lineType=cv2.LINE_AA)
    label = labels[label]

    if label:
        tl = round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
        tf = max(tl - 1, 1)
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        # 两个字类别
        if label.split()[0] == "卡车" or label.split()[0] == "坦克":
            sublength = 100  # 缩减方框的长度
            p2 = p1[0] + t_size[0] - sublength, p1[1] - t_size[1]
        # 三个字类别
        else:
            sublength = 140  # 缩减方框的长度
            p2 = p1[0] + t_size[0] - sublength, p1[1] - t_size[1]

        # 绘制矩形框填充
        cv2.rectangle(img, p1, p2, colors[color_index], -1, cv2.LINE_AA)
        # 绘制标签
        img_text = cv2ImgAddText(img, label, p1[0], p2[1], (255, 255, 255), 25)
    return img_text


def main():
    # 创建输出文件夹
    if Path(output_folder).exists():
        shutil.rmtree(output_folder)
    os.mkdir(output_folder)

    img_path = []
    label_path = []
    for i in range(len(input_list)):
        dir_path = input_folder + "/" + input_list[i]

        # 先对文件中jpg和txt进行分类
        if input_list[i].split('.')[-1] == 'jpg':
            img_path.append(dir_path)
        else:
            label_path.append(dir_path)

    for i in tqdm(img_path):
        # 读取图像文件
        img = cv2.imread(str(i))
        h, w = img.shape[:2]
        label_name = i.split('.')[0] + '.txt'
        if label_name in label_path:
            with open(label_name, 'r') as f:
                lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32)
            # 绘制每一个目标
            for x in lb:
                #  反归一化并得到左上和右下坐标,画出矩形框
                img = xywh2xyxy(x, w, h, img)
            """
            # 直接查看生成结果图
            cv2.imshow('show', img)
            cv2.waitKey(0)
            """
            cv2.imwrite(output_folder + '/' + '{}.jpg'.format(label_name.split('/')[-1][:-4]), img)


if __name__ == '__main__':
    main()

小图标签可视化:保留无目标的图片

'''
直接在小图上还原标签(保留无目标图片)
'''
import os
import random
import shutil
from pathlib import Path
import numpy as np
import cv2
from PIL import ImageDraw, ImageFont
from PIL import Image
from tqdm import tqdm

input_folder = "D:/Data/i2"
# 输出图片文件夹位置
output_folder = "D:/Data/i3"

input_list = os.listdir(input_folder)
input_list.sort()

labels = ['类别一', '类别二']
# colormap = [(0, 255, 0), (132, 112, 255), (255, 255, 255)]  # 色盘,可根据类别添加新颜色
colors = [[random.randint(0, 255) for _ in range(3)] for _ in labels]  # 随机颜色


def cv2ImgAddText(img, text, left, top, textColor=(0, 255, 0), textSize=20):
    # 图像从OpenCV格式转换成PIL格式
    if isinstance(img, np.ndarray):  # 判断是否OpenCV图片类型
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    draw = ImageDraw.Draw(img)
    fontText = ImageFont.truetype("Font/simhei.ttf", textSize, encoding="utf-8")
    draw.text((left, top - 2), text, textColor, font=fontText)
    return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)


# 坐标转换
def xywh2xyxy(x, w1, h1, img):
    label, x, y, w, h = x
    label = int(label)
    # print("原图宽高:\nw1={}\nh1={}".format(w1, h1))
    # 边界框反归一化
    x_t = x * w1
    y_t = y * h1
    w_t = w * w1
    h_t = h * h1
    # print("反归一化后输出:\n第一个:{}\t第二个:{}\t第三个:{}\t第四个:{}\t\n\n".format(x_t, y_t, w_t, h_t))
    # 计算坐标
    top_left_x = x_t - w_t / 2
    top_left_y = y_t - h_t / 2
    bottom_right_x = x_t + w_t / 2
    bottom_right_y = y_t + h_t / 2
    # print('标签:{}'.format(labels[int(label)]))
    # print("左上x坐标:{}".format(top_left_x))
    # print("左上y坐标:{}".format(top_left_y))
    # print("右下x坐标:{}".format(bottom_right_x))
    # print("右下y坐标:{}".format(bottom_right_y))
    p1, p2 = (int(top_left_x), int(top_left_y)), (int(bottom_right_x), int(bottom_right_y))
    # 绘制矩形框
    color_index = label
    cv2.rectangle(img, p1, p2, colors[color_index], thickness=2, lineType=cv2.LINE_AA)
    label = labels[label]

    if label:
        tl = round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
        tf = max(tl - 1, 1)
        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]
        # 两个字类别
        if label.split()[0] == "卡车" or label.split()[0] == "坦克":
            sublength = 100  # 缩减方框的长度
            p2 = p1[0] + t_size[0] - sublength, p1[1] - t_size[1]
        # 三个字类别
        else:
            sublength = 140  # 缩减方框的长度
            p2 = p1[0] + t_size[0] - sublength, p1[1] - t_size[1]

        # 绘制矩形框填充
        cv2.rectangle(img, p1, p2, colors[color_index], -1, cv2.LINE_AA)
        # 绘制标签
        img_text = cv2ImgAddText(img, label, p1[0], p2[1], (255, 255, 255), 25)
    return img_text


def main():
    # 创建输出文件夹
    if Path(output_folder).exists():
        shutil.rmtree(output_folder)
    os.mkdir(output_folder)

    img_path = []
    label_path = []
    for i in range(len(input_list)):
        dir_path = input_folder + "/" + input_list[i]

        # 先对文件中jpg和txt进行分类
        if input_list[i].split('.')[-1] == 'jpg':
            img_path.append(dir_path)
        else:
            label_path.append(dir_path)

    for i in tqdm(img_path):
        # 读取图像文件
        img = cv2.imread(str(i))
        h, w = img.shape[:2]

        img_name = i.split('/')[-1].split('.')[0]
        label_name = i.split('.')[0] + '.txt'
        if label_name in label_path:
            with open(label_name, 'r') as f:
                lb = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32)
            # 绘制每一个目标
            for x in lb:
                #  反归一化并得到左上和右下坐标,画出矩形框
                img = xywh2xyxy(x, w, h, img)
            """
            # 直接查看生成结果图
            cv2.imshow('show', img)
            cv2.waitKey(0)
            """
            cv2.imwrite(output_folder + '/' + '{}.jpg'.format(label_name.split('/')[-1][:-4]), img)
        else:
            cv2.imwrite(output_folder + '/' + img_name + '.jpg', img)


if __name__ == '__main__':
    main()

图像拼接

图像拼接是将标签映射上的小图还原成大图,具体思路是通过读取yaml文件,获得图片的尺寸,然后计算出一张大图有几行几列,先将每一列进行拼接,之后拼接一行,得到大图,最后根据原图尺寸进行裁剪,除掉黑边。

'''
将小图还原出大图
'''
import shutil
from pathlib import Path
import numpy as np
import cv2
import yaml
import os.path

from tqdm import tqdm

rootdir = 'D:/Data/i3'  # 原始图片文件夹
savedir = 'D:/Data/i4'  # 保存图片文件夹
ConfigPath = "../config.yaml"  # 配置文件(记录图片尺寸)


# 拼接每一列的图片
def pix_v_img(v_pic):
    img_stack = []
    for i in v_pic:
        temp_img = cv2.imread(i)
        img_stack.append(temp_img)
    imgStackV = np.vstack(img_stack)
    return imgStackV


if __name__ == '__main__':

    # 创建输出文件夹
    if Path(savedir).exists():
        shutil.rmtree(savedir)
    os.mkdir(savedir)

    h_pic = []
    v_pic = []

    with open(file=ConfigPath, mode="rb") as f:
        infos = yaml.load(f, Loader=yaml.FullLoader)
        pic_width = infos['width']
        pic_height = infos['height']

    x_max = int(pic_width / 1280)
    y_max = int(pic_height / 1280)
    for filename in tqdm(os.listdir(rootdir)):
        currentPath = os.path.join(rootdir, filename)
        name_x_y = currentPath.split('.')[0].split('__')
        name = name_x_y[0]
        x = int(int(name_x_y[1]) / 1280)
        y = int(int(name_x_y[2]) / 1280)

        if (x != x_max and y != y_max) or (x == x_max and y != y_max):
            v_pic.append(currentPath)
        if x != x_max and y == y_max:
            v_pic.append(currentPath)
            temp_img = pix_v_img(v_pic)
            h_pic.append(temp_img)
            v_pic = []

        if x == x_max and y == y_max:
            v_pic.append(currentPath)
            temp_img = pix_v_img(v_pic)
            h_pic.append(temp_img)
            v_pic = []
            imgStackH = np.hstack(h_pic)
            saveFile = savedir + '/' + filename.split('.')[0].split('__')[0] + '.jpg'
            imgROI = imgStackH[0: pic_height, 0: pic_width]  # 裁剪图片,去掉黑边
            cv2.imwrite(saveFile, imgROI)  # 保存图像文件
            h_pic = []
  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
裁剪遥感目标检测数据集图像是为了减小图像的尺寸,方便进行数据处理和训练。裁剪可以根据需要选择感兴趣的区域,并将其提取出来作为新的图像样本。遥感目标检测数据集图像裁剪的具体步骤如下: 1. 首先,确定需要裁剪图像位置和尺寸。可以根据目标检测任务的需求,选择包含目标的区域进行裁剪。可以使用标注文件提供的目标位置信息来指导裁剪。 2. 然后,使用图像处理软件或编程语言读取原始图像。根据确定的位置和尺寸,裁剪出感兴趣的区域。可以使用图像处理库或者相关函数来实现裁剪操作。 3. 接下来,保存裁剪后的图像作为新的样本。可以将裁剪后的图像保存为新的文件,也可以将其存储在内存中进行后续处理。 4. 最后,重复以上步骤,对所有需要裁剪图像进行处理,得到裁剪后的数据集。 值得注意的是,裁剪后的图像尺寸可能会有所变化,需要根据实际情况进行调整。此外,裁剪时要注意保持目标的完整性,避免裁剪过小导致目标信息丢失。同时,应该注意裁剪后的图像仍然保留了目标的类别标签和位置信息,以便后续的目标检测任务使用。这样,裁剪后的图像可以作为新的样本用于训练和评估遥感目标检测模型。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [目标检测+RSOD遥感检测+936张数据集(图片和标签对应)+4个类别检测](https://download.csdn.net/download/qq_45825952/87689570)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [AIR-SARShip-1.0遥感目标检测数据集图像裁剪](https://blog.csdn.net/qq_39180345/article/details/115017879)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [基于大遥感影像的目标检测数据集的裁剪](https://blog.csdn.net/weixin_40450867/article/details/119763189)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zstar-_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值