python对检测分割数据集图像和标注进行切分

python对检测分割数据集图像和标注进行切分

import cv2
import json
import math
import numpy as np
from utils import *
from tqdm import tqdm

# meta数据类,用于存放切片的坐标
class SliceAndSpliceMeta:
    def __init__(self) -> None:
        self.slice_starts = None
        self.slice_ends = None

        self.splice_starts = None
        self.splice_ends = None

'''
get_meta:获取窗口的meta数据(meta:包含了切分区块的左上角和右下角的坐标)
image_side:原图边长
patch_side:区块边长
'''
def get_meta(image_side, patch_side):
    # 确定数据格式(存储:左上角点x1、y1和右下角点数据x2、y2)
    meta = SliceAndSpliceMeta()

    # 如果原图边长小于区块边长
    if image_side <= patch_side:
        # x坐标置为0
        # y坐标置为区块边长
        meta.slice_starts = [0]
        meta.slice_ends = [patch_side]

        meta.splice_starts = [0]
        meta.splice_ends = [patch_side]

        return meta
    # 确定切片个数
    num = math.ceil(image_side / patch_side)  # 能切多少片
    # 确定overlap个数
    num_overlaps = num - 1  # 有多少个overlap
    # overlap多少像素
    overlap_pixels = patch_side * num - image_side

    # 把剩余的分配到前几个overlap上
    # 分配每个区块占的overlap
    base_overlap = math.floor(overlap_pixels / num_overlaps)
    # 任最后还留有overlaps未分配像素长度
    remain_pixels = overlap_pixels % num_overlaps
    #
    overlaps = [base_overlap] * num_overlaps
    #
    for i in range(remain_pixels):
        overlaps[i] += 1

    # 计算切片时starts和ends
    slice_starts = [None] * num
    slice_ends = [None] * num
    for i in range(num):
        slice_starts[i] = patch_side * i - sum(ele for ele in overlaps[:i])
        slice_ends[i] = patch_side * (i + 1) - sum(ele for ele in overlaps[:i])

    # 计算overlaps的lefts和rights
    rights = [math.floor(overlap / 2) for overlap in overlaps]
    lefts = [overlap - right for overlap, right in zip(overlaps, rights)]

    # 计算拼接时有效的starts和ends
    splice_starts = [0] + lefts
    splice_ends = rights + [0]

    tmp = [patch_side] * num
    splice_ends = [t - splice_end for t, splice_end in zip(tmp, splice_ends)]

    # 返回
    meta.slice_starts = slice_starts
    meta.slice_ends = slice_ends

    meta.splice_starts = splice_starts
    meta.splice_ends = splice_ends

    return meta

'''
存切片后的图和切片后的标签      
img_patch: 区块图像
mask_patch_and_id_vec: 缺陷mask和对应id列表
area_thres:过滤阈值
path_to_img_patches: 存图地址
path_to_txt_patches: 存标注地址
image.split(".")[0]:图像名称
patch_idx:区块id
task:任务类型,seg或det
'''
def save_patch_and_txt(patch, mask_patch_and_id_vec, area_thres, path_to_img_patches, path_to_txt_patches, filename,
                       patch_idx, task):
    # 如果该区块内不存在缺陷,则直接结束返回
    if len(mask_patch_and_id_vec) == 0:
        return

    # 获取区块的高宽
    patch_h, patch_w = patch.shape[:2]
    # 合并高宽
    w_and_h = np.array([patch_w, patch_h], dtype=np.float64)
    # append
    opened = False
    # 遍历该区块内的所有缺陷mask和类别id
    for patch_mask, class_id in mask_patch_and_id_vec:
        # 查找区块mask的轮廓
        found_contours, hierarchy = cv2.findContours(patch_mask, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)
        # 遍历所有轮廓
        for found_contour in found_contours:
            # 轮廓面积
            contour_area = cv2.contourArea(found_contour, False)
            # 面积过滤
            if contour_area >= area_thres:
                line = f"{class_id - 1} "
                # 分割任务
                if task == "seg":
                    # 0.005*轮廓周长
                    epsilon = 0.005 * cv2.arcLength(found_contour, True)
                    # 柔和轮廓
                    approx = cv2.approxPolyDP(found_contour, epsilon, True) if len(
                        found_contour.reshape(-1).tolist()) > 20 else found_contour

                    # 点太少了,只有两个点,如果轮廓不满足,则跳过
                    if len(approx.reshape(-1).tolist()) < 6:
                        continue

                    # 画图(optional)
                    # cv2.polylines(patch, [approx], isClosed=True, color=[255, 255, 255], thickness=1)
                    # 轮廓点除以高宽,将坐标缩放到0-1之间
                    approx = approx / w_and_h
                elif task == "det": # 这个不需要过滤面积吗?
                    # 查找轮廓的最小值(即左上角点)
                    x1, y1 = found_contour.min(0)[0]
                    # 查找轮廓的最大值(即右下角点)
                    x2, y2 = found_contour.max(0)[0]

                    # 画图(optional)
                    # cv2.rectangle(patch, (x1,y1),(x2,y2), color=[255, 255, 255], thickness=1)
                    # 根据左上角点和右下角点计算出中心点和宽高,再缩放到0-1之间
                    approx = np.array([(x1 + x2) / 2.0, (y1 + y2) / 2.0, x2 - x1, y2 - y1]) / np.concatenate(
                        [w_and_h, w_and_h])

                else:
                    assert False, f"task: '{task}' not support"
                # yolo标签文本
                line += " ".join(str(ele)[:5] for ele in list(approx.reshape(-1))) + "\n"
                # 如果不是append模式
                if not opened:
                    # 打开标签文本地址
                    f = open(os.path.join(path_to_txt_patches, filename + f"_{patch_idx}.txt"), "w")
                    # 将append置True
                    opened = True
                    # 写入标签文本
                    f.write(line)
    # 如果
    if opened:
        # 存区块图
        cv2.imwrite(os.path.join(path_to_img_patches, filename + f"_{patch_idx}.bmp"), patch)
        # 关闭文本流
        f.close()


def image_json_dynamic_slice(image_path, json_path, output_path, patch_h, patch_w, names2ids, area_thres):
    # --------------------------下面是参数--------------------------------------
    # 图像路径
    image_path = image_path
    # 分割标注路径
    json_path = json_path
    '''
    检测图像及标注
    '''

    task = "det"
    # 切分后图像保存路径
    path_to_img_patches = os.path.join(output_path,"det" ,"images")
    # 切分后标签保存路径
    path_to_txt_patches = os.path.join(output_path,"det" ,"txt")
    # 创建文件夹
    makedir(path_to_img_patches)
    makedir(path_to_txt_patches)

    # 可视化路径
    path_vis1 = os.path.join(output_path,"Vis1")
    path_vis2 = os.path.join(output_path,"Vis2")
    makedir(path_vis1)
    makedir(path_vis2)
    # 切分后窗口高
    patch_h = patch_h
    # 切分后窗口宽
    patch_w = patch_w
    # 标签名称和id对应关系字典 注: 从1开始
    names2ids = names2ids
    # 面积过滤阈值
    area_thres = area_thres

    # --------------------------上面是参数--------------------------------------
    # 获取所有待分割图像路径
    images = os.listdir(image_path)
    # 遍历每一张图像
    for image in tqdm(images):
        # 读取图像
        img = cv2.imread(os.path.join(image_path, image), cv2.IMREAD_COLOR)
        # 获取图像的高宽
        srch, srcw = img.shape[:2]
        # 获取切分后区块的横纵坐标
        meta_h = get_meta(srch, patch_h)
        meta_w = get_meta(srcw, patch_w)
        # 区块左上角和右下角坐标
        x1s = []
        y1s = []
        x2s = []
        y2s = []
        # 赋值区块坐标
        for x1, x2 in zip(meta_w.slice_starts, meta_w.slice_ends):
            x1s.append(x1)
            x2s.append(x2)

        for y1, y2 in zip(meta_h.slice_starts, meta_h.slice_ends):
            y1s.append(y1)
            y2s.append(y2)
        # 左上角点列表
        lts = []
        for x1 in x1s:
            for y1 in y1s:
                lts.append((x1, y1))
        # 右下角点列表
        rbs = []
        for x2 in x2s:
            for y2 in y2s:
                rbs.append((x2, y2))

        # 轮廓
        contours = []
        # id列表
        ids = []
        # 获取图像尾缀
        endFile = os.path.splitext(image)[-1]
        # 打开当前图像的对应的json分割标注文件
        with open(os.path.join(json_path, image.replace(endFile, ".json"))) as f:
            # 标签
            label = json.load(f)
            # 获取标签中的shapes
            for shape in label["shapes"]:
                # 断言:判断shape中的label名称是否在names2ids的字典中
                assert shape["label"] in names2ids.keys(), f"{shape['label']}  not in 'names2ids'."
                # 添加标签中的轮廓
                contours.append(np.array(shape["points"], dtype=np.int32))
                # 同时获取对应id
                ids.append(names2ids[shape["label"]])

        # 画contours
        # 初始化原图大小的mask
        msk = np.zeros((srch, srcw), dtype=np.uint8)
        # 绘画检测对象的的poly型mask到原图mask上
        for contour, id_ in zip(contours, ids):
            # 绘画检测对象 mask
            msk = cv2.fillPoly(msk, [contour], id_)

        # 画切片可视图(optional)
        # 原图深拷贝
        plot_img = img.copy()
        # 原图mask深拷贝
        plot_msk = msk.copy()
        # 左上角点和右下角点
        for lt, rb in zip(lts, rbs):
            # 原图上绘制矩形框
            plot_img = cv2.rectangle(plot_img, lt, rb, (255,255,255), 1)
            # 在原图mask上绘制矩形框
            plot_msk = cv2.rectangle(plot_msk, lt, rb, (4,4,4), 1)
        # 可视化化存图路径
        v1_path = os.path.join(path_vis1,image)
        v2_path = os.path.join(path_vis2,image)
        # 存绘画后的原图
        cv2.imwrite(v1_path, plot_img)
        # 存绘画后的mask
        cv2.imwrite(v2_path, plot_msk * 60)

        # 切片
        # 区块id
        patch_idx = 0
        # 遍历左上角点和右下角点
        for lt, rb in zip(lts, rbs):

            '''
            修改处:加上overlap   .clip(0, x)用于防呆
            '''
            x1_overlap = lt[0]
            x2_overlap = rb[0]
            y1_overlap = lt[1]
            y2_overlap = rb[1]
            # 深拷贝原图区块
            img_patch = img[lt[1]:rb[1], lt[0]:rb[0], :].copy()
            # 深拷贝mask区块(重要)
            msk_patch = msk[lt[1]:rb[1], lt[0]:rb[0]].copy()
            # mask和id元组存储列表
            mask_patch_and_id_vec = []
            # 遍历所有标签类别id,获取所有id对应的缺陷mask
            for id_ in names2ids.values():
                # 获取对应缺陷类别id的mask(重要)
                patch_mask = np.array(msk_patch == id_, dtype=np.uint8)
                # 如果存在该缺陷类别的id的mask,则加入列表
                if np.any(patch_mask):
                    mask_patch_and_id_vec.append((patch_mask, id_))
            '''
            img_patch: 区块图像
            mask_patch_and_id_vec: 缺陷mask和对应id列表
            area_thres:过滤阈值
            path_to_img_patches: 存图地址
            path_to_txt_patches: 存标注地址
            image.split(".")[0]:图像名称
            patch_idx:区块id
            task:任务类型,seg或det
            '''
            save_patch_and_txt(img_patch, mask_patch_and_id_vec, area_thres,
                               path_to_img_patches, path_to_txt_patches, image.split(".")[0], patch_idx,task)
            patch_idx += 1

    '''
    分割图像及标注
    '''

    task = "seg"
    path_to_img_patches = os.path.join(output_path,"seg" ,"images")
    path_to_txt_patches = os.path.join(output_path,"seg" ,"txt")
    # 创建文件夹
    makedir(path_to_img_patches)
    makedir(path_to_txt_patches)

    for image in tqdm(images):
        img = cv2.imread(os.path.join(image_path, image), cv2.IMREAD_COLOR)
        srch, srcw = img.shape[:2]

        meta_h = get_meta(srch, patch_h)
        meta_w = get_meta(srcw, patch_w)

        x1s = []
        y1s = []
        x2s = []
        y2s = []
        for x1, x2 in zip(meta_w.slice_starts, meta_w.slice_ends):
            x1s.append(x1)
            x2s.append(x2)

        for y1, y2 in zip(meta_h.slice_starts, meta_h.slice_ends):
            y1s.append(y1)
            y2s.append(y2)

        lts = []
        for x1 in x1s:
            for y1 in y1s:
                lts.append((x1, y1))

        rbs = []
        for x2 in x2s:
            for y2 in y2s:
                rbs.append((x2, y2))

        contours = []
        ids = []

        # .bmp换成通用的
        endFile = os.path.splitext(image)[-1]
        with open(os.path.join(json_path, image.replace(endFile, ".json"))) as f:
            label = json.load(f)
            for shape in label["shapes"]:
                assert shape["label"] in names2ids.keys(), f"{shape['label']}  not in 'names2ids'."
                contours.append(np.array(shape["points"], dtype=np.int32))
                ids.append(names2ids[shape["label"]])

        # 画contours
        msk = np.zeros((srch, srcw), dtype=np.uint8)
        for contour, id_ in zip(contours, ids):
            msk = cv2.fillPoly(msk, [contour], id_)

        # 切片
        patch_idx = 0
        for lt, rb in zip(lts, rbs):
            img_patch = img[lt[1]:rb[1], lt[0]:rb[0], :].copy()
            msk_patch = msk[lt[1]:rb[1], lt[0]:rb[0]].copy()

            mask_patch_and_id_vec = []
            for id_ in names2ids.values():
                patch_mask = np.array(msk_patch == id_, dtype=np.uint8)
                if np.any(patch_mask):
                    mask_patch_and_id_vec.append((patch_mask, id_))

            save_patch_and_txt(img_patch, mask_patch_and_id_vec, area_thres,
                               path_to_img_patches, path_to_txt_patches, image.split(".")[0], patch_idx,task)
            patch_idx += 1


if __name__ == '__main__':
    try:
        image_path = rf"D:\workplace\python\pythonProject2\SideArray\images\train"
        json_path = rf"D:\workplace\python\pythonProject2\SideArray\Seg\train"
        output_path = r"output"
        patch_h = 1280
        patch_w = 1280
        # {"Dent": 1, "Scratch": 2, "Contamination": 3, "Pitting": 4, "Dust": 5, "HairScratch": 6}  # 从1开始

        names2ids ={"Dent": 1, "Scratch": 2, "Contamination": 3, "Discoloration": 4, "Pitting": 5, "Dust": 6, "HairScratch": 7, "Fiber": 8}  # 从1开始
        area_thres = 10
        image_json_dynamic_slice(image_path=image_path, json_path=json_path, output_path=output_path, patch_h=patch_h, patch_w=patch_w, names2ids=names2ids, area_thres=area_thres)
    except Exception as exp:
        with open('log.txt', "a") as f:
            f.writelines(exp + '\n')
import tkinter as tk
import tkinter.font as tkFont
from deploy import image_json_dynamic_slice


class App:
    def __init__(self, root):
        #setting title
        root.title("undefined")
        #setting window size
        width=685
        height=494
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)

        self.GLineEdit_609=tk.Entry(root)
        self.GLineEdit_609["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_609["font"] = ft
        self.GLineEdit_609["fg"] = "#333333"
        self.GLineEdit_609["justify"] = "center"
        self.GLineEdit_609_content = tk.StringVar()
        self.GLineEdit_609["textvariable"] = self.GLineEdit_609_content
        self.GLineEdit_609.place(x=160,y=30,width=287,height=30)

        GButton_201=tk.Button(root)
        GButton_201["bg"] = "#f0f0f0"
        ft = tkFont.Font(family='Times',size=10)
        GButton_201["font"] = ft
        GButton_201["fg"] = "#000000"
        GButton_201["justify"] = "center"
        GButton_201["text"] = "开始"
        GButton_201.place(x=500,y=380,width=135,height=49)
        GButton_201["command"] = self.GButton_201_command

        GLabel_825=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_825["font"] = ft
        GLabel_825["fg"] = "#333333"
        GLabel_825["justify"] = "center"
        GLabel_825["text"] = "图像目录"
        GLabel_825.place(x=40,y=30,width=70,height=25)

        GLabel_478=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_478["font"] = ft
        GLabel_478["fg"] = "#333333"
        GLabel_478["justify"] = "center"
        GLabel_478["text"] = "分割标注目录"
        GLabel_478.place(x=40,y=80,width=81,height=30)

        self.GLineEdit_786=tk.Entry(root)
        self.GLineEdit_786["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_786["font"] = ft
        self.GLineEdit_786["fg"] = "#333333"
        self.GLineEdit_786["justify"] = "center"
        self.GLineEdit_786_content = tk.StringVar()
        self.GLineEdit_786["textvariable"] = self.GLineEdit_786_content
        self.GLineEdit_786.place(x=160,y=80,width=288,height=30)

        GLabel_435=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_435["font"] = ft
        GLabel_435["fg"] = "#333333"
        GLabel_435["justify"] = "center"
        GLabel_435["text"] = "结果输出目录"
        GLabel_435.place(x=40,y=130,width=79,height=30)

        self.GLineEdit_503=tk.Entry(root)
        self.GLineEdit_503["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_503["font"] = ft
        self.GLineEdit_503["fg"] = "#333333"
        self.GLineEdit_503["justify"] = "center"
        self.GLineEdit_503_content = tk.StringVar()
        self.GLineEdit_503["textvariable"] = self.GLineEdit_503_content
        self.GLineEdit_503.place(x=160,y=130,width=286,height=30)

        GLabel_0=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_0["font"] = ft
        GLabel_0["fg"] = "#333333"
        GLabel_0["justify"] = "center"
        GLabel_0["text"] = "切片高"
        GLabel_0.place(x=40,y=180,width=70,height=25)

        self.GLineEdit_994=tk.Entry(root)
        self.GLineEdit_994["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_994["font"] = ft
        self.GLineEdit_994["fg"] = "#333333"
        self.GLineEdit_994["justify"] = "center"
        self.GLineEdit_994_content = tk.StringVar()
        self.GLineEdit_994["textvariable"] = self.GLineEdit_994_content
        self.GLineEdit_994.place(x=160,y=180,width=158,height=30)

        GLabel_469=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_469["font"] = ft
        GLabel_469["fg"] = "#333333"
        GLabel_469["justify"] = "center"
        GLabel_469["text"] = "切片宽"
        GLabel_469.place(x=40,y=220,width=70,height=25)

        self.GLineEdit_597=tk.Entry(root)
        self.GLineEdit_597["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_597["font"] = ft
        self.GLineEdit_597["fg"] = "#333333"
        self.GLineEdit_597["justify"] = "center"
        self.GLineEdit_597_content = tk.StringVar()
        self.GLineEdit_597["textvariable"] = self.GLineEdit_597_content
        self.GLineEdit_597.place(x=160,y=220,width=160,height=30)

        GLabel_213=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_213["font"] = ft
        GLabel_213["fg"] = "#333333"
        GLabel_213["justify"] = "center"
        GLabel_213["text"] = "名称序号字典"
        GLabel_213.place(x=40,y=260,width=90,height=30)

        self.GLineEdit_82=tk.Entry(root)
        self.GLineEdit_82["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_82["font"] = ft
        self.GLineEdit_82["fg"] = "#333333"
        self.GLineEdit_82["justify"] = "center"
        self.GLineEdit_82_content = tk.StringVar()
        self.GLineEdit_82["textvariable"] = self.GLineEdit_82_content
        self.GLineEdit_82.place(x=160,y=260,width=291,height=30)

        GLabel_431=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_431["font"] = ft
        GLabel_431["fg"] = "#333333"
        GLabel_431["justify"] = "center"
        GLabel_431["text"] = "过滤阈值"
        GLabel_431.place(x=40,y=310,width=70,height=25)

        self.GLineEdit_270=tk.Entry(root)
        self.GLineEdit_270["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_270["font"] = ft
        self.GLineEdit_270["fg"] = "#333333"
        self.GLineEdit_270["justify"] = "center"
        self.GLineEdit_270_content = tk.StringVar()
        self.GLineEdit_270["textvariable"] = self.GLineEdit_270_content
        self.GLineEdit_270.place(x=160,y=310,width=165,height=30)

    def GButton_201_command(self):
        input1 = self.GLineEdit_609_content.get()
        input2 = self.GLineEdit_786_content.get()
        input3 = self.GLineEdit_503_content.get()
        input4 = self.GLineEdit_994_content.get()
        input5 = self.GLineEdit_597_content.get()
        input6 = self.GLineEdit_82_content.get()
        input7 = self.GLineEdit_270_content.get()
        print(input1)
        print(input2)
        print(input3)
        print(input4)
        print(input5)
        print(input6)
        print(input7)
        image_json_dynamic_slice(input1,input2,input3,int(input4),int(input5),eval(input6),int(input7))


if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()


import cv2
import json
import math
import numpy as np
from utils import *
from tqdm import tqdm


# meta数据类,用于存放切片的坐标
class SliceAndSpliceMeta:
    def __init__(self) -> None:
        self.slice_starts = None
        self.slice_ends = None

        self.splice_starts = None
        self.splice_ends = None


'''
get_meta:获取窗口的meta数据(meta:包含了切分区块的左上角和右下角的坐标)
image_side:原图边长
patch_side:区块边长
'''


def get_meta(image_side, patch_side):
    # 确定数据格式(存储:左上角点x1、y1和右下角点数据x2、y2)
    meta = SliceAndSpliceMeta()

    # 如果原图边长小于区块边长
    if image_side <= patch_side:
        # x坐标置为0
        # y坐标置为区块边长
        meta.slice_starts = [0]
        meta.slice_ends = [patch_side]

        meta.splice_starts = [0]
        meta.splice_ends = [patch_side]

        return meta
    # 确定切片个数
    num = math.ceil(image_side / patch_side)  # 能切多少片
    # 确定overlap个数
    num_overlaps = num - 1  # 有多少个overlap
    # overlap多少像素
    overlap_pixels = patch_side * num - image_side

    # 把剩余的分配到前几个overlap上
    # 分配每个区块占的overlap
    base_overlap = math.floor(overlap_pixels / num_overlaps)
    # 任最后还留有overlaps未分配像素长度
    remain_pixels = overlap_pixels % num_overlaps
    #
    overlaps = [base_overlap] * num_overlaps
    #
    for i in range(remain_pixels):
        overlaps[i] += 1

    # 计算切片时starts和ends
    slice_starts = [None] * num
    slice_ends = [None] * num
    for i in range(num):
        # 横坐标(起点)
        slice_starts[i] = patch_side * i - sum(ele for ele in overlaps[:i])
        # 横坐标(终点)
        slice_ends[i] = patch_side * (i + 1) - sum(ele for ele in overlaps[:i])

    # 计算overlaps的lefts和rights
    rights = [math.floor(overlap / 2) for overlap in overlaps]
    lefts = [overlap - right for overlap, right in zip(overlaps, rights)]

    # 计算拼接时有效的starts和ends
    # 纵坐标(起点)
    splice_starts = [0] + lefts
    splice_ends = rights + [0]

    tmp = [patch_side] * num
    # 纵坐标(终点)
    splice_ends = [t - splice_end for t, splice_end in zip(tmp, splice_ends)]

    # 返回
    meta.slice_starts = slice_starts
    meta.slice_ends = slice_ends

    meta.splice_starts = splice_starts
    meta.splice_ends = splice_ends

    return meta


'''
存切片后的图和切片后的标签      
img_patch: 区块图像
mask_patch_and_id_vec: 缺陷mask和对应id列表
area_thres:过滤阈值
path_to_img_patches: 存图地址
path_to_txt_patches: 存标注地址
image.split(".")[0]:图像名称
patch_idx:区块id
task:任务类型,seg或det
'''


def save_patch_and_txt(patch, mask_patch_and_id_vec, area_thres, path_to_img_patches, path_to_txt_patches, filename,
                       patch_idx, task):
    # 如果该区块内不存在缺陷,则直接结束返回
    if len(mask_patch_and_id_vec) == 0:
        return

    # 获取区块的高宽
    patch_h, patch_w = patch.shape[:2]
    # 合并高宽
    w_and_h = np.array([patch_w, patch_h], dtype=np.float64)
    # append
    opened = False
    # 遍历该区块内的所有缺陷mask和类别id
    for patch_mask, class_id in mask_patch_and_id_vec:
        # 查找区块mask的轮廓
        found_contours, hierarchy = cv2.findContours(patch_mask, mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_NONE)
        # 遍历所有轮廓
        for found_contour in found_contours:
            # 轮廓面积
            contour_area = cv2.contourArea(found_contour, False)
            # 面积过滤
            if contour_area >= area_thres:
                line = f"{class_id - 1} "
                # 分割任务
                if task == "seg":
                    # 0.005*轮廓周长
                    epsilon = 0.005 * cv2.arcLength(found_contour, True)
                    # 柔和轮廓
                    approx = cv2.approxPolyDP(found_contour, epsilon, True) if len(
                        found_contour.reshape(-1).tolist()) > 20 else found_contour

                    # 点太少了,只有两个点,如果轮廓不满足,则跳过
                    if len(approx.reshape(-1).tolist()) < 6:
                        continue

                    # 画图(optional)
                    # cv2.polylines(patch, [approx], isClosed=True, color=[255, 255, 255], thickness=1)
                    # 轮廓点除以高宽,将坐标缩放到0-1之间
                    approx = approx / w_and_h
                elif task == "det":  # 这个不需要过滤面积吗?
                    # 查找轮廓的最小值(即左上角点)
                    x1, y1 = found_contour.min(0)[0]
                    # 查找轮廓的最大值(即右下角点)
                    x2, y2 = found_contour.max(0)[0]

                    # 画图(optional)
                    # cv2.rectangle(patch, (x1,y1),(x2,y2), color=[255, 255, 255], thickness=1)
                    # 根据左上角点和右下角点计算出中心点和宽高,再缩放到0-1之间
                    approx = np.array([(x1 + x2) / 2.0, (y1 + y2) / 2.0, x2 - x1, y2 - y1]) / np.concatenate(
                        [w_and_h, w_and_h])

                else:
                    assert False, f"task: '{task}' not support"
                # yolo标签文本
                line += " ".join(str(ele)[:5] for ele in list(approx.reshape(-1))) + "\n"
                # 如果不是append模式
                if not opened:
                    # 打开创建标签文本地址(创建一次即可)
                    f = open(os.path.join(path_to_txt_patches, filename + f"_{patch_idx}.txt"), "w")
                    # 将append置True
                    opened = True
                # 写入标签文本
                f.write(line)
    # 如果
    if opened:
        # 存区块图
        cv2.imwrite(os.path.join(path_to_img_patches, filename + f"_{patch_idx}.bmp"), patch)
        # 关闭文本流
        f.close()


def image_json_dynamic_slice(image_path, json_path, output_path, patch_h, patch_w, names2ids, area_thres,overlap):
    # --------------------------下面是参数--------------------------------------
    # 图像路径
    image_path = image_path
    # 分割标注路径
    json_path = json_path
    '''
    检测图像及标注
    '''

    task = "det"
    # 切分后图像保存路径
    path_to_img_patches = os.path.join(output_path, "det", "images")
    # 切分后标签保存路径
    path_to_txt_patches = os.path.join(output_path, "det", "txt")
    # 创建文件夹
    makedir(path_to_img_patches)
    makedir(path_to_txt_patches)

    # 可视化路径
    path_vis1 = os.path.join(output_path, "Vis1")
    path_vis2 = os.path.join(output_path, "Vis2")
    makedir(path_vis1)
    makedir(path_vis2)
    # 切分后窗口高
    patch_h = patch_h
    # 切分后窗口宽
    patch_w = patch_w
    # 标签名称和id对应关系字典 注: 从1开始
    names2ids = names2ids
    # 面积过滤阈值
    area_thres = area_thres

    # --------------------------上面是参数--------------------------------------
    # 获取所有待分割图像路径
    images = os.listdir(image_path)
    # 遍历每一张图像
    for image in tqdm(images):
        # 读取图像

        if not (os.path.splitext(image)[-1] == ".bmp" or os.path.splitext(image)[-1] == ".jpg" or os.path.splitext(image)[-1] == ".png"):
          continue
        # img = cv2.imread(os.path.join(image_path, image), cv2.IMREAD_COLOR)
        img = cv2.imdecode(np.fromfile(os.path.join(image_path, image), dtype=np.uint8), flags=cv2.IMREAD_COLOR)  # -1代表读取彩色图片

        # 获取图像的高宽
        try :
            srch, srcw = img.shape[:2]
        except Exception as exp:
            print(exp)
        # 获取切分后区块的横纵坐标
        meta_h = get_meta(srch, patch_h)
        meta_w = get_meta(srcw, patch_w)
        # 区块左上角和右下角坐标
        x1s = []
        y1s = []
        x2s = []
        y2s = []
        # 赋值区块坐标
        for x1, x2 in zip(meta_w.slice_starts, meta_w.slice_ends):
            x1s.append(x1)
            x2s.append(x2)

        for y1, y2 in zip(meta_h.slice_starts, meta_h.slice_ends):
            y1s.append(y1)
            y2s.append(y2)
        # 左上角点列表
        lts = []
        for x1 in x1s:
            for y1 in y1s:
                lts.append((x1, y1))
        # 右下角点列表
        rbs = []
        for x2 in x2s:
            for y2 in y2s:
                rbs.append((x2, y2))

        # 轮廓
        contours = []
        # id列表
        ids = []
        # 获取图像尾缀
        endFile = os.path.splitext(image)[-1]
        # 打开当前图像的对应的json分割标注文件
        if not os.path.exists(os.path.join(json_path, image.replace(endFile, ".json"))):
            continue

        with open(os.path.join(json_path, image.replace(endFile, ".json")),encoding='utf-8-sig',errors='ignore') as f:
            # 标签
            label = json.load(f)
            # 获取标签中的shapes
            for shape in label["shapes"]:
                # 断言:判断shape中的label名称是否在names2ids的字典中
                assert shape["label"] in names2ids.keys(), f"{shape['label']}  not in 'names2ids'."
                # 添加标签中的轮廓
                contours.append(np.array(shape["points"], dtype=np.int32))
                # 同时获取对应id
                ids.append(names2ids[shape["label"]])

        # 画contours
        # 初始化原图大小的mask
        msk = np.zeros((srch, srcw), dtype=np.uint8)
        # 绘画检测对象的的poly型mask到原图mask上
        for contour, id_ in zip(contours, ids):
            # 绘画检测对象 mask
            try:
                msk = cv2.fillPoly(msk, [contour], id_)
            except Exception as exp:
                print(exp)
        # 画切片可视图(optional)
        # 原图深拷贝
        plot_img = img.copy()
        # 原图mask深拷贝
        plot_msk = msk.copy()
        # 左上角点和右下角点
        for lt, rb in zip(lts, rbs):
            # 原图上绘制矩形框
            plot_img = cv2.rectangle(plot_img, lt, rb, (255, 255, 255), 1)
            # 在原图mask上绘制矩形框
            plot_msk = cv2.rectangle(plot_msk, lt, rb, (4, 4, 4), 1)
        # 可视化化存图路径
        v1_path = os.path.join(path_vis1, image)
        v2_path = os.path.join(path_vis2, image)
        # 存绘画后的原图
        cv2.imwrite(v1_path, plot_img)
        # 存绘画后的mask
        cv2.imwrite(v2_path, plot_msk * 60)

        # 切片
        # 区块id
        patch_idx = 0
        # 遍历左上角点和右下角点
        for lt, rb in zip(lts, rbs):

            '''
            修改处:加上overlap   .clip(0, x)用于防呆
            '''
            overlap = overlap
            x1_overlap = np.array(lt[0]-overlap).clip(0, srcw)
            x2_overlap = np.array(rb[0]+overlap).clip(0, srcw)
            y1_overlap = np.array(lt[1]-overlap).clip(0, srch)
            y2_overlap = np.array(rb[1]+overlap).clip(0, srch)
            # 深拷贝原图区块
            img_patch = img[y1_overlap:y2_overlap, x1_overlap:x2_overlap, :].copy()
            # 深拷贝mask区块(重要)
            msk_patch = msk[y1_overlap:y2_overlap, x1_overlap:x2_overlap].copy()
            # mask和id元组存储列表
            mask_patch_and_id_vec = []
            # 遍历所有标签类别id,获取所有id对应的缺陷mask
            for id_ in names2ids.values():
                # 获取对应缺陷类别id的mask(重要)
                patch_mask = np.array(msk_patch == id_, dtype=np.uint8)
                # 如果存在该缺陷类别的id的mask,则加入列表
                if np.any(patch_mask):
                    mask_patch_and_id_vec.append((patch_mask, id_))
            '''
            img_patch: 区块图像
            mask_patch_and_id_vec: 缺陷mask和对应id列表
            area_thres:过滤阈值
            path_to_img_patches: 存图地址
            path_to_txt_patches: 存标注地址
            image.split(".")[0]:图像名称
            patch_idx:区块id
            task:任务类型,seg或det
            '''
            save_patch_and_txt(img_patch, mask_patch_and_id_vec, area_thres,
                               path_to_img_patches, path_to_txt_patches, image.split(".")[0], patch_idx, task)
            patch_idx += 1

    # '''
    # 分割图像及标注
    # '''
    #
    # task = "seg"
    # path_to_img_patches = os.path.join(output_path, "seg", "images")
    # path_to_txt_patches = os.path.join(output_path, "seg", "txt")
    # # 创建文件夹
    # makedir(path_to_img_patches)
    # makedir(path_to_txt_patches)
    #
    # for image in tqdm(images):
    #     if not (os.path.splitext(image)[-1] == ".bmp" or os.path.splitext(image)[-1] == ".jpg" or os.path.splitext(image)[-1] == ".png"):
    #       continue
    #     img = cv2.imread(os.path.join(image_path, image), cv2.IMREAD_COLOR)
    #     srch, srcw = img.shape[:2]
    #
    #     meta_h = get_meta(srch, patch_h)
    #     meta_w = get_meta(srcw, patch_w)
    #
    #     x1s = []
    #     y1s = []
    #     x2s = []
    #     y2s = []
    #     for x1, x2 in zip(meta_w.slice_starts, meta_w.slice_ends):
    #         x1s.append(x1)
    #         x2s.append(x2)
    #
    #     for y1, y2 in zip(meta_h.slice_starts, meta_h.slice_ends):
    #         y1s.append(y1)
    #         y2s.append(y2)
    #
    #     lts = []
    #     for x1 in x1s:
    #         for y1 in y1s:
    #             lts.append((x1, y1))
    #
    #     rbs = []
    #     for x2 in x2s:
    #         for y2 in y2s:
    #             rbs.append((x2, y2))
    #
    #     contours = []
    #     ids = []
    #
    #
    #
    #     # .bmp换成通用的
    #     endFile = os.path.splitext(image)[-1]
    #     if not os.path.exists(os.path.join(json_path, image.replace(endFile, ".json"))):
    #         continue
    #
    #
    #
    #     with open(os.path.join(json_path, image.replace(endFile, ".json")),encoding="utf-8-sig",errors='ignore') as f:
    #         label = json.load(f)
    #         for shape in label["shapes"]:
    #             assert shape["label"] in names2ids.keys(), f"{shape['label']}  not in 'names2ids'."
    #             contours.append(np.array(shape["points"], dtype=np.int32))
    #             ids.append(names2ids[shape["label"]])
    #
    #     # 画contours
    #     msk = np.zeros((srch, srcw), dtype=np.uint8)
    #     for contour, id_ in zip(contours, ids):
    #         try :
    #             msk = cv2.fillPoly(msk, [contour], id_)
    #         except Exception as exp:
    #             print(exp)
    #     # 切片
    #     patch_idx = 0
    #     for lt, rb in zip(lts, rbs):
    #         overlap = overlap
    #         x1_overlap = np.array(lt[0] - overlap).clip(0, srcw)
    #         x2_overlap = np.array(rb[0] + overlap).clip(0, srcw)
    #         y1_overlap = np.array(lt[1] - overlap).clip(0, srch)
    #         y2_overlap = np.array(rb[1] + overlap).clip(0, srch)
    #         # 深拷贝原图区块
    #         img_patch = img[y1_overlap:y2_overlap, x1_overlap:x2_overlap, :].copy()
    #         # 深拷贝mask区块(重要)
    #         msk_patch = msk[y1_overlap:y2_overlap, x1_overlap:x2_overlap].copy()
    #
    #         mask_patch_and_id_vec = []
    #         for id_ in names2ids.values():
    #             patch_mask = np.array(msk_patch == id_, dtype=np.uint8)
    #             if np.any(patch_mask):
    #                 mask_patch_and_id_vec.append((patch_mask, id_))
    #
    #         save_patch_and_txt(img_patch, mask_patch_and_id_vec, area_thres,
    #                            path_to_img_patches, path_to_txt_patches, image.split(".")[0], patch_idx, task)
    #         patch_idx += 1


if __name__ == '__main__':
    # try:
        image_path = rf"F:\PUCPDataSet\Seg0516\A\image"
        json_path = rf"F:\PUCPDataSet\Seg0516\A\Seg"
        output_path = r"output1111"
        patch_h = 2600
        patch_w = 2600
        # names2ids = {"Dent": 1, "Scratch": 2, "Contamination": 3, "Pitting": 4, "Dust": 5, "HairScratch": 6}
        names2ids = {"Dent": 1, "Scratch": 2, "Contamination": 3, "Discoloration": 4, "Pitting": 5, "Dust": 6,"HairScratch": 7, "Fiber": 8}  # 从1开始
        area_thres = 10
        overlap = 400
        image_json_dynamic_slice(image_path=image_path, json_path=json_path, output_path=output_path, patch_h=patch_h,
                                 patch_w=patch_w, names2ids=names2ids, area_thres=area_thres,overlap=overlap)
    # except Exception as exp:
    #     pass
    #     # with open('log.txt', "a") as f:
    #     #     f.writelines(exp + '\n')
import tkinter as tk
import tkinter.font as tkFont
from deploy_overlap import image_json_dynamic_slice


class App:
    def __init__(self, root):
        #setting title
        root.title("undefined")
        #setting window size
        width=685
        height=494
        screenwidth = root.winfo_screenwidth()
        screenheight = root.winfo_screenheight()
        alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
        root.geometry(alignstr)
        root.resizable(width=False, height=False)

        self.GLineEdit_609=tk.Entry(root)
        self.GLineEdit_609["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_609["font"] = ft
        self.GLineEdit_609["fg"] = "#333333"
        self.GLineEdit_609["justify"] = "center"
        self.GLineEdit_609_content = tk.StringVar()
        self.GLineEdit_609["textvariable"] = self.GLineEdit_609_content
        self.GLineEdit_609.place(x=160,y=30,width=287,height=30)

        GButton_201=tk.Button(root)
        GButton_201["bg"] = "#f0f0f0"
        ft = tkFont.Font(family='Times',size=10)
        GButton_201["font"] = ft
        GButton_201["fg"] = "#000000"
        GButton_201["justify"] = "center"
        GButton_201["text"] = "开始"
        GButton_201.place(x=500,y=380,width=135,height=49)
        GButton_201["command"] = self.GButton_201_command

        GLabel_825=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_825["font"] = ft
        GLabel_825["fg"] = "#333333"
        GLabel_825["justify"] = "center"
        GLabel_825["text"] = "图像目录"
        GLabel_825.place(x=40,y=30,width=70,height=25)

        GLabel_478=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_478["font"] = ft
        GLabel_478["fg"] = "#333333"
        GLabel_478["justify"] = "center"
        GLabel_478["text"] = "分割标注目录"
        GLabel_478.place(x=40,y=80,width=81,height=30)

        self.GLineEdit_786=tk.Entry(root)
        self.GLineEdit_786["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_786["font"] = ft
        self.GLineEdit_786["fg"] = "#333333"
        self.GLineEdit_786["justify"] = "center"
        self.GLineEdit_786_content = tk.StringVar()
        self.GLineEdit_786["textvariable"] = self.GLineEdit_786_content
        self.GLineEdit_786.place(x=160,y=80,width=288,height=30)

        GLabel_435=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_435["font"] = ft
        GLabel_435["fg"] = "#333333"
        GLabel_435["justify"] = "center"
        GLabel_435["text"] = "结果输出目录"
        GLabel_435.place(x=40,y=130,width=79,height=30)

        self.GLineEdit_503=tk.Entry(root)
        self.GLineEdit_503["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_503["font"] = ft
        self.GLineEdit_503["fg"] = "#333333"
        self.GLineEdit_503["justify"] = "center"
        self.GLineEdit_503_content = tk.StringVar()
        self.GLineEdit_503["textvariable"] = self.GLineEdit_503_content
        self.GLineEdit_503.place(x=160,y=130,width=286,height=30)

        GLabel_0=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_0["font"] = ft
        GLabel_0["fg"] = "#333333"
        GLabel_0["justify"] = "center"
        GLabel_0["text"] = "切片高"
        GLabel_0.place(x=40,y=180,width=70,height=25)

        self.GLineEdit_994=tk.Entry(root)
        self.GLineEdit_994["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_994["font"] = ft
        self.GLineEdit_994["fg"] = "#333333"
        self.GLineEdit_994["justify"] = "center"
        self.GLineEdit_994_content = tk.StringVar()
        self.GLineEdit_994["textvariable"] = self.GLineEdit_994_content
        self.GLineEdit_994.place(x=160,y=180,width=158,height=30)

        GLabel_469=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_469["font"] = ft
        GLabel_469["fg"] = "#333333"
        GLabel_469["justify"] = "center"
        GLabel_469["text"] = "切片宽"
        GLabel_469.place(x=40,y=220,width=70,height=25)

        self.GLineEdit_597=tk.Entry(root)
        self.GLineEdit_597["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_597["font"] = ft
        self.GLineEdit_597["fg"] = "#333333"
        self.GLineEdit_597["justify"] = "center"
        self.GLineEdit_597_content = tk.StringVar()
        self.GLineEdit_597["textvariable"] = self.GLineEdit_597_content
        self.GLineEdit_597.place(x=160,y=220,width=160,height=30)

        GLabel_213=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_213["font"] = ft
        GLabel_213["fg"] = "#333333"
        GLabel_213["justify"] = "center"
        GLabel_213["text"] = "名称序号字典"
        GLabel_213.place(x=40,y=260,width=90,height=30)

        self.GLineEdit_82=tk.Entry(root)
        self.GLineEdit_82["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_82["font"] = ft
        self.GLineEdit_82["fg"] = "#333333"
        self.GLineEdit_82["justify"] = "center"
        self.GLineEdit_82_content = tk.StringVar()
        self.GLineEdit_82["textvariable"] = self.GLineEdit_82_content
        self.GLineEdit_82.place(x=160,y=260,width=291,height=30)

        GLabel_431=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_431["font"] = ft
        GLabel_431["fg"] = "#333333"
        GLabel_431["justify"] = "center"
        GLabel_431["text"] = "过滤阈值"
        GLabel_431.place(x=40,y=310,width=70,height=25)

        self.GLineEdit_270=tk.Entry(root)
        self.GLineEdit_270["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_270["font"] = ft
        self.GLineEdit_270["fg"] = "#333333"
        self.GLineEdit_270["justify"] = "center"
        self.GLineEdit_270_content = tk.StringVar()
        self.GLineEdit_270["textvariable"] = self.GLineEdit_270_content
        self.GLineEdit_270.place(x=160,y=310,width=165,height=30)


        GLabel_324=tk.Label(root)
        ft = tkFont.Font(family='Times',size=10)
        GLabel_324["font"] = ft
        GLabel_324["fg"] = "#333333"
        GLabel_324["justify"] = "center"
        GLabel_324["text"] = "Overlap"
        GLabel_324.place(x=40,y=360,width=70,height=25)

        self.GLineEdit_326=tk.Entry(root)
        self.GLineEdit_326["borderwidth"] = "1px"
        ft = tkFont.Font(family='Times',size=10)
        self.GLineEdit_326["font"] = ft
        self.GLineEdit_326["fg"] = "#333333"
        self.GLineEdit_326["justify"] = "center"
        self.GLineEdit_326_content = tk.StringVar()
        self.GLineEdit_326["textvariable"] = self.GLineEdit_326_content
        self.GLineEdit_326.place(x=160,y=360,width=235,height=30)

    def GButton_201_command(self):
        input1 = self.GLineEdit_609_content.get()
        input2 = self.GLineEdit_786_content.get()
        input3 = self.GLineEdit_503_content.get()
        input4 = self.GLineEdit_994_content.get()
        input5 = self.GLineEdit_597_content.get()
        input6 = self.GLineEdit_82_content.get()
        input7 = self.GLineEdit_270_content.get()
        input8 = self.GLineEdit_270_content.get()
        print(input1)
        print(input2)
        print(input3)
        print(input4)
        print(input5)
        print(input6)
        print(input7)
        image_json_dynamic_slice(input1,input2,input3,int(input4),int(input5),eval(input6),int(input7),int(input8))


if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    root.mainloop()

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值