# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: desc-stack-weekly-latest
#     language: python
#     name: desc-stack-weekly-latest
# ---

# **Summary:** This file is intended for an end-to-end demonstration of BTK capabilities for the July Meeting 2020 Guided Sprint

# # Setup

# %load_ext autoreload
# %autoreload 2

# %matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import os
import sys

home_path = os.path.expanduser("~")

# download WeakLensingDeblending into home directory
# ! git clone <EMAIL>:LSSTDESC/WeakLensingDeblending.git $HOME/WeakLensingDeblending

sys.path.insert(0,os.path.dirname(os.getcwd())) # add BlendingToolKit so we can import btk 
sys.path.insert(0, home_path + "/WeakLensingDeblending")  # add path to WeakLensingDeblending so we can import descwl 

import btk
import btk.plot_utils, btk.utils
import astropy.table
import inspect
import descwl

import sklearn

import scarlet 
import scarlet.display


# ## Utility functions 

def make_histograms(*vals, labels=None, loc='best'):
    # make a histogram of each np.array in vals. 
    labels = labels if labels else [None]*len(vals)   
    bins = np.histogram(np.hstack(vals), bins=40)[1]  #get the bin edges
    for val, label in zip(vals, labels): 
        plt.hist(val, bins, label=label, histtype='step')
    plt.legend(loc=loc)


# # Load Params 

# In this first part we establish a set of parameters that we'll be using throughout the tutorial. You can think of them as user-defined global variables that are used throughout the simulations in BTK. 
# These parameters will be fixed throughout this tutorial as well. 
#
# Note that the `verbose` flag is turned off, in case you would like additional messages that show the functionality you can turn it on. 

catalog_name = os.path.join(os.path.dirname(os.getcwd()), 'data', 'sample_input_catalog.fits')
param = btk.config.Simulation_params(catalog_name, max_number=6, batch_size=2, verbose=False)
np.random.seed(param.seed)

# If you forgot what parameters you used, you can quickly display them like 
param.display()

# Params can be changed by specifying additional keyword arguments when creating the `btk.config.Simulation_params` object. 

# # Load Catalog

# Let's load the small catalog that we will be using. 
#
# The file containing this catalog can be found in the `catalog_name` attribute of `param` as shown above. 

# +
catalog_full = btk.get_input_catalog.load_catalog(param)

# as you can see this is just a astropy table. 
print(type(catalog_full))
print(len(catalog_full))
# -

# As you can see this catalog only contains 100 entries. 

# display available columns
catalog_full.colnames

# ## selection function 

# One can optionally apply selection function at this step, there is the basic selection function already implemented inside BTK in `btk.utils` that we will use as an example: 

# this function selects everything with i band mag < 27 and second moment size < 4 arcsecs 
print(inspect.getsource(btk.utils.basic_selection_function))

# As you can see in the function above, a `selection_function` simply takes in the catalog and 
# returns a new (filtered) one. 
#
# In the example below, we can see how the filtering of the `selection_function` reduced the number of objects.

# After defining the function, you can use it like: 
catalog = btk.get_input_catalog.load_catalog(param, selection_function=btk.utils.basic_selection_function)
len(catalog)

# ## Histograms 

# let's see if our selection function is making sense by doing some histograms with the provided utility function.
labels = ['full', 'default_selection']
make_histograms(catalog_full['i_ab'], catalog['i_ab'], labels=labels, loc=2)
plt.xlabel('i_ab', size=18)

# ## Exercise: Custom selection function

# Create your own selection function that make a cut in the `r` band magnitude instead of the `i` band magnitude. How different is your resulting catalog from the original one? Make a histogram like the one above to check your results. 

# +
# your code here. 
# -

# # Create blend generator 

# A `generator` is python jargon - it's a convenient data structure that only returns one object at a time. Never loading all of the objects into memory at once. 
#
# `BTK` uses generators to create batches of images and corresponding parameters on the fly. We will now work on an example. 
#
# A `blend_generator` uses a `sampling_function` which defines how BTK generates and returns blends (groups of galaxies in a postage stamp that are considered 'blended' and that are drawn together). As you might expect, a `sampling_function` is required by BTK, although passing in `None` like below actually uses the default sampling function. 

blend_generator = btk.create_blend_generator.generate(param, catalog, sampling_function=None)

# Here is the `sampling_function` used:

print(inspect.getsource(btk.create_blend_generator.default_sampling))

# The `sampling_function` should always be returning a `blend_catalog`, which is a (random) small subset from the catalog that defines a blend. In the example above, we are sampling a subset of size `number_of_objects` from the `catalog` after making a more stringent cut on i band magnitude and randomly shifting each galaxy from the center. 

# +
# to use the blend generator we use the 'next' python syntax
blend_catalog = next(blend_generator)

# blend catalog is a batch of list of astropy tables (# tables = batch_size)
# tables same format as original astropy catalog table. 
# objects in each table is variable and depends on params settings. 
blend_catalog
# -

# ## Exercise: Sampling function using RA/DEC 

# Create a `sampling_function` that only returns **pairs** of objects that are close to each other (you can decide the threshold) as measured by `(dx,dy)`. 
# In the later parts of this tutorial we'll draw actual images resulting from a given sampling function and can check that your galaxies look reasonably close by eye (depending on your threshold). 

# +
# To get a sense for scale of distanced we start by creating a plot of all possible distances.

#first use the btk internal function to obtain distances in pixels (dx,dy) from (ra,dec) 
dx, dy = btk.draw_blends.get_center_in_pixels(param, catalog)

# get all pairs of distances. 
dists = np.array([np.hypot(x2 - x1, y2 - y1) for (x1,y1) in zip(dx,dy) for (x2, y2) in zip(dx,dy)])

# remove the self-to-self distances
dists = dists[dists!=0]

#create histogram. 
plt.hist(dists, bins=25, histtype='step');

print(f'total number of pairs is: {len(dists)}')
plt.xlabel("pixels", size=14 )
# -

# You'll notice most of the objects in this catalog are really really close to each other! That's just by construction of the catalog, you can still just apply a small threshold to get the most blended pairs of objects (e.g. < 0.1). The idea will be the same for a more complete catalog. 

# +
# your code here. 
# -

# ## Hack Idea 

# If you would like to contribute to BTK and extend the sampling function that you created above, please take a look at this issue: https://github.com/LSSTDESC/BlendingToolKit/issues/16

# # Observing generator 

# Now we turn to specifying the observational conditions for the blends created. Currently, the observing conditions are specified via the [WeakLensingDeblending](https://weaklensingdeblending.readthedocs.io/en/latest/) package (WLD), this package contains an object called `descwl.survey.Survey` that internally specifies observing conditions. Changeable parameters from the survey object include: 
#
# - mirror_diameter
# - effective_area 
# - pixel_scale 
# - airmass
# - exposure_time
# - sky_brightness 
# - zenith_psf_fwhm
# - zero_points
# - extinction 
#
# The PSF used for both the optics in the atmosphere are either a Moffat or Kolmogorov model. For more details see the [source code](https://github.com/ismael2395/WeakLensingDeblending/blob/master/descwl/survey.py) of `descwl.survey`.

# The default observing conditions function in BTK are just constant across batches and will always return the defaults in the WLD package. 

print(inspect.getsource(btk.create_observing_generator.default_obs_conditions))

observing_generator = btk.create_observing_generator.generate(param, obs_function=None)


# ## Example of custom observing conditions 

# Here is an example of a customized observing conditions function, different batches will have different observing conditions as they are produced. 

def custom_obs_condition(Args, band):
    """Returns observing conditions from the WLD package
    for a given survey_name and band with a small offset from
    the default parametrs.
    Args
        Args: Class containing parameters to generate blends
        band: filter name to get observing conditions for.
    Returns
        survey: WLD survey class with observing conditions.
    """
    survey = descwl.survey.Survey.get_defaults(
        survey_name=Args.survey_name,
        filter_band=band)
    survey['exposure_time'] = random.uniform(10, survey['exposure_time'])
    survey['zenith_psf_fwhm'] += random.uniform(-0.3,0.3)
    return survey


# Here is how to use it: 
observing_generator = btk.create_observing_generator.generate(param, obs_function=custom_obs_condition)

# We will use this custom observing condition to create images in the next part. 

# # Draw blends

# ## Setup

# Here I'm just gathering all the results from the previous sections in case you want to experiment quickly by changing one of the generators. 
#
# Remember that all the `None` statements below can be replaced with your favorite customized function, but I just let them be `None` here for simplicity (uses `BTK` default as explained in the sections above).  

# setup params and catalog 
catalog_name = os.path.join(os.path.dirname(os.getcwd()), 'data', 'sample_input_catalog.fits')
param = btk.config.Simulation_params(catalog_name, max_number=6, batch_size=2, verbose=False)
catalog = btk.get_input_catalog.load_catalog(param, selection_function=None)
blend_generator = btk.create_blend_generator.generate(param, catalog, sampling_function=None)
observing_generator = btk.create_observing_generator.generate(param, obs_function=None)

# ## Draw blend generator

# Now we get to play with some images :) We will put together all the generators we have built so far and create some images simulated with `galsim` by interfacing with the WLD package and BTK. 
#
# We first need one more generator created from the other two generators: `draw_blend_generator`

#Generate images of blends in all the observing bands
draw_blend_generator = btk.draw_blends.generate(param, blend_generator, observing_generator) 

# +
# This cell generates new `batch_size` number of blends each time it's executed

# Draw blend
blend_results = next(draw_blend_generator)
output = blend_results
blend_images = output['blend_images']
isolated_images = output['isolated_images']
blend_list = output['blend_list']
# -

output.keys()

# Note the following: 
#
# - Each time the function `next` is used on `draw_blend_generator` a fresh batch of data is simulated (based on your pre-specified `blend_generator` and `observing_generator`.
#
# - The output of the `draw_blend_generator` is a `dict` containing the following: 
#
#     - `blend_images`: A `np.array` of shape `batch_size x stamp_size x stamp_size x n_bands`. This contains the actual postage stamps with the blends.
#                         
#     - `isolated_images`: A `np.array` of shape `batch_size x max_objects x stamp_size x stamp_size x n_bands`. This contains the images that were added together the create the `blend_images`.   
#     
#     - `blend_list`: A list of `astroy.Table`s containing the source catalog of each blend in the batch. 
#     
#     - `obs_condition`: A list of `batch_size` of list of `n_bands` observing conditions  `n_bands` that is used for every element in the batch. 

# ## Draw some blends!

# +
# Plot blended images
btk.plot_utils.plot_blends(blend_images, blend_list, limits=(30,90))

#this also shows the true centers of the drawn galaxies with a red 'x'
# -

# In case you would also like to see each individual source in the blend you can take a look at the `isolated_images` 

# plot the first source in the first blend in the 'i' band. 
# mark its appropriate position manually from the blend list. 
im = plt.imshow(isolated_images[0][0][...,3])
plt.scatter(blend_list[0][0]['dx'], blend_list[0][0]['dy'], marker='x', color='r')
plt.colorbar(im)


# ## Exercise: Other observing conditions 

# Recreate some of the plots above with different `obs_conditions`, you can use the `custom_obs_condition` as a start. Are the observing conditions the same across different batches? Can you understand why the images produced make sense given the observing conditions that you changed? 



# ## Exercise: User-defined sampling function output

# Generate some more blend plots using a different `sampling_function`, you can use the one suggested in the exercise above or create your own depending on what you want to explore.  



# ## Extra Exercise: Realistic blends 

# It is possible to create more realistic blends using BTK and the WeakLensingDeblending (WLD) package. The `WLD` package is able to assign galaxies to groups by drawing in a particular catalog under some pre-specified observing conditions, and putting together all the galaxies that have at least 1 overlapping pixel (above some threshold). 
#
# There is an example of a small catalog processed with WLD in the BTK repo that you can use to experiment with this feature: `data/sample_group_catalog.fits`, if you load this table you should notice the column `grp_id` which is ID identifying the groups selected by `WLD`. 

catalog_file = "../data/sample_group_catalog.fits"

# In `BTK` there is already a sampling function that you can use to sample groups from this catalog: `btk.utils.group_sampling_function`. Feel free to take a look at this function and use it to generate some blends from the catalog just mentioned. Do these blends look any different from the ones you produced in the previous exercises or with the `default_sampling_function`?



# ## More resources

# For more resouces on custom observing conditions and sampling functions, take a look at this notebook: `notebooks/custom_sampling_function.ipynb` or in this github link: https://github.com/LSSTDESC/BlendingToolKit/blob/master/notebooks/custom_sampling_function.ipynb

# # Create Measure generator 

# In this next part, we will see how we can combine the functionality of creating blends in BTK  with your favorite detection/deblending/measurement algorithms. 
#
# For simplicity let's start by defining everything we did so far in one function. 

def get_draw_blend_generator():
    # Load parameters
    #Input catalog name
    catalog_name = os.path.join(os.path.dirname(os.getcwd()), 'data', 'sample_input_catalog.fits')
    param = btk.config.Simulation_params(catalog_name,batch_size=2, max_number=3 )
    np.random.seed(param.seed)

    #Load input catalog
    catalog = btk.get_input_catalog.load_catalog(param)

    #Generate catlogs of blended objects 
    blend_generator = btk.create_blend_generator.generate(param, catalog)

    #Generates observing conditions for the selected survey_name and all input bands
    observing_generator = btk.create_observing_generator.generate(param)

    #Generate images of blends in all the observing bands
    draw_blend_generator = btk.draw_blends.generate(param, blend_generator, observing_generator)
    return draw_blend_generator


draw_blend_generator = get_draw_blend_generator()

# Measurement/Deblending in BTK is structured very similarly to the previous approaches: using a generator. The reason for this is that the measurement might produce additional images or data that is later summarized, so it makes sense to not store it all at once but rather aggregate at the end if desired. 

# The functionality that a user needs to add to BTK in order to specify their own algorithm is summarized in a class `btk.measure.Measurement_params`. You need to implement at least one of two functions:
#
# - `make_measurement`: Given a dictionary `data` (`output` in the above section) return an astropy table with the measurements you made (fluxes, shapes, size, etc.) for each object in the blend. 
#
# - `get_deblended_images`: Return output of deblending algorithm as dict. 
#
# We will now walk through an example demonstration. 

# Here is an example class to perform peak detection with sklearn that is copied from `btk.utils` :

# ## Basic Detection algorithm 

from btk.measure import Measurement_params
class Basic_measure_params(Measurement_params):
    """Class to perform detection by identifying peaks with skimage"""

    def get_centers(self, image):
        # set detection threshold to 5 times std of image
        threshold = 5 * np.std(image)
        coordinates = skimage.feature.peak_local_max(image, min_distance=2,
                                                     threshold_abs=threshold)
        return np.stack((coordinates[:, 1], coordinates[:, 0]), axis=1)

    def get_deblended_images(self, data, index):
        """Returns scarlet modeled blend and centers for the given blend"""
        image = np.mean(data['blend_images'][index], axis=2)  # don't use multi-band information. 
        peaks = self.get_centers(image)
        return {'deblend_image': None, 'peaks': peaks}


#this is how we specify the measurement params
meas_params = btk.utils.Basic_measure_params()

# get generator that yields measured values by running the algorithm in the images from the draw_blend_generator. 
# you can also turn on multiprocessing in this step in case your algorithm is CPU intensive. 
meas_generator = btk.measure.generate(
        meas_params, get_draw_blend_generator(), param)

output, deb, _ = next(meas_generator)  # obtain simulation measurement output for this next batch. 
blend_list = output['blend_list']
for i in range(len(blend_list)):
    blend_images = output['blend_images'][i:i+1]
    detected_centers = deb[i]['peaks']
    true_centers = np.stack([blend_list[i]['dx'], blend_list[i]['dy']]).T
    true_table = blend_list[i]
    det_table = astropy.table.Table(detected_centers, names=('dx', 'dy'))
    _, _, summary = btk.compute_metrics.evaluate_detection([true_table], [det_table], 0)
    det, undet, spur = summary[0][1:4]
    print("Out of %i objects, %i detected, %i undetected, & %i  spurious detections"%(len(true_centers), det, undet, spur))
    btk.plot_utils.plot_blends(blend_images, blend_list[i: i+1], [detected_centers],limits=(30,90))

# We can see how to basic `skimage` algorithm misses detection when the amount of blending is considerable. 

# ## Deblending with Scarlet 

# There is already an implementation of Scarlet as `Measurement_params`, take a look in `utils.measure.Scarlet_params`. We will walk through it step by step in this section. 

# get output from draw_blend_generator and let's look at the blends. 
output = next(get_draw_blend_generator())
blend_images = output['blend_images']
blend_list = output['blend_list']
obs_conds = output['obs_condition'][1]
btk.plot_utils.plot_blends(blend_images, blend_list, limits=(30,90))

# Let's try to deblend the second image which has two galaxies that look somewhat blended. 
#

# +
catalog = blend_list[1] 

#make the order of the axis convention match that of scarlet 
images = np.transpose(blend_images[1], axes=(2,0,1))  # shape = (6, 120, 120 )


bands = np.array([obs_cond.filter_band for obs_cond in obs_conds], dtype=str)

 # use the true peaks and organize them. 
peaks = np.stack((catalog['dx'], catalog['dy']), axis=1) 
# -

# First we define an utility function for obtaining the true PSF used to create the image. 

# +
from functools import partial 

def get_psf_sky(obs_cond, psf_stamp_size):
    """Returns postage stamp image of the PSF and mean background sky
    level value saved in the input obs_cond class
    Args:
        obs_cond:`descwl.survey.Survey` class describing observing
            conditions.
        psf_stamp_size: Size of postage stamp to draw PSF on in pixels.
    Returns:
        `np.ndarray`: Postage stamp image of PSF
        float: Mean of sky background

    """
    mean_sky_level = obs_cond.mean_sky_level
    psf = obs_cond.psf_model
    psf_image = psf.drawImage(
        scale=obs_cond.pixel_scale,
        nx=psf_stamp_size,
        ny=psf_stamp_size).array
    return psf_image, mean_sky_level


# -

# We now proceed to initialize scarlet, the following procedure is based heavily on the Scarlet tutorial which I recommend checking out: https://pmelchior.github.io/scarlet/0-quickstart.html

# +
# initialize scarlet 
psf_stamp_size = 41

#create model psf. 
model_psf = scarlet.PSF(
    partial(scarlet.psf.gaussian, sigma=0.8),
    shape=(None, psf_stamp_size, psf_stamp_size))

#create a frame for the model using the model_psf. 
model_frame = scarlet.Frame(
    images.shape,
    psfs=model_psf,
    channels=bands)

# +
# Now we need to do some setting up to define the observation. 
n_bands = len(images)
psfs = np.zeros((n_bands, psf_stamp_size, psf_stamp_size),
                dtype=np.float32)
variances = np.zeros_like(images)

for i in range(n_bands): 
    psf, mean_sky_level = get_psf_sky(
        obs_conds[i], psf_stamp_size)
    psfs[i] = psf
    variances[i] = images[i] + mean_sky_level

# -

# observation frame based on the image. 
observation = scarlet.Observation(
    images,
    psfs=scarlet.PSF(psfs),
    weights=1. / variances,
    channels=bands).match(model_frame)

#specify the fitting model using the true centers and some constraints. 
sources = []
for n, peak in enumerate(peaks):
    result = scarlet.ExtendedSource(
        model_frame, (peak[1], peak[0]), observation,
        symmetric=True, monotonic=True,
        thresh=1, shifting=True)
    sources.append(result)

# perform the fit of the blended sscene. 
e_rel = 1e-6
iters=200
blend = scarlet.Blend(sources, observation)
blend.fit(iters, e_rel=e_rel)

# use an btk internal plotting tool to get the visualization. 
btk.plot_utils.show_scarlet_residual(blend, observation, limits=(30,90))

# +
# Looks pretty good :) 
# -

# In case you want to look at what this function is doing internally: 

# +
# get the model resulting form the fit and render it into the observationn frame
# these resizes the model to the original image size. 
model = blend.get_model()
model_ = observation.render(model)
residual = images-model_

# finally plot residual. 
plt.imshow(scarlet.display.img_to_rgb(residual))
# -

# ### Scarlet integration with BTK  

# `BTK` already has an implementation of Scarlet deblending where centers are obtained using `SEP` (not installed in this environment fortunately) but here is how you would use it. 

# + jupyter={"outputs_hidden": true}
meas_params = btk.utils.Scarlet_params(show_scene=True)
meas_generator = btk.measure.generate(
    meas_params, get_draw_blend_generator(), param)
output, deb,_ = next(meas_generator)
blend_list = output['blend_list']
blend_images = output['blend_images']
deblend_images = [deb[i]['deblend_image'] for i in range(len(blend_list))]
detected_centers = [deb[i]['peaks'] for i in range(len(blend_list))]

# you would get a similar plto to the one above. 
# -

# ## Exercise

# Try re-running Scarlet on some more complicated blends (draw other blends from the generator) and try to come up with an example where it fails. 

# +
# your code here. 
# -

# # Metrics

# Now let's show how use metrics to evaluate your deblenders robustly over several batches. 
#
# For this example we will use the Stack for detection. 

# In BTK, evaluating the detection efficiency of your algorithm is as easy as creating a children from the parent class `btk.compute_metrics.Metrics_params` 

# +
def get_meas_generator(meas_params):
    """Returns generator that yields output of user defined algorithm specified in meas_param.    
    Measurements are performed for default sampling function with maximum of 6 objects per blend.
    
    Returns:
        meas_generator: Generator that yeilds output of deblender and measurement algorithm..
    """
    # Load parameters
    #Input catalog name
    catalog_name = os.path.join(os.path.dirname(os.getcwd()), 'data', 'sample_input_catalog.fits')
    param = btk.config.Simulation_params(catalog_name,batch_size=4, max_number=6 )
    np.random.seed(param.seed)

    #Load input catalog
    catalog = btk.get_input_catalog.load_catalog(param)

    #Generate catlogs of blended objects 
    blend_generator = btk.create_blend_generator.generate(param, catalog)

    #Generates observing conditions for the selected survey_name and all input bands
    observing_generator = btk.create_observing_generator.generate(param)
    
    np.random.seed(int(param.seed))
    #Generate images of blends in all the observing bands
    draw_blend_generator = btk.draw_blends.generate(param, blend_generator, observing_generator) 
    # get generator that yields measured values.
    meas_generator = btk.measure.generate(
            meas_params, draw_blend_generator, param)
    return meas_generator, param


# -

import astropy.table
class Stack_metric_params(btk.compute_metrics.Metrics_params):
    def __init__(self, *args, **kwargs):
        super(Stack_metric_params, self).__init__(*args, **kwargs)
        """Class describing functions to return results of
         detection/deblending/measurement algorithm in meas_generator. Each
         blend results yielded by the meas_generator for a batch.
    """

    def get_detections(self):
        """Returns blend catalog and detection catalog for detction performed

        Returns:
            Results of the detection algorithm are returned as:
                true_tables:  List of astropy Tables of the blend catalogs of the
                    batch. Length of tables must be the batch size. x and y coordinate
                    values must be under columns named 'dx' and 'dy' respectively, in
                    pixels from bottom left corner as (0, 0).
                detected_tables: List of astropy Tables of output from detection
                    algorithm. Length of tables must be the batch size. x and y
                    coordinate values must be under columns named 'dx' and 'dy'
                    respectively, in pixels from bottom left corner as (0, 0).
        """
        # Astropy table with entries corresponding to true sources
        blend_op, _, cat = next(self.meas_generator)
        true_tables = blend_op['blend_list']
        detected_tables = []
        for i in range(len(true_tables)):
            detected_centers = np.stack([cat[i]['base_NaiveCentroid_x'], cat[i]['base_NaiveCentroid_y']], axis=1)
            detected_table = astropy.table.Table(detected_centers,
                                                 names=['dx', 'dy'])
            detected_tables.append(detected_table)
        return true_tables, detected_tables


# here we follow the same steps as before, and we specify the size of the test (how many batches)
meas_param = btk.utils.Stack_params()
meas_generator, param = get_meas_generator(meas_param)
metric_param = Stack_metric_params(meas_generator=meas_generator, sim_param=param)
results = btk.compute_metrics.run(metric_param, test_size=10)
true_table, detected_table, detection_summary = results['detection']

# +
num = np.array(detection_summary).max()

_, ax = plt.subplots(1,3,figsize=(15,5))
plt.subplots_adjust(wspace=0.3, hspace=0.3)

#match1
det_summary = np.array(detection_summary)
tot = np.sum(det_summary[:, 0])
det = np.sum(det_summary[:, 1])
spur = np.sum(det_summary[:, 3])
print("match1", "precision: ", float(det/(det + spur)), "recall: ", float(det/(tot)))
btk.plot_utils.plot_metrics_summary(det_summary, num, ax=ax[0])
ax[0].set_title('match1')
    
# match 2
summ = np.array(detection_summary)
det_summary = np.concatenate([summ[:, 0, np.newaxis], summ[:, 5:]], axis=1)
tot = np.sum(det_summary[:, 0])
det = np.sum(det_summary[:, 1])
spur = np.sum(det_summary[:, 3])

print("match2", "precision: ", float(det/(det + spur)), "recall: ", float(det/(tot)))
btk.plot_utils.plot_metrics_summary(det_summary, num, ax=ax[1])
ax[1].set_title('match2')

summ = np.array(detection_summary)
det_summary = np.concatenate([summ[:, 0, np.newaxis], summ[:, 5:]], axis=1)
det_summary[:, 1] += det_summary[:, 3] +det_summary[:, 4]
btk.plot_utils.plot_metrics_summary(det_summary, num, ax=ax[2])
ax[2].set_title('Blend')
ax[2].set_ylabel('# detected objects')
# -

# ## Exercise

# Try running for more iterations to see where the efficienty matrix converges. 

# ## Hack idea 

# The code this part is pretty hard to understand and I"m looking for people to help me refactor the code in `compute_metrics.py` so that it is more accessible. 
# Suggestions or advice are also welcome! 



StarcoderdataJupyterScriptsDedupFiltered