imageprocessing icon indicating copy to clipboard operation
imageprocessing copied to clipboard

Bugfix/metadata utctime error negative subsec

Open ssf-ford opened this issue 9 months ago • 2 comments

Casting f"0.self.get_time('EXIF:SubSecTime')" directly causes ValueError as the string to be casted is in the format '0.-nnnnnn'.

ssf-ford avatar Mar 22 '25 13:03 ssf-ford

Hi, when did this occur with which camera? With our MicaSense RedEdge-P this is working fine.

How are you processing the images?

maurerle avatar Jul 08 '25 16:07 maurerle

Hi,

when did this occur with which camera?

I encountered this issue in March 2025, however, the image set is quite old from RedEdge-M (the red body) camera taken in 2018. I've also included the set in my unit test commit

Note: This commit eeaf6e86d505acc74f0c5a6dab7269f5333b824c introduced the type casting f"0.self.get_time('EXIF:SubSecTime')".

How are you processing the images?

  • I cloned the repository and use the micasense code as a python module
  • I use Python 3.10.15 installed via pyenv
  • Here's the code snippet that I ran.
import micasense.capture as capture
import micasense.imageutils as imageutils


def get_image_type_and_irradiance(the_capture, panel_cap):
    """
    Determine if the output image should be "reflectance" or "radiance"
    and compute the irradiance list using panel capture if available.
    """
    if panel_cap is not None:
        if panel_cap.panel_albedo() is not None:
            panel_reflectance_by_band = panel_cap.panel_albedo()
        else:
            panel_reflectance_by_band = [0.49] * len(
                the_capture.eo_band_names()
            )  # RedEdge band_index order
        irradiance_list = panel_cap.panel_irradiance(panel_reflectance_by_band) + [0]
        # prevent negative irradiance
        irradiance_list = np.abs(irradiance_list).tolist()
        the_capture.plot_undistorted_reflectance(irradiance_list)
        return "reflectance", irradiance_list
    else:
        if the_capture.dls_present():
            irradiance_list = the_capture.dls_irradiance() + [0]
            # prevent negative irradiance
            irradiance_list = np.abs(irradiance_list).tolist()
            the_capture.plot_undistorted_reflectance(irradiance_list)
            return "reflectance", irradiance_list
        else:
            the_capture.plot_undistorted_radiance()
            return "radiance", None

def align_non_panchro_capture(
    the_capture,
    warp_matrices,
    img_type,
    match_index,
    pyramid_levels,
    max_alignment_iterations,
    warp_mode,
    regenerate=True,
    multithreaded=False,
):
    """
    For non-panchromatic cameras, align the capture using OpenCV.
    """
    st = time.time()
    # Set regenerate flag to force creation of new warp matrices (or use already loaded ones)
    if warp_matrices and not regenerate:
        print("Using existing warp matrices...")
    else:
        print("Aligning images...")
        warp_matrices, alignment_pairs = imageutils.align_capture(
            the_capture,
            ref_index=match_index,
            max_iterations=max_alignment_iterations,
            warp_mode=warp_mode,
            pyramid_levels=pyramid_levels,
            multithreaded=multithreaded,
        )

    print("Finished aligning")
    et = time.time()
    print("Alignment time:", int(et - st), "seconds")

    cropped_dimensions, edges = imageutils.find_crop_bounds(
        the_capture, warp_matrices, warp_mode=warp_mode, reference_band=match_index
    )
    print("Cropped dimensions:", cropped_dimensions)

    im_aligned = the_capture.create_aligned_capture(
        warp_matrices=warp_matrices, motion_type=warp_mode, img_type=img_type
    )
    return im_aligned, warp_matrices


main():
    image_names = [
        '/path/to/IMG_0008_1.tif',
        '/path/to/IMG_0008_2.tif',
        '/path/to/IMG_0008_3.tif',
        '/path/to/IMG_0008_4.tif',
        '/path/to/IMG_0008_5.tif'
    ]
    warp_matrices = None
    regenerate = True
    sharpened_stack = None
    warp_mode = cv2.MOTION_HOMOGRAPHY
    max_alignment_iterations = 10
    multithreaded = True

    the_capture = capture.Capture.from_filelist(image_names)
    img_type, irradiance_list = get_image_type_and_irradiance(the_capture, None)
    im_aligned, warp_matrices = align_non_panchro_capture(
          the_capture,
          warp_matrices,
          img_type,
          match_index,
          pyramid_levels,
          max_alignment_iterations,
          warp_mode,
          regenerate=regenerate,
          multithreaded=multithreaded
   )

...

ssf-ford avatar Jul 14 '25 06:07 ssf-ford