Exemplo n.º 1
0
class GUI(EasyGui):
    def __init__(self, Dataset_Name):
        """
        用于生成全局GUI,GUI只负责生成一个配置文件,之后的逻辑处理还是交给opencv进行处理
        """
        super().__init__()
        #1:参数初始化
        self.Dataset_Name = Dataset_Name
        self.mission_name = tk.StringVar()

        #2:文件路径初始化
        #主目录定义
        ROOT_DIR = os.path.dirname(
            os.path.realpath(__file__))  #ROOT_DIR是lib的文件路径
        self.target_file = ROOT_DIR + "/../../" + str(
            self.Dataset_Name)  #总文件夹指定
        self.target_file = os.path.abspath(self.target_file)

        #生成原数据集路径
        self.dataset_path = self.target_file + "/dataset"  #用于之后图片路径生成
        self.images_path = self.dataset_path + "/images"
        self.annotations_path = self.dataset_path + "/image_annotations"

        #生成aug数据集路径
        self.aug_path = self.target_file + "/aug"
        self.aug_images_path = self.aug_path + "/images"
        self.aug_annotations_path = self.aug_path + "/image_annotations"

        #如果是一个新的项目,则生成所有需要文件
        if not os.path.exists(self.target_file):
            os.mkdir(self.target_file)
            #存放原图的位置
            os.mkdir(self.dataset_path)
            os.mkdir(self.images_path)
            os.mkdir(self.annotations_path)

            #存放数据增强的位置
            os.mkdir(self.aug_path)
            os.mkdir(self.aug_images_path)
            os.mkdir(self.aug_annotations_path)

        else:
            print("已经存在{}数据集,基于这个进行处理".format(self.Dataset_Name))

        #绘制GUI,初始化bbox生成器
        self.drawGUI()

        #功能类初始化
        self.dataAugment = DataAugment(dataset_path=self.dataset_path,
                                       aug_path=self.aug_path)
        self.dataProcesser = DataProcesser(dataset_path=self.dataset_path)
        self.generateDataset = GenerateDataset(self.aug_path)  #生成数据集依赖于增强的数据结果
        self.annotation = Annotation(self.Dataset_Name)

    def beginMission(self, mission_name):
        self.mission_name.set(mission_name)
        self.Mission_Manager()

    def setMissionButton(self,
                         text="",
                         mission_name="",
                         width=1,
                         row=None,
                         column=0,
                         columnspan=1):
        """
        生成一个启动任务的按键
        :param text: 按键名称
        :param mission_name: 任务名称
        :param width: Button宽度
        :param row: Button所在行
        :param column: Button所在列
        :return:
        """

        if row is None:
            row = self.writing_line
        b = tk.Button(self.father_windows,
                      text=text,
                      width=width,
                      command=lambda: self.beginMission(mission_name),
                      font=self.font)
        b.grid(row=row, column=column, columnspan=columnspan)

        return b

    def drawGUI(self):
        """
        进行GUI的绘制工作
        :return:
        """
        #GUI名称
        self.setWindowName("数据标注软件2.0")
        self.writing_line = 0

        #确定数据来源
        self.setLabel('数据源类型', width=10, column=0, columnspan=5)  #选择数据来源
        self.dataType = tk.StringVar()
        self.setRadiobutton(text='已有数据集',
                            value='image',
                            var=self.dataType,
                            column=6,
                            columnspan=5)  #两个圆圈选择,将image赋值给dataType
        self.setRadiobutton(text='视频',
                            value='video',
                            var=self.dataType,
                            column=12,
                            columnspan=2)
        self.setLabel('采样间隔', width=8, column=15, columnspan=4)
        self.samplingEntry = self.setEntry(width=2, column=20,
                                           columnspan=2)  #采样间隔输入
        self.samplingEntry.insert('end', 10)  #默认采样率为10

        self.new_line()
        self.setLabel('数据源路径', width=10, column=0, columnspan=5)  #数据源路径输入
        self.dataPath = self.setEntry(width=20, column=6, columnspan=10)
        self.setMissionButton(text="选择路径",
                              mission_name="select_file",
                              width=8,
                              column=17,
                              columnspan=4)
        self.new_line()
        self.setMissionButton(text="生成图片",
                              mission_name="begin_videotopictures",
                              width=8,
                              column=10,
                              columnspan=4)
        self.add_line()

        #标注方式
        self.new_line()
        self.setLabel("标注方式", width=8, column=0, columnspan=4)
        self.setLabel("目标跟踪", width=8, column=5, columnspan=4)
        self.TrackCar = tk.StringVar()
        #目标跟踪只有 有和没有的区别,有的话统一采用KCF
        self.setCheckButton(text="使用跟踪",
                            var=self.TrackCar,
                            column=10,
                            columnspan=5)

        self.new_line()
        #目标检测分为车子和装甲板
        self.DetectCar = tk.StringVar()
        self.DetectArmor = tk.StringVar()
        self.setLabel("目标检测", width=8, column=5, columnspan=4)
        self.setCheckButton(text="识别车子",
                            var=self.DetectCar,
                            column=10,
                            columnspan=5)  #是否识别车子
        self.setCheckButton(text="识别装甲板",
                            var=self.DetectArmor,
                            column=16,
                            columnspan=5)  #是否识别装甲板
        self.new_line()
        #是否展示标注信息
        self.flag_show_annotation = tk.StringVar()
        self.setRadiobutton(text="显示已标注",
                            value="True",
                            var=self.flag_show_annotation,
                            column=10,
                            columnspan=4)
        self.setRadiobutton(text="不显示",
                            value="False",
                            var=self.flag_show_annotation,
                            column=15,
                            columnspan=3)
        self.new_line()
        self.setMissionButton("开始标注",
                              mission_name="begin_annotation",
                              width=8,
                              column=10,
                              columnspan=4)
        self.add_line()

        #数据增强方式
        self.new_line()
        self.Augment_color = tk.StringVar()
        self.Augment_scale = tk.StringVar()
        self.setLabel("数据增强", width=8, column=0, columnspan=4)
        self.setCheckButton(text="亮暗操作",
                            var=self.Augment_color,
                            column=5,
                            columnspan=5)
        self.setCheckButton(text="目标增多",
                            var=self.Augment_scale,
                            column=11,
                            columnspan=5)

        self.new_line()
        self.setMissionButton(text="开始增强",
                              mission_name="begin_dataaugment",
                              width=8,
                              column=10,
                              columnspan=4)
        self.add_line()

        #不同数据格式生成
        self.new_line()
        self.setLabel("数据格式生成类型", width=16, column=0, columnspan=8)
        self.GenerateDatesetType = tk.StringVar()
        self.setRadiobutton(text="txt",
                            value="txt",
                            var=self.GenerateDatesetType,
                            column=9,
                            columnspan=3)  #目标跟踪只有一个算法
        self.setRadiobutton(text="xml",
                            value="xml",
                            var=self.GenerateDatesetType,
                            column=13,
                            columnspan=3)
        self.setRadiobutton(text="json",
                            value="json",
                            var=self.GenerateDatesetType,
                            column=17,
                            columnspan=3)
        self.setRadiobutton(text="csv",
                            value="csv",
                            var=self.GenerateDatesetType,
                            column=21,
                            columnspan=3)
        self.new_line()
        self.setMissionButton(text="生成文件",
                              mission_name="begin_makedata",
                              width=8,
                              column=10,
                              columnspan=4)
        self.add_line()

        #数据清洗
        self.new_line()
        self.setLabel("数据清洗", width=16, column=0, columnspan=8)
        self.add_line()

        #消息框
        self.new_line()
        self.setLabel("信息输出框", width=10, column=8, columnspan=10)
        self.new_line()
        self.messageText = self.setText(width=44,
                                        height=10,
                                        column=0,
                                        columnspan=22)

        #窗口状态栏
        menubar = tk.Menu(self.father_windows)
        seeResultMenu = tk.Menu()
        seeResultMenu.add_command(
            label="查看原始数据",
            command=lambda: self.beginMission("begin_seeresult_o"))
        seeResultMenu.add_command(
            label="查看增强数据",
            command=lambda: self.beginMission("begin_seeresult_a"))

        menubar.add_cascade(label="查看标注数据", menu=seeResultMenu)
        self.father_windows.config(menu=menubar)

    def printText(self, info_output):
        """
        用于在GUI界面中进行信息输出
        :return:
        """
        self.messageText.insert('end', info_output + "\n")

    def Mission_Manager(self):
        """
        这个类用于进行多个任务管理,由这个产生任务,然后调用不同的类进行执行
        :return:
        """
        mission = self.mission_name.get()
        if mission == "select_file":
            self.beginGetDataPath()

        elif mission == "begin_videotopictures":
            Thread_videoToPictures = threading.Thread(
                target=self.beginToPicture)
            Thread_videoToPictures.run()

        elif mission == "begin_annotation":
            Thread_Annotation = threading.Thread(
                target=self.beginAnnotation)  #,name="annotation")
            Thread_Annotation.run()

        elif mission == "begin_dataaugment":
            Thread_DataAugment = threading.Thread(
                target=self.beginDataAugment())  #,name="dataaugment")
            Thread_DataAugment.run()

        elif mission == "begin_makedata":
            Thread_MakeData = threading.Thread(target=self.beginMakeData)
            Thread_MakeData.run()

        elif mission[:-2] == "begin_seeresult":
            Thread_SeeResult = threading.Thread(target=self.beginSeeResult)
            Thread_SeeResult.run()

        elif mission == "stop":
            print("这个功能等黄哥哥回来加")

        else:
            print("你这个按键还没有定义功能")

    def beginGetDataPath(self):
        """
        获取图片路径,需要提前确定数据源是image还是video
        :return:
        """
        self.printText("执行任务: 文件路径选择")
        self.dataPath.delete(0, 'end')  #把上一次路径信息进行删除

        if self.dataType.get() == 'image':
            path = filedialog.askdirectory()  #要求是文件夹

        elif self.dataType.get() == 'video':
            path = filedialog.askopenfilename()  #要求是图片名称

        else:
            self.printText("  Warning:请先选择数据源类型")
            return
        self.dataPath.insert(0, path)  #最后最开始插入选择的路径
        self.printText("!完成数据选择!")
        self.printText(" ")

    def beginToPicture(self):
        """
        用于基于视频生成图片的函数
        所有的图片生成在了/Dataset/images中
        包含了视频到Picture和文件路径到Picture
        :return:
        """
        #1:基于视频路径开始进行处理
        self.printText("执行任务: 视频转图片")
        data_path = self.dataPath.get()  #获取数据源路径
        sample_rate = int(self.samplingEntry.get())  #获取采样数目

        #2:当目标是文件夹的时,导入文件夹图片
        if os.path.isdir(data_path):

            new_images_path = data_path + "/images"
            new_annotations_path = data_path + "/image_annotations"
            assert os.path.exists(new_images_path), "图片路径不存在,不进行执行"
            assert os.path.exists(new_annotations_path), "标注路径不存在,不进行执行"
            self.printText("  送入的是文件夹,将其内部的文件进行导入")
            self.dataProcesser.File_To_Picture(new_dataset_path=data_path)

        #3:当目标是视频时,基于采样频率进行视频导入
        else:
            #3.1:确定导入路径
            images_path = self.dataset_path + "/images"
            annotations_path = self.dataset_path + "/image_annotations"

            #3.2:基于目标文件夹导入数据集
            if not os.path.exists(self.dataset_path):  #不存在数据集
                self.printText("  没有已经存在的图片,自动生成在了{}".format(
                    self.dataset_path))
                os.mkdir(self.dataset_path)
                os.mkdir(images_path)
                os.mkdir(annotations_path)
                begin_number = 0

            else:  #存在数据集
                self.printText("  已经存在文件,将会在后面继续生成")
                list_image = os.listdir(images_path)
                if len(list_image) == 0:
                    begin_number = 0
                else:
                    list_image.sort(key=lambda x: int(x[:-4]))
                    begin_number = int(list_image[-1][:-4]) + 1
            self.dataProcesser.Video_To_Picture(data_path, images_path,
                                                sample_rate, begin_number)

        self.printText("!完成视频转图片任务!")
        self.printText(" ")  #完成一次生成图片,换行

    def beginAnnotation(self):
        """
        用于标注任务
        :return:
        """
        self.printText("执行任务: 开始标注")
        flag_trackcar = self.TrackCar.get()
        flag_detectcar = self.DetectCar.get()
        flag_detectarmor = self.DetectArmor.get()
        flag_show_annotation = self.flag_show_annotation.get()

        if not os.path.exists(self.dataset_path):
            self.printText(" 请创建路径文件夹")
            return

        if flag_show_annotation == "":
            self.printText('  请选择是否展示已标注的内容')
            return

        annotation_info = {
            "dataset_path": self.dataset_path,
            "flag_trackcar": flag_trackcar,
            "flag_detectcar": flag_detectcar,
            "flag_detectarmor": flag_detectarmor,
            "flag_show_annotation": flag_show_annotation
        }

        self.annotation.Annotation_Mission(annotation_info)

        self.printText("完成了数据标注任务")
        self.printText(" ")
        cv.destroyAllWindows()

    def beginDataAugment(self):
        #1:先进行一次数据确保
        self.printText("执行任务: 开始数据增强")
        self.dataProcesser.checkData(dataset_path=self.dataset_path)

        #2:将数据移动到aug中
        self.dataAugment.move_origin_to_aug()

        #根据选项进行数据增强
        if self.Augment_color.get() == "True":
            self.dataAugment.aug_color_motion()
            self.printText("   完成了颜色增强")
        if self.Augment_scale.get() == "True":
            self.dataAugment.aug_more_bboxes()
            self.printText("   完成了bbox的增多")

        self.printText("!完成了数据集增强任务!")
        self.printText(" ")
        cv.destroyAllWindows()

    def beginMakeData(self):
        self.dataProcesser.checkData(dataset_path=self.aug_path)
        generateDatasettype = self.GenerateDatesetType.get()
        self.printText("执行任务: 开始生成{}数据".format(generateDatasettype))
        if generateDatasettype == "txt":
            print("需要补充txt数据格式生成")
        elif generateDatasettype == "json":
            self.generateDataset.getJsonFile()
        elif generateDatasettype == "xml":
            print("需要补充xml数据格式生成")
        elif generateDatasettype == "csv":
            self.generateDataset.getCsvFile()
        else:
            self.printText("请选择需要生成的数据格式")
            return

        self.printText("!完成数据集生成任务!")
        self.printText(" ")

    def beginSeeResult(self):
        self.printText("执行任务: 查看标注结果")
        mission = self.mission_name.get()
        if mission == "begin_seeresult_a":
            self.annotation.See_Annotation_Mission(self.aug_path)
        else:
            self.annotation.See_Annotation_Mission(self.dataset_path)