# This is a CamiTK python action
#
# Using numpy to threshold an image

import numpy as np
import camitk

def print_numpy_array_info(msg, image_data, threshold):
    # number of voxels below threshold but over 0
    count = np.sum((image_data < threshold) & (image_data > 0))
    # Mask values in range [0, threshold)
    masked = image_data[(image_data > 0) & (image_data < threshold)]
    # Get the number of unique values between 0 and threshold
    num_unique = np.unique(masked).size
    # full info
    camitk.info(msg + ":\n- size: " +str(image_data.shape) 
        + "\n- dim:" + str(image_data.ndim) + " of type " + str(type(image_data.ndim)) 
        + "\n- values: [" + str(image_data.min()) + ".." + str(image_data.max()) 
        + "]\n- nb of voxels < " + str(threshold) + ": " + str(count) + " voxels with " + str(num_unique) + " different values")

def init(self:camitk.Action):
    camitk.info("CamiTK version: " + camitk.__version__)
    # camitk.info("Opened components: " + str(len(camitk.Application.getTopLevelComponents())))
    # # camitk.Application.open(camitk.Core.get_test_data_dir() + "/brain.mha")
    # camitk.Application.open(camitk.Core.getTestDataDir() + "/amos_0336/amos_0336.mhd")
    # for c in camitk.Application.getTopLevelComponents():
    #     print(c.getName()) # print will print to CamiTK console without timestamp, level and location in code

def process(self:camitk.Action):
    image_component = self.getTargets()[-1] # last target (should be the same as [0])
    image_data = image_component.getImageDataAsNumpy()
    
    # use numpy to threshold (set values that are below the threshold to 0)
    threshold = self.getParameterValue("Threshold")
    image_data[image_data<threshold] = image_data.min()

    # Test replace_image_data
    # image_component.replace_image_data(image_data)

    # Create image from data
    new_image_component = camitk.newImageComponentFromNumpy(image_data, "Image From Python", image_component.getSpacing())
    new_image_component.setFrameFrom(image_component)

    # other ways to call
    #camitk.newImageComponentFromNumpy(image_data) # default name="image", spacing=(1,1,1)
    #camitk.newImageComponentFromNumpy(image_data, spacing=image_data_info["spacing"]) # default name="image", spacing=(1,1,1)
    #camitk.newImageComponentFromNumpy(image_data, "label_map", spacing=(0.5, 0.5, 1.0))
    #camitk.newImageComponentFromNumpy(image_data, spacing=(0.5, 0.5, 1.0), name="threshold image")
    
    self.refreshApplication() # similar to what would be done in C++
    # or camitk.refresh()

    return True

def targetDefined(self:camitk.Action):
    targets = self.getTargets()
    image_component = self.getTargets()[-1] # last target (should be the same as [0])

    image_data = image_component.getImageDataAsNumpy()
    minValue = image_data.min()
    maxValue = int(image_data.max())
    step = (maxValue - minValue) / 255.0;
    
    threshold = self.getProperty("Threshold")    
    threshold.setAttribute("minimum", int(minValue)) # int(minValue) otherwise pybind11 converts 'numpy.int16' to QMap<QString, QVariant>'
    threshold.setAttribute("maximum", maxValue)
    threshold.setAttribute("singleStep", int(step))
    self.setParameterValue("Threshold", int(minValue + (maxValue - minValue)/2))

def parameterChanged(self:camitk.Action, name:str):
    camitk.info("Parameter " + name + " changed, new value: " + str(self.getParameterValue(name)))