Code Monkey home page Code Monkey logo

Comments (7)

pjbull avatar pjbull commented on May 21, 2024 2

@perrygeo Still interested in this? If so, I've included the basic approach below for reference (not yet cleaned up, just as FYI). What it does is:

  • Accept ref_paths as a list of paths rather than just a single image.
  • Add a dimension to the ref array for the nth image in the list ref_paths
  • Load every image from ref_paths into the ref array
  • Once these are all loaded, we then loop through ref_paths again and treat every image passed as the src array
  • The current histogram algorithm handles the binning of the ref array without any changes (even with the added dimension)
  • This creates a new src image that uses the histogram binning across all of the images in ref_paths, so they should all share the same histogram.

The open questions for integration are:

  • How to expose this functionality in the CLI
  • How much refactoring to do so that the existing hist_match_worker and the new batch_hist_match_worker rely on the same code (which, as you can see below, is duplicated)
def batch_hist_match_worker(ref_paths, match_proportion,
                            creation_options, bands, color_space, plot,
                            masked=True,
                            dst_suffix='_hist_matched'):

    """Matches the histogram of every image in ref_paths
       to the average histogram across all of the included images.

       Outputs each file with _hist_matched appended to the filename.

       Modified from:
       https://github.com/mapbox/rio-hist/blob/81e3d5f0f59ba1e4e15f8850592a818501957ed2/rio_hist/match.py
    """

    # we need one sample to setup the processing
    src_path = ref_paths[0]

    with rasterio.open(src_path) as src:
        profile = src.profile.copy()
        src_arr = src.read(masked=masked)

        if src_arr.dtype != np.float32:
            raise("Currently, this only supports float32")

        if masked:
            src_mask, src_fill = calculate_mask(src, src_arr)
            src_arr = src_arr.filled()
        else:
            src_mask, src_fill = None, None

    ref_out = np.zeros([len(ref_paths)] + list(src_arr.shape), dtype=src_arr.dtype)

    # no bands, just masks
    ref_mask_out = np.zeros([len(ref_paths)] + list(src_arr.shape[1:]), dtype=src_arr.dtype)

    n_bands = src_arr.shape[0]

    for i, ref_path in enumerate(ref_paths):
        with rasterio.open(ref_path, 'r') as ref:
            ref_arr = ref.read(masked=masked)

            if masked:
                ref_mask, ref_fill = calculate_mask(ref, ref_arr)
                ref_arr = ref_arr.filled()
            else:
                ref_mask, ref_fill = None, None

            ref_out[i, :, :, :] = ref_arr

            if ref_mask is None:
                ref_mask_out = None
            else:
                ref_mask_out[i, :, :] = ref_mask

    bixs = tuple([int(x) - 1 for x in bands.split(',')])

    ref = ref_out

    output_paths = []

    print("Matching histograms...")
    for src_path in tqdm(ref_paths):
        with rasterio.open(src_path) as src:
            profile = src.profile.copy()
            src_arr = src.read(masked=masked)

            if masked:
                src_mask, src_fill = calculate_mask(src, src_arr)
                src_arr = src_arr.filled()
            else:
                src_mask, src_fill = None, None

        src = src_arr

        target = src.copy()
        for i, b in enumerate(bixs):
            logger.debug("Processing band {}".format(b))
            src_band = src[b]
            ref_band = ref[:, b, :, :]

            # Re-apply 2D mask to each band
            if src_mask is not None:
                logger.debug("apply src_mask to band {}".format(b))
                src_band = np.ma.asarray(src_band)
                src_band.mask = src_mask
                src_band.fill_value = src_fill

            if ref_mask_out is not None:
                logger.debug("apply ref_mask to band {}".format(b))
                ref_band = np.ma.asarray(ref_band)
                ref_band.mask = ref_mask_out
                ref_band.fill_value = ref_fill

            target[b] = histogram_match(src_band, ref_band, match_proportion)

        target_rgb = target # cs_backward(target, color_space)

        # re-apply src_mask to target_rgb and write ndv
        if src_mask is not None:
            logger.debug("apply src_mask to target_rgb")
            if not np.ma.is_masked(target_rgb):
                target_rgb = np.ma.asarray(target_rgb)
            target_rgb.mask = np.array((src_mask, src_mask, src_mask))
            target_rgb.fill_value = src_fill

        profile['count'] = n_bands

        # profile['dtype'] = 'uint8'
        profile['nodata'] = None
        profile['transform'] = guard_transform(profile['transform'])
        profile.update(creation_options)

        input_base_filepath = os.path.splitext(os.path.abspath(src_path))[0]
        dst_path = '{}{}.tif'.format(input_base_filepath, dst_suffix)

        logger.info("Writing raster {}".format(dst_path))
        with rasterio.open(dst_path, 'w', **profile) as dst:
            for b in bixs:
                dst.write(target_rgb[b], b + 1)

            if src_mask is not None:
                gdal_mask = (np.invert(src_mask) * 255).astype('uint8')

                # write to extra band
                dst.write(gdal_mask, bixs[-1] + 2)

        output_paths.append(dst_path)

    return output_paths

from rio-hist.

pjbull avatar pjbull commented on May 21, 2024 1

@perrygeo First, thanks for this awesome tool!

Second, I've updated part of this tool to take a list of images, calculates the histogram aggregated over all of the images, and then applies the histogram matching to each individual image so that it matches that average.

This seems related to what you've mentioned in this issue. If it's of interest to you, happy to submit as a PR.

In my test case, it's producing better mosaics then a single reference image for the histogram. 👍

from rio-hist.

perrygeo avatar perrygeo commented on May 21, 2024

@pjbull interesting idea! Mosaics/seams are currently one of the most challenging issue with hist matching so I'd love to check out your solution.

from rio-hist.

pjbull avatar pjbull commented on May 21, 2024

I'll clean it up for a PR--it's not super smart, just takes the histogram across the whole collection of images and then follows the same histogram matching algorithm to update each individual image.

I'm using it as part of a machine learning pipeline (rather than for visual inspection) so my tolerance for seams and other artifacts is a little higher. Here's a comparison of processed/unprocessed:

screen shot 2017-01-23 at 3 30 10 pm

from rio-hist.

jzmiller1 avatar jzmiller1 commented on May 21, 2024

This seems like a cool feature!

from rio-hist.

perrygeo avatar perrygeo commented on May 21, 2024

@pjbull I haven't had time to dig into the details yet but this looks great. Thanks! Looking forward to trying it out.

Can you submit a PR with batch_hist_match_worker function? For now, let's add it to https://github.com/mapbox/rio-hist/blob/master/rio_hist/match.py. I'm not too worried about the repeated code. More important to get it out there for testing - we can factor out common pieces of logic as needed once tests are in place.

from rio-hist.

sgillies avatar sgillies commented on May 21, 2024

It looks like this is not going forward. Will close.

from rio-hist.

Related Issues (16)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.