Изменение координат ограничительной рамки в файле xml в соответствии с новой шириной и высотой изображения - PullRequest
1 голос
/ 27 апреля 2020

Я пытаюсь преобразовать координаты ограничительной рамки в файле xml относительно ширины и высоты нового изображения. Пример файла xml приведен ниже:

<annotations>
 <image height="940" id="3" name="C_00080.jpg" width="1820">
  <box label="Objects" occluded="0" xbr="801.99255" xtl="777.78656" ybr="506.9955" ytl="481.82132">
   <attribute name="Class">B</attribute>
  </box>
  <box label="Objects" occluded="0" xbr="999.319" xtl="963.38654" ybr="519.2735" ytl="486.68628">
   <attribute name="Class">A</attribute>
  </box>
 </image>
<annotations>

Ширина и высота исходного изображения в xml равна 1820x940, а координаты поля совпадают. Я хочу изменить координаты поля на ширину и высоту нового изображения, равные 1080x720. Я написал этот код, может кто-нибудь помочь мне проверить или сказать мне лучший способ для кода ниже.

import xml.etree.ElementTree as ET

label_file = '1.xml'
tree = ET.parse(label_file)
root = tree.getroot()

for image in root.findall('image'):
    image.attrib['width'] = '1080'  # Original width = 1820
    image.attrib['height'] = '720'  # Original width = 940
    for allBboxes in image.findall('box'):
        xmin = float(allBboxes.attrib['xtl'])
        xminNew = float(xmin / (1820/1080))
        xminNew = float("{:.5f}".format(xminNew))
        allBboxes.attrib['xtl'] = str(xminNew)
        ymin = float(allBboxes.attrib['ytl'])
        yminNew = float(ymin / (940/720))
        yminNew = float("{:.5f}".format(yminNew))
        allBboxes.attrib['ytl'] = str(yminNew)
        xmax = float(allBboxes.attrib['xbr'])
        xmaxNew = float(xmax / (1820/1080))
        xmaxNew = float("{:.5f}".format(xmaxNew))
        allBboxes.attrib['xbr'] = str(xmaxNew)
        ymax = float(allBboxes.attrib['ybr'])
        ymaxNew = float(ymax / (940/720))
        ymaxNew = float("{:.5f}".format(ymaxNew))
        allBboxes.attrib['ybr'] = str(ymaxNew)

tree.write(label_file)

Ответы [ 2 ]

1 голос
/ 27 апреля 2020

Рассмотрим параметризованное решение XSLT с использованием стороннего модуля Python, lxml, где вы передаете новые значения ширины и высоты из Python для динамического применения формулы до XML атрибутов.

XSLT (сохранить как файл .xsl)

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes" encoding="utf-8"/>
  <xsl:strip-space elements="*"/>

  <!-- PARAMS WITH DEFAULTS -->
  <xsl:param name="new_width" select="1080"/>
  <xsl:param name="new_height" select="720"/>  

  <!-- IDENTITY TRANSFORM -->
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- WIDTH AND HEIGHT ATTRS CHANGE -->
  <xsl:template match="image">
    <xsl:copy>
      <xsl:apply-templates select="@*"/>
      <xsl:attribute name="width"><xsl:value-of select="$new_width"/></xsl:attribute>
      <xsl:attribute name="height"><xsl:value-of select="$new_height"/></xsl:attribute>
      <xsl:apply-templates select="node()"/>
    </xsl:copy>
  </xsl:template>

  <!-- X ATTRS CHANGE -->
  <xsl:template match="box/@xbr|box/@xtl">
      <xsl:variable select="ancestor::image/@width" name="curr_width"/>

      <xsl:attribute name="{name(.)}">
          <xsl:value-of select="format-number(. div ($curr_width div $new_width) , '#.00000')"/>
      </xsl:attribute>
  </xsl:template>

  <!-- Y ATTRS CHANGE -->
  <xsl:template match="box/@ybr|box/@ytl">
      <xsl:variable select="ancestor::image/@height" name="curr_height"/>

      <xsl:attribute name="{name(.)}">
          <xsl:value-of select="format-number(. div ($curr_height div $new_height), '#.00000')"/>
      </xsl:attribute>
  </xsl:template>

</xsl:stylesheet>

Python (нет for l oop или if logi c)

import lxml.etree as et

# LOAD XML AND XSL SCRIPT
xml = et.parse('Input.xml')
xsl = et.parse('Script.xsl')

# PASS PARAMETERS TO XSLT
transform = et.XSLT(xsl)
result = transform(xml, new_width = et.XSLT.strparam(str(1080)), 
                        new_height = et.XSLT.strparam(str(720)))

# SAVE RESULT TO FILE
with open("Output.xml", 'wb') as f:
    f.write(result)

Выход

<?xml version="1.0" encoding="utf-8"?>
<annotations>
  <image height="720" id="3" name="C_00080.jpg" width="1080">
    <box label="Objects" occluded="0" xbr="475.90767" xtl="461.54367" ybr="388.33698" ytl="369.05463">
      <attribute name="Class">B</attribute>
    </box>
    <box label="Objects" occluded="0" xbr="593.00248" xtl="571.67992" ybr="397.74140" ytl="372.78098">
      <attribute name="Class">A</attribute>
    </box>
  </image>
</annotations>
1 голос
/ 27 апреля 2020

Для улучшения кода вы можете:

  • вычислить крысу ios до того, как l oop
  • удалить бесполезные преобразования с плавающей точкой
  • удалить деление (деление на деление - это умножение)
  • округление числа с плавающей запятой может не потребоваться
  • группирует операторы в последовательном порядке
  • переименовывает allBoxes в box, поскольку он представляет только один блок

Вот возможный код:

import xml.etree.ElementTree as ET

label_file = '1.xml'
tree = ET.parse(label_file)
root = tree.getroot()

r_w = 1080 / 1820
r_h = 720 / 940

for image in root.findall('image'):
    image.attrib['width'] = '1080'  # Original width = 1820
    image.attrib['height'] = '720'  # Original width = 940

    for box in image.findall('box'):
        xmin = float(box.attrib['xtl'])
        ymin = float(box.attrib['ytl'])
        xmax = float(box.attrib['xbr'])
        ymax = float(box.attrib['ybr'])

        xminNew = xmin * r_w
        yminNew = ymin * r_h
        xmaxNew = xmax * r_w
        ymaxNew = ymax * r_h

        box.attrib['xtl'] = str(xminNew)
        box.attrib['ytl'] = str(yminNew)
        box.attrib['xbr'] = str(xmaxNew)
        box.attrib['ybr'] = str(ymaxNew)

tree.write(label_file)

Вы можете еще больше улучшить этот код, заключив все это в функции, чтобы улучшить удобство использования, ясность и возможное повторное использование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...