diff --git a/easyidp/data.py b/easyidp/data.py index b19f704..cf62b2d 100644 --- a/easyidp/data.py +++ b/easyidp/data.py @@ -480,6 +480,7 @@ def __init__(self, test_out="./tests/out"): * ``.metashape.wheat_param`` * ``.metashape.multichunk_psx`` * ``.metashape.multichunk_param`` + * ``.metashape.two_calib`` **pix4d test module** @@ -564,6 +565,9 @@ def __init__(self, data_dir, test_out): self.camera_disorder_psx = data_dir / "metashape" / "camera_disorder.psx" self.camera_disorder_param = data_dir / "metashape" / "camera_disorder.files" + self.two_calib_psx = data_dir / "metashape" / "two_calib.psx" + self.two_calib_param = data_dir / "metashape" / "two_calib.files" + class Pix4Dataset(): diff --git a/easyidp/metashape.py b/easyidp/metashape.py index f67faf9..5d40238 100644 --- a/easyidp/metashape.py +++ b/easyidp/metashape.py @@ -279,10 +279,11 @@ def _open_whole_project(self, project_path): chunk_id2label = {} label2chunk_id = {} + remove_chunk_id = [] for chunk_id in project_dict.keys(): chunk_dict = read_chunk_zip(folder_path, project_name, chunk_id, return_label_only=True) - if chunk_dict['enabled']: + if chunk_dict and chunk_dict['enabled']: lb = chunk_dict['label'] lb_len = len(lb) # judge if two chunks have the same label @@ -298,6 +299,11 @@ def _open_whole_project(self, project_path): chunk_id2label[chunk_id] = lb label2chunk_id[lb] = chunk_id else: # ignore the disabled chunk. + remove_chunk_id.append(chunk_id) + continue + + if len(remove_chunk_id) > 0: + for chunk_id in remove_chunk_id: project_dict.pop(chunk_id) # open the first chunk if chunk_id not given. @@ -309,12 +315,14 @@ def _open_whole_project(self, project_path): f"[{first_chunk_id}] '{chunk_id2label[first_chunk_id]}', " f"ignore the wrong chunk_id [{self.chunk_id}] specified by user.") self.chunk_id = first_chunk_id - else: # has multiple chunks + elif len(project_dict) > 1: # has multiple chunks if self.chunk_id is None: warnings.warn( f"The project has [{len(project_dict)}] chunks, however no chunk_id has been specified, " f"open the first chunk [{first_chunk_id}] '{chunk_id2label[first_chunk_id]}' by default.") self.chunk_id = first_chunk_id + else: # has zero chunk available + raise IndexError(f"Metashape project has no chunk folder (e.g. './0/', './1/') at [{folder_path}/{project_name}.files]") # save to project parameters self.project_folder = folder_path @@ -1195,6 +1203,11 @@ def read_chunk_zip(project_folder, project_name, chunk_id, skip_disabled=False, """ frame_zip_file = f"{project_folder}/{project_name}.files/{chunk_id}/chunk.zip" + # for test data, some metashape projects are not complete, then skip and return None + if not os.path.exists(frame_zip_file): + print(f'[Warning] Metashape project {project_folder} Chunk {chunk_id} folder missing') + return None + xml_str = _get_xml_str_from_zip_file(frame_zip_file, "doc.xml") xml_tree = ElementTree.fromstring(xml_str) @@ -1573,27 +1586,31 @@ def _decode_sensor_tag(xml_obj, debug_meta={}): sensor.pixel_height_unit = "mm" sensor.focal_length = float(xml_obj.findall("./property/[@name='focal_length']")[0].attrib["value"]) - calib_tag = xml_obj.findall("./calibration") - if len(calib_tag) != 1: - # load the debug info - if len(debug_meta) == 0: # not specify input - debug_meta = { - "project_folder": 'project_folder', - "project_name" : 'project_name', - "chunk_id": 'chunk_id', - "chunk_path": 'chunk_id/chunk.zip' - } + calib_tags = xml_obj.findall("./calibration") + # check if has tag + has_adjusted_tag = False + for c in calib_tags: + if c.attrib['class'] == 'adjusted': + has_adjusted_tag = True + sensor.calibration = _decode_calibration_tag(c) + sensor.calibration.sensor = sensor + break + + if len(calib_tags) != 1: + if has_adjusted_tag: + warnings.warn(f'Detect {len(calib_tags)} tags in tag, using ') + + + if not has_adjusted_tag: xml_str = minidom.parseString(ElementTree.tostring(xml_obj)).toprettyxml(indent=" ") # remove the first line and empty lines xml_str = os.linesep.join([s for s in xml_str.splitlines() if s.strip() and '?xml version=' not in s]) - warnings.warn(f"The sensor tag in [{debug_meta['chunk_path']}] has {len(calib_tag)} tags, but expected 1\n" - f"\n{xml_str}\n\nThis may cause by importing photos but delete them before align processing in metashape, " - f"and leave the 'ghost' empty sensor tag, this is just a warning and should have no effect to you") + warnings.warn( + f'No expected tag found in tag\n' + f'Problemed XML tags for debugging reference: \n{xml_str}\n') + sensor.calibration = None - else: - sensor.calibration = _decode_calibration_tag(xml_obj.findall("./calibration")[0]) - sensor.calibration.sensor = sensor return sensor diff --git a/tests/test_metashape.py b/tests/test_metashape.py index c13f1c7..6df800f 100644 --- a/tests/test_metashape.py +++ b/tests/test_metashape.py @@ -152,7 +152,6 @@ def test_class_init_metashape_multi_folder(): assert ms.photos[0].label == "[0]100MEDIA-DJI_0001" assert len(ms.photos) == 218 - def test_class_init_metashape_warns_errors(): # warning init with chunk_id without project_path with pytest.warns(UserWarning, match=re.escape("Unable to open chunk_id [0] for empty project with project_path=None")): @@ -189,6 +188,13 @@ def test_class_init_metashape_warns_errors(): roi = idp.ROI() roi['plot1'] = plot m5.back2raw(roi) + + +def test_class_init_metashape_with_missing_chunk_folders(): + m6 = idp.Metashape(project_path=test_data.metashape.two_calib_psx) + assert len(m6._chunk_id2label) == 1 + assert '0' in m6._chunk_id2label.keys() + assert 'RGB_230923_20m' in m6._chunk_id2label.values() def test_class_fetch_by_label(): @@ -582,7 +588,7 @@ def test_debug_calibration_tag_error(): for i, sensor_tag in enumerate(sensors_search): if i == 0: - with pytest.warns(UserWarning, match=re.escape("The sensor tag in [chunk_id/chunk.zip] has 0 tags")): + with pytest.warns(UserWarning, match=re.escape('No expected tag found in tag')): sensor = idp.metashape._decode_sensor_tag(sensor_tag) assert sensor.calibration is None @@ -707,4 +713,37 @@ def test_metashape_disordered_image_xml(): assert ms.photos[0].label == "80m/e-w/card01/100MEDIA/DJI_0001.JPG" - assert ms.photos[1].label == '80m/e-w/card01/101MEDIA/DJI_0538.JPG' \ No newline at end of file + assert ms.photos[1].label == '80m/e-w/card01/101MEDIA/DJI_0538.JPG' + + +def test_parse_sensor_tags_with_multiple_calibration(): + """ + + + 3713.29 + 7.0199999999999996 + -8.7200000000000006 + -0.11257523999999999 + 0.014874429999999999 + -0.027064109999999999 + 9.9999999999999995e-08 + -8.5719999999999999e-05 + + + + 4758.8543529678982 + 14.842273128715597 + -2.8329842300848433 + -0.15762331681570707 + -0.16432342601626651 + 0.43042327353025245 + -0.49918757322065133 + -0.0012357946245061236 + -0.0011496526754087306 + + """ + with pytest.warns(UserWarning, match=re.escape('Detect 2 tags in tag, using ')): + m6 = idp.Metashape(project_path=test_data.metashape.two_calib_psx) + + assert m6.sensors[0].calibration.f == 4758.8543529678982 + assert m6.sensors[0].calibration.cx == 14.842273128715597