예제 #1
0
# This notebook shows usage for the Panel class.  This type is useful for detecting MicaSense calibration panels and extracting information about the lambertian panel surface.

# In[ ]:

import os, glob
from micasense.image import Image
from micasense.panel import Panel
get_ipython().run_line_magic('matplotlib', 'inline')

imagePath = os.path.join('.', 'data', '0000SET', '000')
imageName = glob.glob(os.path.join(imagePath, 'IMG_0000_1.tif'))[0]

img = Image(imageName)
panel = Panel(img)

if not panel.panel_detected():
    raise IOError("Panel Not Detected!")

print("Detected panel serial: {}".format(panel.serial))
mean, std, num, sat_count = panel.raw()
print("Extracted Panel Statistics:")
print("Mean: {}".format(mean))
print("Standard Deviation: {}".format(std))
print("Panel Pixel Count: {}".format(num))
print("Saturated Pixel Count: {}".format(sat_count))

panel.plot()

# ---
# Copyright (c) 2017-2018 MicaSense, Inc.  For licensing information see the [project git repository](https://github.com/micasense/imageprocessing)
def run():
    import sys
    from micasense.capture import Capture
    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    import micasense.imageutils as imageutils
    import micasense.plotutils as plotutils
    import argparse
    import os, glob
    from multiprocessing import Process, freeze_support
    import imutils
    import statistics
    import matplotlib.pyplot as plt
    from micasense.image import Image
    from micasense.panel import Panel
    import micasense.utils as msutils
    import csv
    import pickle

    freeze_support()

    ap = argparse.ArgumentParser()
    ap.add_argument(
        "-l",
        "--log_file_path",
        required=False,
        help=
        "file path to write log to. useful for using from the web interface")
    ap.add_argument(
        "-a",
        "--image_path",
        required=False,
        help=
        "image path to directory with all images inside of it. useful for using from command line. e.g. /home/nmorales/MicasenseTest/000. NOTE: a temp folder will be created within this directory"
    )
    ap.add_argument(
        "-b",
        "--file_with_image_paths",
        required=False,
        help=
        "file path to file that has all image file names and temporary file names for each image in it, comma separated and separated by a newline. useful for using from the web interface. e.g. /home/nmorales/myfilewithnames.txt"
    )
    ap.add_argument(
        "-c",
        "--panel_image_path",
        required=False,
        help=
        "image path to directory with all 5 panel images inside of it. useful for using from command line. e.g. /home/nmorales/MicasenseTest/000"
    )
    ap.add_argument(
        "-d",
        "--file_with_panel_image_paths",
        required=False,
        help=
        "file path to file that has all image file names in it, separated by a newline. useful for using from the web interface. e.g. /home/nmorales/myfilewithnames.txt"
    )
    ap.add_argument(
        "-o",
        "--output_path",
        required=True,
        help=
        "output path to directory in which all resulting files will be placed. useful for using from the command line"
    )
    ap.add_argument("-y",
                    "--final_rgb_output_path",
                    required=True,
                    help="output file path for stitched RGB image")
    ap.add_argument("-z",
                    "--final_rnre_output_path",
                    required=True,
                    help="output file path for stitched RNRe image")
    ap.add_argument(
        "-p",
        "--output_path_band1",
        required=True,
        help=
        "output file path in which resulting band 1 will be placed. useful for using from the web interface"
    )
    ap.add_argument(
        "-q",
        "--output_path_band2",
        required=True,
        help=
        "output file path in which resulting band 2 will be placed. useful for using from the web interface"
    )
    ap.add_argument(
        "-r",
        "--output_path_band3",
        required=True,
        help=
        "output file path in which resulting band 3 will be placed. useful for using from the web interface"
    )
    ap.add_argument(
        "-s",
        "--output_path_band4",
        required=True,
        help=
        "output file path in which resulting band 4 will be placed. useful for using from the web interface"
    )
    ap.add_argument(
        "-u",
        "--output_path_band5",
        required=True,
        help=
        "output file path in which resulting band 5 will be placed. useful for using from the web interface"
    )
    ap.add_argument(
        "-n",
        "--number_captures",
        required=False,
        help="When you want to test using only a subset of images.")
    ap.add_argument(
        "-k",
        "--thin_images",
        required=False,
        help=
        "When you have too many images, specify a number of images to skip. e.g. 1 will only use every other image, 2 will use every third image, 3 will use every fourth image."
    )
    ap.add_argument(
        "-w",
        "--work_megapix",
        required=False,
        help="Resolution for image registration step. The default is 0.6 Mpx")
    ap.add_argument(
        "-x",
        "--ba_refine_mask",
        required=False,
        default='xxxxx',
        help=
        "Set refinement mask for bundle adjustment. It looks like 'x_xxx' where 'x' means refine respective parameter and '_' means don't refine one, and has the following format: <fx><skew><ppx><aspect><ppy>. The default mask is 'xxxxx'. If bundle adjustment doesn't support estimation of selected parameter then the respective flag is ignored."
    )
    args = vars(ap.parse_args())

    log_file_path = args["log_file_path"]
    image_path = args["image_path"]
    file_with_image_paths = args["file_with_image_paths"]
    panel_image_path = args["panel_image_path"]
    file_with_panel_image_paths = args["file_with_panel_image_paths"]
    output_path = args["output_path"]
    final_rgb_output_path = args["final_rgb_output_path"]
    final_rnre_output_path = args["final_rnre_output_path"]
    output_path_band1 = args["output_path_band1"]
    output_path_band2 = args["output_path_band2"]
    output_path_band3 = args["output_path_band3"]
    output_path_band4 = args["output_path_band4"]
    output_path_band5 = args["output_path_band5"]
    thin_images = args["thin_images"]
    if thin_images is not None:
        thin_images = int(thin_images)
    number_captures = args["number_captures"]
    if number_captures is not None:
        number_captures = int(number_captures)
    work_megapix = args["work_megapix"]
    ba_refine_mask = args["ba_refine_mask"]

    if sys.version_info[0] < 3:
        raise Exception("Must use Python3. Use python3 in your command line.")

    if log_file_path is not None:
        sys.stderr = open(log_file_path, 'a')

    def eprint(*args, **kwargs):
        print(*args, file=sys.stderr, **kwargs)

    #Must supply either image_path or file_with_image_paths as a source of images
    imageNamesAll = []
    imageTempNames = []
    tempImagePath = None
    if image_path is not None:

        tempImagePath = os.path.join(image_path, 'temp')
        if not os.path.exists(tempImagePath):
            os.makedirs(tempImagePath)

        imageNamesAll = glob.glob(os.path.join(image_path, '*.tif'))
        for idx, val in enumerate(imageNamesAll):
            imageTempNames.append(
                os.path.join(tempImagePath, 'temp' + str(idx) + '.tif'))

    elif file_with_image_paths is not None:
        with open(file_with_image_paths) as fp:
            for line in fp:
                imageName, tempImageName = line.strip().split(",")
                imageNamesAll.append(imageName)
                imageTempNames.append(tempImageName)
    else:
        if log_file_path is not None:
            eprint(
                "No input images given. use image_path OR file_with_image_paths args"
            )
        else:
            print(
                "No input images given. use image_path OR file_with_image_paths args"
            )
        os._exit

    panelBandCorrection = {}
    panelNames = []
    if panel_image_path is not None:
        panelNames = glob.glob(os.path.join(panel_image_path, '*.tif'))
    elif file_with_panel_image_paths is not None:
        with open(file_with_panel_image_paths) as fp:
            for line in fp:
                imageName = line.strip()
                panelNames.append(imageName)
    else:
        if log_file_path is not None:
            eprint(
                "No panel input images given. use panel_image_path OR file_with_panel_image_paths args"
            )
        else:
            print(
                "No panel input images given. use panel_image_path OR file_with_panel_image_paths args"
            )
        #os._exit

    for imageName in panelNames:
        img = Image(imageName)
        band_name = img.band_name
        if img.auto_calibration_image:
            if log_file_path is not None:
                eprint("Found automatic calibration image")
            else:
                print("Found automatic calibration image")
        panel = Panel(img)

        if not panel.panel_detected():
            raise IOError("Panel Not Detected!")

        mean, std, num, sat_count = panel.raw()
        micasense_panel_calibration = panel.reflectance_from_panel_serial()
        radianceToReflectance = micasense_panel_calibration / mean
        panelBandCorrection[band_name] = radianceToReflectance
        if log_file_path is not None:
            eprint("Detected panel serial: {}".format(panel.serial))
            eprint("Extracted Panel Statistics:")
            eprint("Mean: {}".format(mean))
            eprint("Standard Deviation: {}".format(std))
            eprint("Panel Pixel Count: {}".format(num))
            eprint("Saturated Pixel Count: {}".format(sat_count))
            eprint('Panel Calibration: {:1.3f}'.format(
                micasense_panel_calibration))
            eprint('Radiance to reflectance conversion factor: {:1.3f}'.format(
                radianceToReflectance))
        else:
            print("Detected panel serial: {}".format(panel.serial))
            print("Extracted Panel Statistics:")
            print("Mean: {}".format(mean))
            print("Standard Deviation: {}".format(std))
            print("Panel Pixel Count: {}".format(num))
            print("Saturated Pixel Count: {}".format(sat_count))
            print('Panel Calibration: {:1.3f}'.format(
                micasense_panel_calibration))
            print('Radiance to reflectance conversion factor: {:1.3f}'.format(
                radianceToReflectance))

    imageNamesDict = {}
    for i in imageNamesAll:
        s = i.split("_")
        k = s[-1].split(".")
        if s[-2] not in imageNamesDict:
            imageNamesDict[s[-2]] = {}
        imageNamesDict[s[-2]][k[0]] = i

    imageNameCaptures = []
    capture_count = 0
    skip_count = 0
    image_count = 0
    skip_proceed = 1
    num_captures_proceed = 1
    for i in sorted(imageNamesDict.keys()):
        im = []
        if thin_images is not None:
            if image_count > 0 and skip_count < thin_images:
                skip_count = skip_count + 1
                skip_proceed = 0
            else:
                skip_count = 0
                skip_proceed = 1
            image_count = image_count + 1

        if skip_proceed == 1:
            if number_captures is not None:
                if capture_count < number_captures:
                    num_captures_proceed = 1
                else:
                    num_captures_proceed = 0
            if num_captures_proceed == 1:
                for j in sorted(imageNamesDict[i].keys()):
                    imageName = imageNamesDict[i][j]
                    img = Image(imageName)
                    # meta = img.meta
                    # flightImageRaw=plt.imread(imageName)
                    # flightRadianceImage, _, _, _ = msutils.raw_image_to_radiance(meta, flightImageRaw)
                    # flightReflectanceImage = flightRadianceImage * panelBandCorrection[img.band_name]
                    # flightUndistortedReflectance = msutils.correct_lens_distortion(meta, flightReflectanceImage)
                    # calibratedImage = imageNameToCalibratedImageName[imageName]
                    # print(flightUndistortedReflectance.shape)
                    # plt.imsave(calibratedImage, flightUndistortedReflectance, cmap='gray')
                    # calIm = Image(calibratedImage, meta = meta)
                    im.append(img)
                if len(im) > 0:
                    imageNameCaptures.append(im)
                    capture_count = capture_count + 1

    def enhance_image(rgb):
        gaussian_rgb = cv2.GaussianBlur(rgb, (9, 9), 10.0)
        gaussian_rgb[gaussian_rgb < 0] = 0
        gaussian_rgb[gaussian_rgb > 1] = 1
        unsharp_rgb = cv2.addWeighted(rgb, 1.5, gaussian_rgb, -0.5, 0)
        unsharp_rgb[unsharp_rgb < 0] = 0
        unsharp_rgb[unsharp_rgb > 1] = 1

        # Apply a gamma correction to make the render appear closer to what our eyes would see
        gamma = 1.4
        gamma_corr_rgb = unsharp_rgb**(1.0 / gamma)
        return (gamma_corr_rgb)

    captures = []
    # captureGPSDict = {}
    # counter = 0
    for i in imageNameCaptures:
        im = Capture(i)
        captures.append(im)
        # latitudes = []
        # longitudes = []
        # altitudes = []
        # for i,img in enumerate(im.images):
        #     latitudes.append(img.latitude)
        #     longitudes.append(img.longitude)
        #     altitudes.append(img.altitude)
        # captureGPSDict[counter] = [round(statistics.mean(latitudes), 4), round(statistics.mean(longitudes), 4), statistics.mean(altitudes)]
        # counter = counter + 1

    # GPSsorter = {}
    # for counter, loc in captureGPSDict.items():
    #     if loc[0] not in GPSsorter:
    #         GPSsorter[loc[0]] = {}
    #     GPSsorter[loc[0]][loc[1]] = counter

    imageCaptureSets = captures

    img_type = "reflectance"
    match_index = 0  # Index of the band
    max_alignment_iterations = 1000
    warp_mode = cv2.MOTION_HOMOGRAPHY  # MOTION_HOMOGRAPHY or MOTION_AFFINE. For Altum images only use HOMOGRAPHY
    pyramid_levels = None  # for images with RigRelatives, setting this to 0 or 1 may improve alignment

    if log_file_path is not None:
        eprint(img_type)
        eprint(
            "Alinging images. Depending on settings this can take from a few seconds to many minutes"
        )
    else:
        print(img_type)
        print(
            "Alinging images. Depending on settings this can take from a few seconds to many minutes"
        )

    warp_matrices = None
    if tempImagePath is not None:
        if os.path.exists(os.path.join(tempImagePath, 'capturealignment.pkl')):
            with open(os.path.join(tempImagePath, 'capturealignment.pkl'),
                      'rb') as f:
                warp_matrices, alignment_pairs = pickle.load(f)

    if warp_matrices is None:
        warp_matrices, alignment_pairs = imageutils.align_capture(
            captures[0],
            ref_index=match_index,
            max_iterations=max_alignment_iterations,
            warp_mode=warp_mode,
            pyramid_levels=pyramid_levels,
            multithreaded=True)

    if log_file_path is not None:
        eprint("Finished Aligning, warp matrices={}".format(warp_matrices))
    else:
        print("Finished Aligning, warp matrices={}".format(warp_matrices))

    if tempImagePath is not None:
        with open(os.path.join(tempImagePath, 'capturealignment.pkl'),
                  'wb') as f:
            pickle.dump([warp_matrices, alignment_pairs], f)

    images_to_stitch1 = []
    images_to_stitch2 = []
    count = 0
    for x in imageCaptureSets:
        cropped_dimensions, edges = imageutils.find_crop_bounds(
            x, warp_matrices, warp_mode=warp_mode)
        im_aligned = imageutils.aligned_capture(x,
                                                warp_matrices,
                                                warp_mode,
                                                cropped_dimensions,
                                                match_index,
                                                img_type=img_type)
        if log_file_path is not None:
            eprint(im_aligned.shape)
        else:
            print(im_aligned.shape)

        i1 = im_aligned[:, :, [0, 1, 2]]
        i1 = enhance_image(i1)
        image1 = np.uint8(i1 * 255)
        cv2.imwrite(imageTempNames[count], image1)
        images_to_stitch1.append(imageTempNames[count])
        count = count + 1

        i2 = im_aligned[:, :, [2, 3, 4]]
        i2 = enhance_image(i2)
        image2 = np.uint8(i2 * 255)
        cv2.imwrite(imageTempNames[count], image2)
        images_to_stitch2.append(imageTempNames[count])
        count = count + 1

        del cropped_dimensions
        del edges
        del im_aligned
        del i1
        del i2
        del image1
        del image2

    sep = " "
    images_string1 = sep.join(images_to_stitch1)
    images_string2 = sep.join(images_to_stitch2)
    num_images = len(images_to_stitch1)

    del imageNamesAll
    del imageTempNames
    del imageNamesDict
    del panelNames
    del imageNameCaptures
    del imageCaptureSets
    del images_to_stitch1
    del images_to_stitch2

    log_file_path_string = ''
    if log_file_path is not None:
        log_file_path_string = " --log_file '" + log_file_path + "'"
    stitchCmd = "stitching_multi " + images_string1 + " " + images_string2 + " --num_images " + str(
        num_images
    ) + " --result1 '" + final_rgb_output_path + "' --result2 '" + final_rnre_output_path + "' " + log_file_path_string
    # stitchCmd = "stitching_multi "+images_string1+" "+images_string2+" --num_images "+str(num_images)+" --result1 '"+final_rgb_output_path+"' --result2 '"+final_rnre_output_path+"' --log_file "+log_file_path+" --work_megapix "+work_megapix+" --ba_refine_mask "+ba_refine_mask
    # stitchCmd = "stitching_multi "+images_string1+" "+images_string2+" --num_images "+str(len(images_to_stitch1))+" --result1 '"+final_rgb_output_path+"' --result2 '"+final_rnre_output_path+"' --try_cuda yes --log_file "+log_file_path+" --work_megapix "+work_megapix
    if log_file_path is not None:
        eprint(stitchCmd)
        eprint(len(stitchCmd))
    else:
        print(stitchCmd)
        print(len(stitchCmd))
    os.system(stitchCmd)

    final_result_img1 = cv2.imread(final_rgb_output_path, cv2.IMREAD_UNCHANGED)
    final_result_img2 = cv2.imread(final_rnre_output_path,
                                   cv2.IMREAD_UNCHANGED)
    final_result_img1 = enhance_image(final_result_img1 / 255)
    final_result_img2 = enhance_image(final_result_img2 / 255)

    plt.imsave(final_rgb_output_path, final_result_img1)
    plt.imsave(final_rnre_output_path, final_result_img2)

    plt.imsave(output_path_band1, final_result_img1[:, :, 0], cmap='gray')
    plt.imsave(output_path_band2, final_result_img1[:, :, 1], cmap='gray')
    plt.imsave(output_path_band3, final_result_img1[:, :, 2], cmap='gray')
    plt.imsave(output_path_band4, final_result_img2[:, :, 1], cmap='gray')
    plt.imsave(output_path_band5, final_result_img2[:, :, 2], cmap='gray')