Python应用基础-根据指定文件生成XML

    因项目需要根据指定格式的文件生成XML标注文件,可以方便使用LabelImg打开进行编辑和查看。其原始文件默认使用逗号进行分隔,如下所示:


image.png
  • 第1个值:原始图片中切图小文件,以AIpng_x,其中x代表原始图片的第几个切图文件
  • 第2~5值:分别对应于ymin, xmin, ymax, xmax
  • 第6个值:代表对应的标签标注

    在生成XML文件时,需要对其进行汇总,即将属于同一个原始文件的切图小文件的标注汇总到一起,其实现代码如下所示:

import os
from Logger import MyLogger
from xml.dom.minidom import Document,parse
from collections import defaultdict
import re

class OpeateXML:

    def __init__(self, srcPath: str, targetPath: str, srcFileName: str):
        self._srcPath = srcPath
        self._targetPath = targetPath
        self._srcFileName = srcFileName

    def readSrcFileName(self, fileEncoding="utf8") -> defaultdict:
        data = defaultdict(list)
        s = re.compile("\.AIpng_\d{1,}", re.IGNORECASE)
        srcFileFullPath = os.path.join(self._srcPath, self._srcFileName)
        try:
            if os.path.exists(srcFileFullPath):
                with open(srcFileFullPath, mode="r", encoding=fileEncoding, errors="ignore") as fr:
                    for content in fr.readlines():
                        data[s.sub(".AIpng",content.strip().split(",")[0])].append(content.strip())
        except Exception as ex:
            MyLogger().error(f"OperateXML:read file error:\n{ex}")
            return {}
        else:
            # data.sort(key=lambda x: x.strip().split(",")[0])
            return data

    def getCreateXMLData(self,srcData:dict,mnlData:list)->defaultdict:
        """
        获取手动确认的图片
        srcData:Location.txt中的原始数据
        mnlData:手动确认数据
        """
        try:
            for key,values in srcData.items():
                for item in mnlData:
                    for v in values:
                        if item in v.strip().split(",")[0]:
                            srcData[key][srcData[key].index(v)]=srcData[key][srcData[key].index(v)].replace("auto","mnl")
        except Exception as ex:
            MyLogger().error(f"OperateXML: get data from location and mnldata interaction error\n{ex}")
            return {}
        else:
            return srcData

    def operateXML(self,data:defaultdict)->None:
        for k in data.keys():
            xmlFileFullPath = os.path.join(self._targetPath, os.path.splitext(k)[0]+".xml")
            if os.path.exists(xmlFileFullPath):
                self.appendExistXML(data={k:data[k]},xmlFileFullPath=xmlFileFullPath)
            else:
                self.createNewXML({k:data[k]})

    def appendExistXML(self,data:defaultdict,xmlFileFullPath:str,fileEncoding="utf8"):
        try:
            doc = parse(xmlFileFullPath)
            rootNode = doc.documentElement
            # print(rootNode.nodeName)
            key=rootNode.getElementsByTagName("filename")[0].childNodes[0].data
            objs=rootNode.getElementsByTagName("object")
            for obj in objs:
               name=obj.getElementsByTagName("name")[0].childNodes[0].data
               bndboxs = obj.getElementsByTagName("bndbox")
               for bndbox in bndboxs:
                   xmin = bndbox.getElementsByTagName("xmin")[0].childNodes[0].data
                   ymin = bndbox.getElementsByTagName("ymin")[0].childNodes[0].data
                   xmax = bndbox.getElementsByTagName("xmax")[0].childNodes[0].data
                   ymax = bndbox.getElementsByTagName("ymax")[0].childNodes[0].data
               existData=f"existData,{ymin},{xmin},{ymax},{xmax},{name}"
               data[key].append(existData)
            data[key]=list(set(data[key]))
            os.remove(xmlFileFullPath)
            self.createNewXML(data=data)
        except Exception as ex:
            MyLogger().error(f"OperateXML:append content to {xmlFileFullPath} error\n{ex}")
            return

    def createNewXML(self, data: dict, fileEncoding="utf8")->None:
        """
        data:传入的数据字典
        fileEncoding:XML默认编码格式
        """
        if data:
            try:
                for k,v in data.items():
                    doc = Document()
                    # 创建根节点
                    rootNode = doc.createElement("annotation")
                    # 添加根节点
                    doc.appendChild(rootNode)

                    folder = doc.createElement("folder")
                    folderText = doc.createTextNode(self._targetPath)
                    folder.appendChild(folderText)
                    rootNode.appendChild(folder)

                    filename = doc.createElement("filename")
                    filenameText = doc.createTextNode(k)
                    filename.appendChild(filenameText)
                    rootNode.appendChild(filename)

                    path = doc.createElement("path")
                    pathText = doc.createTextNode(os.path.join(self._targetPath,k))
                    path.appendChild(pathText)
                    rootNode.appendChild(path)
                    for i in v:
                        tmpData = i.strip().split(",")
                        if len(tmpData) == 6:
                            _, ymin, xmin, ymax, xmax, labelName = tmpData

                            if not labelName.__contains__("/"):
                                continue

                            objectObj = doc.createElement("object")
                            rootNode.appendChild(objectObj)

                            objectName = doc.createElement("name")
                            objectNameText = doc.createTextNode(labelName)
                            objectName.appendChild(objectNameText)
                            objectObj.appendChild(objectName)

                            objectBndBox = doc.createElement("bndbox")
                            objectObj.appendChild(objectBndBox)

                            objectBndBoxXmin = doc.createElement("xmin")
                            objectBndBoxYmin = doc.createElement("ymin")
                            objectBndBoxXmax = doc.createElement("xmax")
                            objectBndBoxYmax = doc.createElement("ymax")

                            objectBndBoxXminText = doc.createTextNode(xmin)
                            objectBndBoxYminText = doc.createTextNode(ymin)
                            objectBndBoxXmaxText = doc.createTextNode(xmax)
                            objectBndBoxYmaxText = doc.createTextNode(ymax)

                            objectBndBox.appendChild(objectBndBoxXmin)
                            objectBndBox.appendChild(objectBndBoxYmin)
                            objectBndBox.appendChild(objectBndBoxXmax)
                            objectBndBox.appendChild(objectBndBoxYmax)

                            objectBndBoxXmin.appendChild(objectBndBoxXminText)
                            objectBndBoxYmin.appendChild(objectBndBoxYminText)
                            objectBndBoxXmax.appendChild(objectBndBoxXmaxText)
                            objectBndBoxYmax.appendChild(objectBndBoxYmaxText)

                            objectObj.appendChild(objectBndBox)
                        else:
                            continue
                    # save xml
                    xmlName=os.path.splitext(k)[0]+".xml"
                    targetPath = os.path.join(self._targetPath, xmlName)
                    with open(targetPath, mode="w", encoding=fileEncoding,errors="ignore") as fw:
                        doc.writexml(fw, indent="\t", newl="\n", addindent="\t", encoding=fileEncoding)
            except Exception as ex:
                MyLogger().error(f"OperateXML:Save xml error\n{ex}")
                return

if __name__ == '__main__':
    srcPath = r"C:\Users\Surpass\Documents\PycharmProjects\data\TEST-8\outs"
    srcName = "locations.txt"
    targetPath = r"C:\Users\Surpass\Documents\PycharmProjects\data\TEST-8\outs\in_number"
    operateXML = OpeateXML(srcPath, targetPath, srcName)
    a = operateXML.readSrcFileName()
    testData=['slide1_cell420_image0_met.AIpng_36.jpg', 'slide1_cell420_image0_met.AIpng_33.jpg', 'slide1_cell420_image0_met.AIpng_10.jpg', 'slide1_cell420_image0_met.AIpng_30.jpg']
    res=operateXML.getCreateXMLData(a,testData)
    operateXML.operateXML(res)

    最终生成的XML效果如下所示:

image.png

    在LabelImg中的效果如下所示:


image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容