# File Maker for Unevenly Yellowed Photo Correction 32/16/8bit Version
# Ver. 5.9a Jan. 2025 32bit processing version (modify brown mask etc.)
# masks made from perceptural image
# (c)2020-25 Ohnishi, Yasuo
from tokenize import Double
from xdrlib import ConversionError
from ij import IJ, ImagePlus
from ij import plugin
from ij import WindowManager as WM
from ij.io import OpenDialog
from ij.gui import GenericDialog
from ij.plugin import ChannelSplitter
from ij.plugin import Duplicator
from ij.process import ImageConverter, ImageProcessor, ByteProcessor,\
    ShortProcessor, FloatProcessor
from ij.plugin.filter import ThresholdToSelection
from loci.plugins import BF
from java.awt.event import ItemListener, AdjustmentListener
from java.awt import Color
import os
import datetime
import math
from org.python.core import codecs
codecs.setDefaultEncoding('utf-8')

#------------------------
def set_Lmax(imp):
    # set Maximum value of Lightness
    bitdepth = imp.getBitDepth()
    if bitdepth == 32:
        L_max = 1.0
    elif bitdepth == 16:
        L_max = 65535
    else: # 8 or 24 bit
        L_max = 255
    return L_max

def setMaxValueTo1(imp, bitdepth):
    if bitdepth !=32:
        return
    L_max = imp.getDisplayRangeMax()
    if int(L_max) > 65536:
        factor = 1.0 / 16777215
    elif int(L_max) > 256:
        factor = 1.0 / 65535
    elif int(L_max) > 1.01:
        factor = 1.0 / 255
    stk = imp.getStack()
    for i in range(1,4): # R:1, G:2, B:3
        stk.getProcessor(i).multiply(factor)
    print ("32bit image Max Value changed to 1.0")
#-----------------
def imageBinarySmpl(impOrg, lowerV, upperV, roiColor, maskname):
    # fill roiColor between lowerV - upperV
    # make binary mask image with setThreshold
    # lowerV: lowerThreshold / upperV: upperThreshold (double)
    # roiColor: Color Value, ROI filled with (double)
    # maskname: name of mask image (string)
    imp = Duplicator().run(impOrg)
    ip = imp.getProcessor()
    mask = ip.createProcessor(ip.width, ip.height) # Background Value = 0.0
    mask.setValue(0.0)
    mask.fill()

    mask.setValue(roiColor)
                    # with which color ROI filled (normally L_max)
    ip.setThreshold(lowerV, \
        upperV, ImageProcessor.NO_LUT_UPDATE)
    roi = ThresholdToSelection.run(imp)
    mask.setRoi(roi)
    mask.fill(roi)
    maskimp = ImagePlus(maskname, mask)
    imp.hide()
    imp.close()
    return maskimp
#-----------------
def clipUnder0(imp, Mode, L_max = 1.0):
    if Mode != "32-bit":
        return imp
    else:
        stat = imp.getStatistics()
        if stat.min >= 0.0:
            return imp
        else:
            ip = imp.getProcessor()
            ip_mask = FloatProcessor(ip.width, ip.height)
            ip_mask.setValue(0.0)
            ip_mask.fill()
            imp1 = ImagePlus("imp1", ip_mask)
            ic = plugin.ImageCalculator()
            imp2 = ic.run("Max created", imp, imp1) # bind masks
            imp2.setLut(None)
            imp1.close()
            imp.hide()
            imp.close()
            stat = imp2.getStatistics()
            if stat.min < 0.0:
                print ("clipunder0 is not effective")
    return imp2
#--------------------
def clipOverLmax(imp, Mode, Lmax):
    if Mode != "32-bit":
        return imp
    stat = imp.getStatistics()
    if stat.max < Lmax:
        return imp
    ip = imp.getProcessor()
    ip_mask = FloatProcessor(ip.width, ip.height)
    ip_mask.setValue(1.0 * Lmax)
    ip_mask.fill()
    imp1 = ImagePlus("imp1", ip_mask)
    ic = plugin.ImageCalculator()
    imp2 = ic.run("Min created", imp, imp1) # bind masks
    imp2.setDisplayRange(0.0, Lmax)
    imp1.hide()
    imp1.close()
    imp.hide()
    imp.close()
    return imp2
#----------------------------------------------
def clipValue(imp, Mode, Lmax):
    if Mode != "32-bit":
        return imp
    imp1 = clipUnder0(imp, Mode, Lmax)
    imp2 = clipOverLmax(imp1, Mode, Lmax)
    imp2.setDisplayRange(0.0, Lmax)
    imp.hide()
    imp1.hide()
    imp.close()
    imp1.close()
    return imp2

#----------------------------------------------
def printStat(Stat, string):
    print (string + " mean", Stat.mean)
    print (string + " min", Stat.min)
    print (string + " max", Stat.max)
    print (string + " mode", Stat.mode)
    print (string + " stdDev", Stat.stdDev)

#----------------------- 2022/7/5
def make_Gaussian_imp(imp_org, sigma0, Mode):
    imp_width = imp_org.getWidth()
    imp_height  = imp_org.getHeight()
    #-- adjustment Sigma of Extended pseudo FF correction width image pixel size (4.60c)
    imp0_long = max(imp_width, imp_height)
    sigma_factor = 10.0 / (imp0_long / 1000.0)
    sigma = sigma0 / sigma_factor

    swidth = int(imp_width / 4)
    sheight = int(imp_height / 4)
    imp_g = Duplicator().run(imp_org)
    if Mode == "8-bit":
        ImageConverter(imp_g).convertToGray32()
        # imp_g will be gauss image of level adjusted imp_org
    imp_g.resize(swidth, sheight, 'bilinear')
    IJ.run(imp_g, "Gaussian Blur...", "sigma=" + str(sigma))
    imp_g.resize(imp_width, imp_height, 'bilinear')
    if Mode == "8-bit":
        ImageConverter(imp_g).convertToGray8()
    imp_g.setDisplayRange(0.0, set_Lmax(imp_g))
    return imp_g
#------------------------------------
def get_cropped_imp_stat(imp):
    width = imp.getWidth()
    height = imp.getHeight()
    roi_x = int(width * 0.15)
    roi_y = int(height * 0.15)
    roi_width = int(width * 0.7)
    roi_height = int(height * 0.7)

    imp.setRoi(roi_x, roi_y, roi_width, roi_height)
    impTmp = imp.crop()
    stat = impTmp.getStatistics()
    impTmp.close()
    imp.resetRoi()
    return stat
#--------
# sub routine for pseud_Flat_field_C [32bit]
def levelAdjust4GB(parm_leveladj, imp_g1, imp_b1, \
                process, parm_correct_fac): # process == test
    """ imp_g1: G ch. Gaussan blur image
        imp_b1: B ch. Gaussan blur image """
    #if meanadjustment == True: # adjust level difference of G and B Ver. 3.53
        # modifyed in Ver. 5.3 / get statistics with cropped image
    stat_g1 = get_cropped_imp_stat(imp_g1)
    stat_b1 = get_cropped_imp_stat(imp_b1)
    if parm_leveladj == "Auto":
        if stat_g1.mean < stat_b1.mean:
            # adjust level difference of G and B Gaussian image (Ver. 3.52)
            ### Bug Fixed (Ver. 4.54) 2022_08_20
            imp_g1.getProcessor().add(stat_b1.mean - stat_g1.mean)
                            # imp_g1 must be lighter
            imp_g1 = clipValue(imp_g1, MODE32, LMAX32) # July, 2024
        elif (process == "B") and (stat_g1.mean > stat_b1.mean):
            imp_g1.getProcessor().subtract(stat_g1.mean - stat_b1.mean)
            imp_g1 = clipValue(imp_g1, MODE32, LMAX32)
            # when subtract higher region of B than G and G is higher than B
            # also adjust level difference of G and B Gaussian image
    elif parm_leveladj == "Manual":
                # Manual adjustment of correction amount of B
        if stat_g1.mean < stat_b1.mean:
            # adjust level difference of G and B Gaussian image (Ver. 3.52)
            imp_g1.getProcessor().add(stat_b1.mean - stat_g1.mean)
            imp_g1 = clipValue(imp_g1, MODE32, LMAX32) # July, 2024
        imp_g1.getProcessor().add(parm_correct_fac / 255.0 * LMAX32)
            # add correction value "parm_correct_fac"
            # make Manual value relative to auto adjusted value
#--------
# sub routine for pseud_Flat_field_C [32bit]
def subtractHigherBthanG(imp_b1, imp_g1, imp_c_b0):
    #  subtract higher region of B than G
    ic = plugin.ImageCalculator()
    imp_dif_bg = ic.run("Subtract create", imp_b1, imp_g1)
    stat = imp_dif_bg.getStatistics()
    if stat.min < 0.0:
        imp_dif_bg = clipUnder0(imp_dif_bg, MODE32, LMAX32)
    imp_dif_bg.setLut(None)
    imp_dif_bg.setTitle("imp_dif_bg")
    imp_dif_bg.show()
    imp_dif_bg.hide()

    imp_dif_bg.setDisplayRange(0.0, LMAX32)
    imp_c_b = ic.run("Subtract create", imp_c_b0, imp_dif_bg)
    imp_c_b.setDisplayRange(0.0, LMAX32)
    imp_c_b = clipValue(imp_c_b, MODE32, LMAX32)
    return imp_c_b, imp_dif_bg

#--------
def pseudo_Flat_field_C(imp_g0, imp_b0, sigma, \
        parm_subtractBv, parm_leveladj, parm_correct_fac): #[32bit]
    global CountN
    CountN = CountN + 1
    """
        imp_g0: reference ch. imp, imp_b0: target B ch. imp
        sigma: Gaussian blur sigma
        parm_subtractBv: Subtraction or not B when B is much higher than G
        parm_leveladj: adjust or not luminance level
        parm_correct_fac: manual correction factor
    """
    imp_b0.setDisplayRange(0.0, LMAX32) #stat.min
    ### Options for developping
    if parm_subtractBv == False:
        # No Subtraction B when B is much higher than G
        process = "A"  # Normal
    else:  # Subtraction B when B is much higher than G
        process = "B"  # optional > make G similer image
    if parm_leveladj != "No":
        meanadjustment = True # select mean adjustment True or False
    else:
        meanadjustment = False # select mean adjustment True or False
    ####
    imp_g1 = make_Gaussian_imp(imp_g0, sigma, "32-bit")
    imp_b1 = make_Gaussian_imp(imp_b0, sigma, "32-bit")
    imp_g1.setDisplayRange(0.0, LMAX32) #stat.min
    imp_b1.setDisplayRange(0.0, LMAX32) #stat.min
    """ imp_g1: G ch. Gaussan blur image imp_b1: B ch. Gaussan blur image """
    if meanadjustment == True: # adjust level difference of G and B Ver. 3.53
        levelAdjust4GB(parm_leveladj, imp_g1, imp_b1, \
                                    process, parm_correct_fac)

        # modifyed in Ver. 5.3 / get statistics with cropped image
    imp_g1 = clipValue(imp_g1, MODE32, LMAX32) # July, 2024
    imp_b1 = clipValue(imp_b1, MODE32, LMAX32) # July, 2024
    ic = plugin.ImageCalculator()
    imp_dif_gb = ic.run("Subtract create", imp_g1, imp_b1)
    imp_dif_gb = clipUnder0(imp_dif_gb, MODE32, LMAX32)
    imp_dif_gb.setDisplayRange(0.0, LMAX32)
    imp_dif_gb.setLut(None)
    imp_dif_gb.setTitle("imp_dif_gb")
    imp_dif_gb.show()
    imp_dif_gb.hide()
    stat = imp_dif_gb.getStatistics()
    imp_dif_gb.setDisplayRange(0.0, stat.max) #stat.min
    imp_c_b0 = ic.run("Add create", imp_b0, imp_dif_gb)
    imp_c_b0 = clipOverLmax(imp_c_b0, MODE32, LMAX32) # July, 2024
    stat_imp_c_b0 = imp_c_b0.getStatistics()
    if stat_imp_c_b0.min < 0.0:
        imp_c_b0 = clipUnder0(imp_c_b0, MODE32, LMAX32)
    imp_c_b0.setLut(None)
    imp_c_b0.setTitle("imp_c_b0")
                # image correction of B (subtract yellowed value from B)
    imp_c_b0.setDisplayRange(0.0, LMAX32) #stat_imp_c_b0.min

    if process =="B": #Optional Process
        # subtract higher region of B than G
        imp_c_b, imp_dif_bg = subtractHigherBthanG(imp_b1, imp_g1, imp_c_b0)
    else:
        # No Subtraction B when B is much higher than G
        imp_c_b = Duplicator().run(imp_c_b0) # for process "A"
    ##2024.6.9
    imp_c_b = clipValue(imp_c_b, MODE32, LMAX32)
    imp_c_b0.close()
    imp_b1.close()
    imp_g1.close()
    stat =imp_c_b.getStatistics()
    #return imp_c_b, imp_b1, imp_g1 #b1, g1: Gaussian blur images
    if process == "A":
        return imp_c_b, imp_dif_gb #b1, g1: Gaussian blur images
    else: # process B
        imp_dif_gb.close()
        return imp_c_b, imp_dif_bg #b1, g1: Gaussian blur images
#--------------------------
def make_imp_blueHigh(impRf2, impT):
    # [32bit]  Mode, L_max: for processing bitdepth not for reading file bitdepth
    """
    making relatively high value region image of target channel
                                                    with user make mask
    impPrvWin: imp of preview window / impPrv: imp of preview image (ref. ch.)
    imp_binaryMask / impPrvBinMask: imp of user made mask image
    """
    # impT: target ch. image
    # impRf2: reference ch. image
    # read user made binary mask
    od = OpenDialog("Choose user made mask image file")
    folder = od.getDirectory()
    filename = od.getFileName()
    imps_Umask = BF.openImagePlus(folder + filename)
        # Open user made mask image file with Bio-format Plugin
    imp_binaryMask = imps_Umask[0] # user's binary mask Image
    stackSize = imp_binaryMask.getStackSize()
    if stackSize == 3 or stackSize == 4: # if file image is RGB color...
        impTmp = imageConvert2Gray(imp_binaryMask)
        imp_binaryMask.close()
        imp_binaryMask = impTmp
    Mask_L_max = set_Lmax(imp_binaryMask)
    imp_binaryMask.setDisplayRange(0.0, Mask_L_max)

    # bit depth adjustment of original image and user's mask image
    ImageConverter(imp_binaryMask).setDoScaling(True)
    ImageConverter(imp_binaryMask).convertToGray32()
    if LMAX32 != Mask_L_max:
        imp_binaryMask.getProcessor().multiply(LMAX32 / Mask_L_max)
        imp_binaryMask.setDisplayRange(0.0, LMAX32)

            # adjustment for discrepancy of target image and user mask image
    if LnProcess == True:
        imp_binaryMask1 = sRGB_TRC_decode(imp_binaryMask, LMAX32)
    else:
        imp_binaryMask1 = imp_binaryMask
    # combine mask
    ic = plugin.ImageCalculator()
    impT_high0 =  ic.run("Min create", impT, imp_binaryMask1)
    # impT_high0: masked target (B) ch. image
    impRf2_high0 =  ic.run("Min create", impRf2, imp_binaryMask1)
    # impRf2_high0: masked reference (G) ch. image
    impT_high0.setDisplayRange(0.0, LMAX32)
    impRf2_high0.setDisplayRange(0.0, LMAX32)

    stat_impT_high0 = impT_high0.getStatistics()
        # statistics of target ch. (B)
    stat_impRf2_high0 = impRf2_high0.getStatistics()
        # statistics of ref. ch. (G)
    # making preview image for correct blue high region
    impPrv = make_preview(impRf2_high0, LMAX32)
    impPrvBinMask = make_preview(imp_binaryMask1, LMAX32)
    impPrvWin = Duplicator().run(impPrv)
        # impPrvWin: image for preview image window
    impPrvWin.setTitle("High blue region correction preview")
    impPrvWin.show()
    ### input correction factors ###
    factor = gd_input_blue_high_cFfactor\
        (impPrvWin, impPrv, impPrvBinMask, stat_impT_high0)
            # Show dialog for set parameter
    # set parameter for ref. ch. highlight region image
    impPrvWin.hide()
    impPrvWin.close()
    impPrv.close()
    if LnProcess == True:
        imp_binaryMask.close()
    ### end of  input factors --
    #  adjust mask image (ref. ch.) with factor
    impRf2_high0.getProcessor().add(stat_impT_high0.stdDev * factor)
    impRf2_high0 = clipOverLmax(impRf2_high0, MODE32, LMAX32)
    impRf2_high2 =  ic.run("Min create", impRf2_high0, imp_binaryMask1)
    impRf2_high0.close()
    impRf2_high0 = impRf2_high2
    impRf2_high0.setTitle("ref image of blue high region")
    impRf2_high0.getProcessor().setLut(None)
    #---
    if stat_impRf2_high0.mean < stat_impT_high0.mean:
                # adjust Rf ch. value
        impRf2_high0.getProcessor().\
            add(stat_impT_high0.mean - stat_impRf2_high0.mean)
        impRf2_high0 = clipOverLmax(impRf2_high0, MODE32, LMAX32)
        impRf2_high1 =  ic.run("Min create", impRf2_high0, imp_binaryMask1)
        impRf2_high0.close()
        impRf_highlight = impRf2_high1
    else:
        impRf_highlight = impRf2_high0
    impT_highlight  = impT_high0
    imp_binaryMask1.hide()
    return impT_highlight, impRf_highlight, imp_binaryMask
    # change for Ver. 5.8 imp_binaryMask1 > imp_binaryMask as mask is perceptural
    # impT_highlight: target ch. highlight region image for pFFc
    # impRf_highlight: ref. ch. highlight region image for pFFc

#--------------------------------
def gd_input_blue_high_cFfactor\
        (impPrvWin, impPrv, impPrvBinMask, statT):
    """
    impPrvWin: imp of preview window / impPrv: imp of preview image (ref. ch.)
    impPrvBinMask: imp of user made mask image
    statT: statistics of target ch.
    """
    gd = GenericDialog("Input correction factor for bluer region")
    gd.addSlider("Correction factor (%) x std Dev", 0, 300, 100)
    gd.addMessage("Normally 100 (%) is recommended.")
    #-- define UI elements ---
    slider_factor = gd.getSliders().get(0)
    nfield_slider_factor = gd.getNumericFields().get(0)
    #-- set previewer --------
    previewer = imagePreviewer(impPrvWin, impPrv, impPrvBinMask, \
                    slider_factor, nfield_slider_factor, statT)
    slider_factor.addAdjustmentListener(previewer)
    #-------------------------
    gd.showDialog()
    bluer_factor = gd.getNextNumber() / 100
    return bluer_factor
#--------------------------------
class imagePreviewer(AdjustmentListener):
    def __init__(self, impPrvWin, impPrv, impPrvBinMask, \
                slider_factor, nfield_slider_factor, statT):
        #    slider_*: a java.awt.Scrollbar UI element
        self.impPrvWin = impPrvWin # imp of preview windows
        self.impPrv = impPrv # preview image (ref. ch.)
        self.impPrvBinMask = impPrvBinMask # mask image
        self.slider_factor = slider_factor
        self.nfield_slider_factor = nfield_slider_factor
        self.stat = statT # statistics of target ch. (B)

    def adjustmentValueChanged(self, event):
        #event: an AdjustmentEvent with data on the state of the scroll bar.
        if (IJ.isMacOSX() == False) and event.getValueIsAdjusting():
            return
                # there are more scrollbar adjustment events queued already
        self.updatePrView()

    def updatePrView(self):
        factor = float(self.nfield_slider_factor.getText()) / 100.0
        impPrv = Duplicator().run(self.impPrv)
        impPrv.getProcessor().add(self.stat.stdDev * factor)
                            #stdDev must be calculated before masking?
        ic = plugin.ImageCalculator()
        impPrv1 =  ic.run("Min create", impPrv, self.impPrvBinMask)
        ip = impPrv1.getProcessor()
        self.impPrvWin.setProcessor(ip)
        # self.impPrvWin: canvas for preview image
        impPrv.close()
        impPrv1.close()
        self.impPrvWin.updateAndDraw()
#--------------------------------
def make_preview(imp, L_max):
    """ making preview image (32bit) """
    impprv0 = Duplicator().run(imp) # impprv: preview imp
    if LnProcess == True:
        impprv = sRGB_TRC_encode(impprv0, L_max)
        impprv0.close()
    else:
        impprv = impprv0
    impprv.setDisplayRange(0.0, L_max)
    scale = 1280.0 / impprv.getWidth()
                    # preview image scaling value (width = 1200px fixed)
    new_height = impprv.getHeight() * scale # preview image height
    new_height = int(new_height)
    impprv1 = impprv.resize(1280, new_height, "none")
    impprv1.getProcessor().setLut(None)
    return impprv1
#----------------------------------
#---mask for excluding bluer region from correction (for Hybrid correction)
def make_blue_mask(impG, impB, adjstV, inverse):
    # [32bit process] always perceptural mask
    # This routine is making mask for foreground layer
    # multFactor: defining factor of mask opacity
    ic = plugin.ImageCalculator()
    imp8_B = Duplicator().run(impB) # corrected B ch.
    imp8_G = Duplicator().run(impG) # G ch.
    # modifyed in Ver. 5.3 / get statistics with cropped image
    statB = get_cropped_imp_stat(impB)
    statG = get_cropped_imp_stat(impG)
    if statG.mean < statB.mean: #adjust B value relative to G
        imp8_G.getProcessor().add(statB.mean - statG.mean)
                                            # del int 4.82b
        imp8_G = clipOverLmax(imp8_G, MODE32, LMAX32)

    imp8_G.getProcessor().subtract(adjstV / 255.0 * LMAX32)
                                # adjusting blue threshold
    imp8_G = clipUnder0(imp8_G, MODE32, LMAX32)
    imp8_B_G = ic.run("Subtract create", impB, imp8_G)
    stat = imp8_B_G.getStatistics()
    imp8_B_G = clipValue(imp8_B_G, MODE32, LMAX32)
    imp8_G.close()
        # extract more green region
    stat = imp8_B_G.getStatistics()
    printStat(stat, "imp8_B_G")
    # set new white point / 5% of pixels will be clipped / WP
    multFactor2 = 1.0 / (stat.mean + stat.stdDev * 2)
    print("New multi factor in blue mask: ", multFactor2)
    imp8_B_G.getProcessor().multiply(multFactor2)
        # define multiplying factor automatically
        # adjusting transparency changed Dec. 2024
    imp8_B_G = clipOverLmax(imp8_B_G, MODE32, LMAX32)
    imp8_B_G.setDisplayRange(0.0, LMAX32)
    if inverse == True:
        IJ.run(imp8_B_G, "Invert", "") # bluer region  > blacker (opaque)
                # not to apply correction in bluer region
    imp8_0 = imp8_B_G
    stat = imp8_0.getStatistics()
    if stat.min < 0.0 or stat.max > 1.0:
        imp8_0 = clipValue(imp8_0, MODE32, LMAX32)
    imp8_0.getProcessor().blurGaussian(1.0)
    imp8_0.setDisplayRange(0.0, LMAX32)
    imp8_B.close()
    imp8_G.close()
    return imp8_0
    #imp8_0: region bluer > more opaque (not to apply correction)
#---mask for preserving plants green area from correction
#                                       (for pseudo FF correction)
def make_green_prsv_mask_new(impG, imp_c_b, impB, impR, \
                                parm_Binary): # [32bit]
    """
    This routine is for preserving plants green option of foreground
                layer mask in Hybrid / pseudo flat-field correction algorism
    impG: G ch. / imp_c_b: corrected B ch. / impB: original B ch. / impR: R ch.
    imp_R130: mask for background correction mask (R130) > but no use
    parm_Binary: imp_R130's threshold > but no use
    #!!!!!! imp_c_b may context adjusted B
    """
    ic = plugin.ImageCalculator()
    imp8_cB = Duplicator().run(imp_c_b) # corrected B ch.
    imp8_G = Duplicator().run(impG) # G ch.
    imp8_Brestriction = Duplicator().run(impB) # B ch.
    imp8_cB.setDisplayRange(0.0, LMAX32)
    imp8_G.setDisplayRange(0.0, LMAX32)
    imp8_Brestriction.setDisplayRange(0.0, LMAX32)
    # making exclucing region image
    Threshold = parm_Binary # changed in 4.82
        # threshold of highlight / midtone
    #cThreshold = Threshold / 255.0 * L_max
    Threshold = Threshold * 1.1 # change for LnProcess
    imp8_B130 = make_bg_mask(imp8_cB, Threshold, MODE32, LMAX32)
    imp8_B130.getProcessor().invert()
    imp8_B130.setDisplayRange(0.0, LMAX32)
        # ROI (under 130) will be filled with white
        # under 130 > white and over 130 > black

    # making relative green mask process start
    statR = impR.getStatistics()
    # level adjustment of R and G
    stat8_G = imp8_G.getStatistics()
    if stat8_G.mean < statR.mean: #adjust R value relative to G
        imp8_G.getProcessor().add(statR.mean - stat8_G.mean)
                                # del int 4.82b
        imp8_G = clipOverLmax(imp8_G, MODE32, LMAX32)
    imp8_R = modify1stCh(impR, imp8_G, 0.80)
    # R ch. modified 2024.6 to make min value at yellowish green R:G = 0.8:1
    imp8_R.setDisplayRange(0.0, LMAX32)
    # making reference image to make relative green area image
    # making R x 0.7 + corrected B x 0.3 image (mixing ratio changed aug. 2024)
    imp8_R.getProcessor().multiply(0.70) ## variable?
                            # image R x 0.70 #*** 2024/8/6 revised
    # revised R (2024.8)
    imp8_cB.setDisplayRange(0.0, LMAX32)
    # add gamma correction to image B for proximity compensation of B & G
    imp8_cB.getProcessor().gamma(1.7)
    # revised R (2024.6)
    imp8_cB.getProcessor().multiply(0.30) # image B x 0.3* 2024/8/6 revised
    imp8_RandB = ic.run("Add create", imp8_R, imp8_cB)
    stat = imp8_RandB.getStatistics()
    if stat.min < 0.0 or stat.max > 1.0:
        imp8_RandB = clipValue(imp8_RandB,"32-bit", LMAX32)
    imp8_RandB.setDisplayRange(0.0, LMAX32)
        # imp8_RandB: R and B mixing image
    # G - (R x 0.7 + B x 0.3) for making yellowish green sensitive mask
    # changed R x 0.3 + B x 0.7 to R x 0.7 + B x 0.3
    stat = imp8_G.getStatistics()
    imp8_G = clipOverLmax(imp8_G, MODE32, LMAX32)
    # making relative green area image / G ch. - ref. image
    imp8_G_cB = ic.run("Subtract create", imp8_G, imp8_RandB)
    stat = imp8_G_cB.getStatistics()
    if stat.min < 0.0:
        imp8_G_cB = clipUnder0(imp8_G_cB, MODE32, LMAX32)
    factor = 1.0 / (stat.mean + stat.stdDev * 2.5) #(5.9-2)
    imp8_G_cB.getProcessor().multiply(factor)
    imp8_G_cB.setDisplayRange(0.0, LMAX32)
        # data clip for 32bit image
    # clipping over mid gray point area from relative green image
    imp8b = ic.run("Min create", imp8_G_cB, imp8_B130)
    if stat.min < 0.0:
        imp8b = clipUnder0(imp8b, MODE32, LMAX32)
    imp8b.setDisplayRange(0.0, LMAX32)
        # excluding over 130 (Threshold) area
        # under 130: relative G image / over 130: black
    # deciding multiplying factor
    stat8b = imp8b.getStatistics()
    mult_factor = 1.0 / (stat8b.mean + stat8b.stdDev * 2)
    print ("imp8b multiplying factor: ", mult_factor)
#    mult_factor = mult_factor * 2.0
        #culculate multiplying factor
    imp8b.getProcessor().multiply(mult_factor)
                                        # mask x multiplying factor
    stat8b = imp8b.getStatistics()
    if stat8b.max > 1.0:
        imp8b = clipOverLmax(imp8b, MODE32, LMAX32)
    imp8b.setDisplayRange(0.0, LMAX32)
    stat8b = imp8b.getStatistics()
        # more green region  > more black (opaque) and over region 130 > white

    #----------------------------------------------------------------
    imp8b.getProcessor().gamma(1.0 / 4.0)
    imp8b.setDisplayRange(0.0, LMAX32)
    imp8_cB.close()
    imp8_R.close()
    imp8_G_cB.close()
    imp8_B130.close()
    imp8b.close()
    imp8_Brestriction.close()
    return imp8b
    """
    imp8b:
        region relatively green > more opaque
    """
#------2024.6---------
def modify1stCh(imp1, imp2, maxrate):
    # to make minimum value at R:G = 0.8:1 in green protect mask
    # 32bit process
    imp2Tmp = imp2.duplicate()
    imp2Tmp.getProcessor().multiply(maxrate)
    ic = plugin.ImageCalculator()
    impTmp1 = ic.run("Difference create", imp1, imp2Tmp)
         #ABS(R-G*0.8) or ABS(G-R*0.65)
    imp2Tmp.close()
    impTmp3 = ic.run("Subtract create", imp1, imp2)
    impTmp3.getProcessor().multiply(2.0)
    impTmp3 = clipValue(impTmp3,  "32-bit", LMAX32)
    impTmp4 = ic.run("Max create", impTmp1, impTmp3)
    impTmp1.close()
    impTmp3.close()
    impTmp4.setDisplayRange(0.0, LMAX32)
    return impTmp4
#-----Image Convert to Gray-------------
# 3 channel color image to mono channel grayscale image / Ver. 4.71
def imageConvert2Gray(imp):
    Lmax = set_Lmax(imp)
    impsCh2 = ChannelSplitter.split(imp) #Channel Splitting of imp0
    ic = plugin.ImageCalculator()
    for i in impsCh2:
        if i == 0:
            i.getProcessor().multiply(0.299) # R
        elif i ==1:
            i.getProcessor().multiply(0.587) # G
        elif i ==2:
            i.getProcessor().multiply(0.114) # B
    impTmp0 = ic.run("Add create", impsCh2[0], impsCh2[1])
    impTmp0.setDisplayRange(0.0, Lmax)
    impTmp1 = ic.run("Add create", impTmp0, impsCh2[2])
    impTmp1.setDisplayRange(0.0, Lmax)
    impTmp0.close()
    return impTmp1

#---------------------------------
def preMixing(impT, impPrMix, parm_FFC_sigma, parm_correct_fac):
    # 32bit process
    # add parm_correct_fac Ver. 4.71
    """
        This routine is for premixing G channel to B for original B image
        impT: Target Ch. (B) impPrMix: Pre mixing Ch. (G)
    """
    # imp_c_Mx: modified mixing G ch.
    # in arg. pseudo_Flat_field_C below: target: impPrMix(G) ref: impT(B)
    # it's opposite to normal!! Aug. 2024
    imp_c_Mx, imp_dif_gb = \
        pseudo_Flat_field_C(impT, impPrMix, parm_FFC_sigma * 4, \
            True, "Auto", parm_correct_fac)
    imp_dif_gb.close()
    # improve B image with G texture info
    # if parm_FFC_sigma is larger, then image quality will
    #                                   be much better for pre mixing
    # make adjusted G ch.
    # imp_c_Mx: corrected Mixing ch.
    #   parm_subtractBv: True, parm_leveladj: "Auto"
    impT_tmp03 = Duplicator().run(impT) # B ch.
    # modifyed in Ver. 5.3 / get statistics with cropped image
    # > deleted July, 2024
    impT_tmp03.getProcessor().multiply(0.3) # B ch. 0.3
    imp_c_Mx.getProcessor().multiply(0.7) # Modified G ch. 0.7

    impT_tmp03.setDisplayRange(0.0, LMAX32 * 0.3)
    imp_c_Mx.setDisplayRange(0.0, LMAX32 * 0.7)
    impT.close()
    ic = plugin.ImageCalculator()
    impT = ic.run("Add create", impT_tmp03, imp_c_Mx)
    #add 2024.7.18
    impT = clipValue(impT, MODE32, LMAX32)
    #--
    impT_tmp03.close()
    imp_c_Mx.close()

    return impT # An image of impG resembling impB

#-----------------
def extractDarkArea(darkThreshold, imp):
    # 32bit process
    # attention!: imp's max value may be higher than L_max
    imp1 = Duplicator().run(imp)
    imp1.setDisplayRange(0.0, LMAX32)
    ip1 = imp.getProcessor()

    blank_mask = FloatProcessor(ip1.width, ip1.height)
    blank_mask.BLACK_AND_WHITE_LUT
    blank_mask.setValue(LMAX32) # ROI: L_max / otherwise: 0.0
    blank_mask.fill()
    impDarkMask = ImagePlus("Dark parts layer mask", blank_mask)

    ip2 = impDarkMask.getProcessor()

    if LnProcess == True: # adjustment threshold value for Linear Process
        Threshold = (darkThreshold / 255.0) ** 2.2
    else:
        Threshold = (darkThreshold / 255.0)

    imp1.getProcessor().setThreshold((Threshold * LMAX32), \
        1.0, ImageProcessor.NO_LUT_UPDATE)
    print ("Dark Threshold ", darkThreshold, "=", \
           (darkThreshold / 255.0) * LMAX32, LMAX32)
    roi = ThresholdToSelection.run(imp1)
    # Dark Layer image
    ip1.setValue(LMAX32)
    ip1.setRoi(roi)
    ip1.fill(roi)
    imp1.setProcessor(ip1)
    # Mask for Dark Layer image
    ip2.setValue(0)
    ip2.setRoi(roi)
    ip2.fill(roi)
    impDarkMask.setProcessor(ip2)

    return imp1, impDarkMask
#-----------------------------------------
def outputImg(imp, Mode, L_max, TRCType, MaskOutput = False):
    # variable bitdepth process
    # convert 32bit max 1.0 image to 16 or 8 bit image
    # Mode: to convert bitdepth L_max: converted max value
    imp.setDisplayRange(0.0, LMAX32)
    if TRCType == "Perceptual" and LnProcess == True and MaskOutput == False:
        # Linear to Perceptual
        impOut = sRGB_TRC_encode(imp, LMAX32)
                                # L_max' 1.0
    else: #originally perceptual or Ln to Ln
        impOut = imp.duplicate()
    impOut.getProcessor().setLut(None)
    ImageConverter(impOut).setDoScaling(True)
    if Mode == "8-bit":
        ImageConverter(impOut).convertToGray8()
    elif Mode == "16-bit":
        ImageConverter(impOut).convertToGray16()
    impOut.setDisplayRange(0.0, L_max) # L_max is variable with bitdepth
    return impOut
#-----------------------------------------
def RGBmanipulation(folder, filename, parm, imp0, L_max, Mode, TRCType):
    # variable bitdepth process
    # in this routine level, Mode and L_max is of input image
    # in sub level, Mode and L_max is of 32-bit image processing
    print ("LnProcess: " + str(LnProcess))
#  input Parameters
    if parm is not None:
        _, parm_Binary, parm_Shadow, Show, parm_Prph, \
            parm_Fg_tL, _, parm_FFC_sigma, _, _, _, \
            parm_correct_fac, _, _, _, _, DoPreMixing, _ = parm
        print(Mode, parm_Binary, parm_Shadow, Show, parm_Prph, \
                                                                parm_Fg_tL)
#     Mode: Image bit depth
#     L_max: Maximum Value of imp0
#     TRCType: Type of TRC
#     Show: Show working windows or not
#     parm_Binary: Threshold of fore / background
#     parm_Gain: Lightness of Distance adjusting Layer
#     parm_Shadow: Threshold of Dark part adjustment image
#     parm_Prph: Threshold of Periphery adjustment image
#     parm_Fg_tL: Perceptual (128) or Linear (56)
#     parm_Gmixrate: G mixing rate for foreground image in EPFFC only mode
#     parm_subtractBv: execute value subtraction of B value higher region
#     parm_leveladj: execute level adjustment G and B and it's type: Auto/Manual/None
#     parm_correct_fac: Manual correction factor
#     HybridC: execute hybrid correction or not and it's type
#     bgG_Gamma: Gamma value of mixing G for background image
#     Fg_mask_type: Type of mask for foreground layer Type1 to 4 and None
#     parm_ReadMask: read user made mask True/False
#     DoPreMixing: Do Premixing G to B for original B image True/False
# Always Open file with Bio-format Plugin
    #imp0 = imps[0] #imp0: Original Image

    if Mode == "32-bit":
        IJ.showMessage("32-bit image Lightness Max Value: " + str(L_max))
    imp0.setDisplayRange(0.0, L_max)
    if Show == "Yes":
    # Windows Showing Mode
        imp0.show()
    #------
    impsCh0 = ChannelSplitter.split(imp0)
    impsCh = []
    for i in impsCh0: # it needs to convert Grayscale image
        if Mode == "8-bit":
            i.getProcessor().setLut(None)
        ImageConverter(i).convertToGray32()
        i.getProcessor().multiply(1.0 / L_max) # Convert to 32bit & max 1.0
        i.setDisplayRange(0.0, LMAX32)
        if TRCType == "Perceptual" and LnProcess == True:
            impTmp = sRGB_TRC_decode(i, LMAX32)
            impsCh.append(impTmp) # Convert to Linear for Linear process
        else:
            impsCh.append(i)
    #-- auto correction for very yellow image ---
    adjustedB, Conversion = B_level_adjustment(impsCh, "32-bit", 1.0, "Linear")
            # for Ln process Mode, L_max > 32-bit, 1.0 # Mode, L_max, TRCType
    if Conversion == True:
        impsCh[2] = adjustedB
    ## R ch. / impRprc: perceptual
    impR =  impsCh[0]
    impRprc =  impsCh0[0]
    impRout = outputImg(impR, Mode, L_max, TRCType)
    IJ.saveAsTiff(impRout, folder + filename + "_R.tif")
    impRout.close()
    ## G ch. / impGprc: perceptual
    impG =  impsCh[1]
    impGprc =  impsCh0[1]
    impGout = outputImg(impG, Mode, L_max, TRCType)
    IJ.saveAsTiff(impGout, folder + filename + "_G.tif")
    impGout.close()
    ## B ch. & B x 0.3  / impBprc: perceptual
    impB = Duplicator().run(impsCh[2])
    impBout = outputImg(impB, Mode, L_max, TRCType)
    if DoPreMixing == False:
        IJ.saveAsTiff(impBout, folder + filename + "_B.tif")
        impBprc = Duplicator().run(impsCh0[2])
    else: # execute premixing G to B
        imp_nB = preMixing(impB, impG, parm_FFC_sigma / 3, parm_correct_fac)
                            # for Ln process Mode, L_max > 32-bit, 1.0
        imp_nBout = outputImg(imp_nB, Mode, L_max, TRCType)
        IJ.saveAsTiff(imp_nBout, folder + filename + "_B.tif")
        IJ.saveAsTiff(impBout, folder + filename + "_Borg.tif")
        print("premixing B out")
        imp_nBout.close()
        impB.close()
        impB = imp_nB
        impBprc = sRGB_TRC_encode(impB, LMAX32)
    impBout.close()
        # replace impB with premixing & corrected B image
    for i in impsCh0:
        i.close()
    return impR, impG, impB, impRprc, impGprc, impBprc # Linear / Perceptual
#---------------------
def base(impR, impG, impB, impRprc, impGprc, impBprc, folder, filename, \
                                                parm, L_max, Mode, TRCType):
    # substantial main routine / variable bitdepth process
    # in this routine, imps are basically processed in 32bit
    # Mode and L_max is bitdepth of output image
    MASK_OUTPUT = True
    if parm is not None:
        _, parm_Binary, parm_Shadow, Show, parm_Prph, \
            parm_Fg_tL, bool_PFFcOnly, parm_FFC_sigma, parm_Gmixrate, \
            parm_subtractBv, parm_leveladj, parm_correct_fac, HybridC, \
            _, bgG_gamma, parm_ReadMask, _, discolorOut = parm
        print(Mode, parm_Binary, parm_Shadow, Show, parm_Prph, parm_Fg_tL)
        if LnProcess == True: # for Ver. 5.8 as masks are always perceptural
            parm_Binary_mask = (float(parm_Binary / 255.0)** (1.0/2.2)) * 255.0
            parm_Shadow_mask = (float(parm_Shadow / 255.0)** (1.0/2.2)) * 255.0
        else:
            parm_Binary_mask = parm_Binary
            parm_Shadow_mask = parm_Shadow
    # Show Windows
    if Show == "Yes":
        impR.show()
        impG.show()
        impB.show()
    #--- preparetion for making layer images / to make (R+G)/2 image
    #         & periphery correction layer
    # G ch. x 0.5 / Make impG05  as G x 0.5 Image
    impG05 = Duplicator().run(impG)
    if Show == "Yes":
        impG05.show()
    impG05.getProcessor().multiply(0.5)
    impG05.setDisplayRange(0.0, 0.5)
    # R ch. x 0.5 / Make impR05  as R x 0.5 Image
    impR05 = Duplicator().run(impR)
    if Show == "Yes":
        impR05.show()
    impR05.getProcessor().multiply(0.5)
    impR05.setDisplayRange(0.0, 0.5)
    # B x 0.5 + R x 0.5 -- make peripherry corrction layer
    ic = plugin.ImageCalculator()
    imp5 = ic.run("Add create", impR05, impG05) # No window made
    imp5.setDisplayRange(0.0, LMAX32)

    #-----save periphery correction layer (no pseudo FF correction)
    print ("imp5put")
    if (bool_PFFcOnly == False) and (HybridC == "Off"):
        # traditional algorism
        imp5out = outputImg(imp5, Mode, L_max, TRCType)
        IJ.saveAsTiff(imp5out, folder + filename + "_Periph_Adjst.tif")
        imp5out.close()
    #-----save periphery correction layer (pseudo FF correction)
    else: # Hybrid & EPFFc
        imp_periph, imp_dif_gb = \
            pseudo_Flat_field_C(imp5, impB, parm_FFC_sigma, \
                    True, parm_leveladj, parm_correct_fac)
        # Make periphery correction image with exPFFc
                                    # for Ln process Mode, L_max > 32-bit, 1.0
        if discolorOut == True:
            IJ.saveAsTiff(imp_dif_gb, \
                folder + filename + "_Periph_blued_area.tif")
        imp_dif_gb.close()
           # do pseudo FF correction with option subtract B higher region
           # reference image must be impG or imp5?
        print ("imp_periphout")
        imp_periphout = outputImg(imp_periph, Mode, L_max, TRCType)
        IJ.saveAsTiff(imp_periphout, folder + filename + "_Periph_Adjst.tif")
        imp_periphout.close()
        imp_periph.close()
    if Show == "Yes": #Hide the image windows to close
        imp5.show()
        impR05.hide()
        impG05.hide()
    impR05.close()
    impG05.close()
    #--- making foreground layer image
    imp3, impB03, imp_c_b, impUserMask, imp_dif_gb = making_Fg_image(impG, \
        impB, parm_FFC_sigma, HybridC, parm_subtractBv, parm_leveladj, \
        parm_correct_fac, bool_PFFcOnly, parm_Gmixrate, parm_ReadMask, Show)
                        # for Ln process Mode, L_max > 32-bit, 1.0
        # imp3: corrected foreground layer image
        # impB03: imp3 x 0.3 image
        # imp_c_b: extended pseudo flat-field correction applyed B
    print("imp3out")
    ##2024.6.10
    imp3 = clipUnder0(imp3, MODE32, LMAX32)
    imp3out = outputImg(imp3, Mode, L_max, TRCType)
    IJ.saveAsTiff(imp3out, folder + filename + "_B_Fg.tif")

    if discolorOut == True: # output discolord amount as image
        imp_dif_gbout = outputImg(imp_dif_gb, Mode, L_max, TRCType)
        IJ.saveAsTiff(imp_dif_gbout, folder + filename + "_yellowed_area.tif")
        imp_dif_gbout.close()

    imp3out.close()
    imp_dif_gb.close()
    print("imp3out ended")
        # even in new alogorithm G mixing is needed to improve texture
    #--- making fg brown correction layer
    print("call making brounC layer")
    impBrownC = making_brownC_layer(imp3)
                # for Ln process Mode, L_max > 32-bit, 1.0
    print("impBrownCout")
    ##2024.6.10
    impBrownC = clipUnder0(impBrownC, MODE32, LMAX32)
    impBrownCout = outputImg(impBrownC, Mode, L_max, TRCType)
    IJ.saveAsTiff(impBrownCout, folder + filename + "_Brown_Adjst.tif")
    impBrownCout.close()
    impBrownC.close()

    # --- making fg brown correction layer mask --(Perceptural)---
    impBrownMask = \
        making_brownC_layer_mask(impRprc, impGprc, parm_Binary_mask)
    print("impBrownMaskout")
    impBrownMaskout = \
        outputImg(impBrownMask, Mode, L_max, TRCType, MASK_OUTPUT)
    IJ.saveAsTiff(impBrownMaskout, folder + filename +  \
                                    "_Brown_Adjst_Mask.tif")
    impBrownMask.close()

    #--- making background layer image
    imp4 = making_Bg_image(impG, imp3, bool_PFFcOnly, \
                        bgG_gamma, 15, Show) # parm_Gain = 10
            # for Ln process Mode, L_max > 32-bit, 1.0
        #imp4: background layer image / bgG_gamma : gamma for imp4
    print("imp4out")
    imp4out = outputImg(imp4, Mode, L_max, TRCType)
    IJ.saveAsTiff(imp4out, folder + filename + "_B_Bg.tif")
    imp4out.close()
    imp4.close()
    if Show == "Yes":
        impB03.hide()
    impB03.close()
    #--- making Periphery Adjustment Mask --(Perceptural)--
    # Periphery Adjustment Mask: B - ((R+G)/2-10)
    if LnProcess == True: # Linear Process
        imp5in = sRGB_TRC_encode(imp5, LMAX32)
    else: # Perceptural process
        imp5in = imp5
    imp2GR = making_Prph_adj_Mask(imp5in, impBprc, parm_Prph, \
                    bool_PFFcOnly, Show)
                                # for Ln process Mode, L_max > 32-bit, 1.0
        # imp2GR: Periphery adjustment mask image
    print("imp2GRout")
    imp2GRout = outputImg(imp2GR, Mode, L_max, TRCType, MASK_OUTPUT)
    IJ.saveAsTiff(imp2GRout, folder + filename + "_Periph_Adjst_Mask.tif")
    if Show == "Yes":
    #Hide the image windows to close
        imp2GR.hide()
    # close the image
    imp2GR.close()
    imp2GRout.close()

    #--- making dark parts correction layer
    #  attention! the mask will be linear based if process is linear
    # Dark Parts 0 - parm_Shadow
    imp6, impDarkMask = extractDarkArea(parm_Shadow_mask, imp5)
                                # for Ln process Mode, L_max > 32-bit, 1.0
    imp_dark = imp6
    imp_dark.setDisplayRange(0.0, LMAX32)
    impDarkMask.setDisplayRange(0.0, LMAX32)

    print("imp_darkout")
    imp_darkout = outputImg(imp_dark, Mode, L_max, TRCType)
    IJ.saveAsTiff(imp_darkout, folder + filename + "_Shadow.tif")
    imp_darkout.close()
    print("imp_darkMaskout")
    imp_darkMaskout = \
        outputImg(impDarkMask, Mode, L_max, TRCType, MASK_OUTPUT)
    IJ.saveAsTiff(imp_darkMaskout, folder + filename + "_Shadow_Mask.tif")
    if Show == "Yes":
    #Hide the image windows to close
        imp5.hide()
        imp6.hide()
    imp5.close()
    #Close the images
    imp6.close()
    #--- making background correction mask  --(Perceptural)--
    imp9_0 = make_bg_mask(impRprc, parm_Binary_mask, MODE32, LMAX32) # Ver. 5.3a
                        # for Ln process Mode, L_max > 32-bit, 1.0
                    # Make Bg Mask: black for under threshold
                    # parm_Binary: Bg Layer threshold
                    # Bg Threshold +/-20 > gradient (in Ver. 5.3a)
    # White out the part specified by the user specified mask Aug. 2024
    if parm_ReadMask == True:
        imp9 = ic.run("Max create", imp9_0, impUserMask)
    else:
        imp9 = imp9_0
    impUserMask.close()

    if Show == "Yes":
        imp9.show()
    print("imp9out")
    imp9out = outputImg(imp9, Mode, L_max, TRCType, MASK_OUTPUT)
    IJ.saveAsTiff(imp9out, folder + filename + "_Bg_Thld_Mask.tif")
    imp9out.close()
    #--- making foreground correction mask  --(Perceptural)--
    # R + parm_Fg_tL(128) ## mask image for foreground correction layer
    adjstV = 0.0 # blue threshold
    imp8 = make_blue_mask(impGprc, impBprc, adjstV, True)
             #True<Inverse / for Ln process Mode, L_max > 32-bit, 1.0

        # imp8: Fg layer mask image
    if Show == "Yes":
        imp8.show()
    print("imp8out")
    imp8out = outputImg(imp8, Mode, L_max, TRCType, MASK_OUTPUT)
    imp8out.setDisplayRange(0.0, L_max)
    imp8out.getProcessor().setLut(None)
    IJ.saveAsTiff(imp8out, folder + filename + "_Fg_Mask.tif")
    imp8out.close()
    #--- making Fg mask suppliment image for yellow parts mask --(Perceptural)--
    #--------- for reserving editing of yellow parts (from Ver. 5.5 [2023.12])
    process = "B"

    if LnProcess == True: # for Ver. 5.8 as masks are always perceptural
        imp3in = sRGB_TRC_encode(imp3, LMAX32)
        imp8in = sRGB_TRC_encode(imp8, LMAX32)
    else:
        imp3in = imp3
        imp8in = imp8
    impRinv = make_yellow_parts(process, impRprc, imp3in, Mode, L_max)
                        #impRinv: impR inverted or impR - imp3(corrected B)
                            # Mode, L_max are output Mode & L_max
    imp8Yreserve = ic.run("Min create", imp8in, impRinv) #imp8: fg_layer_mask
    print("LnProcess = " + str(LnProcess))
    stat = imp8.getStatistics()
    printStat(stat, "imp8")
    print("imp8Yreserveout")
    imp8Yreserveout = outputImg(imp8Yreserve, Mode, L_max, TRCType, MASK_OUTPUT)
    IJ.saveAsTiff(imp8Yreserveout, folder + filename + "_Fg_Mask_Yrsv.tif")
    impRinv.close()
    imp8Yreserve.close()
    imp8Yreserveout.close()
    if Show == "Yes":
        imp8.hide()

    #--- making green correction layer
    impGreen = Duplicator().run(impB)
    print("impGreenout")
    impGreenout = outputImg(impGreen, Mode, L_max, TRCType)

    IJ.saveAsTiff(impGreenout, folder + filename + "_Green_Adjst.tif")
    impGreenout.close()
    impGreen.close()
    #--- making green correction layer mask --(Perceptural)--
    if LnProcess == True:
        imp_c_b_in = sRGB_TRC_encode(imp_c_b, LMAX32)
    else:
        imp_c_b_in = imp_c_b
    imp8b = make_green_prsv_mask_new(impGprc, imp_c_b_in, \
                        impBprc, impRprc, parm_Binary_mask) # change 4.71
                                # for Ln process Mode, L_max > 32-bit, 1.0
    imp8b.setDisplayRange(0.0, LMAX32)
    adjstV = 20
    impBlmask = \
        make_blue_mask(impGprc, impBprc, adjstV, True)
            # inverse = True / for Ln process Mode, L_max > 32-bit, 1.0

    ic = plugin.ImageCalculator()
    impGreenC_mask =  ic.run("Min create", imp8b, impBlmask) # Nov. 2023
    impGreenC_mask = clipValue(impGreenC_mask, MODE32, LMAX32)
    # adjust the transparency of green mask automatically (Ver. 5.8)
    # make mask mean value to 0.10 with exponent
    stat = impGreenC_mask.getStatistics()
    printStat(stat, "green mask: ")
    if stat.mean > 0.3:
        impGreenC_mask.getProcessor().gamma(math.log(0.10, stat.mean))
        stat = impGreenC_mask.getStatistics()
        printStat(stat, "green mask revised: ")
    #
    imp8.close()
    impBlmask.close()

# 2025.01 / making mask with shadow curve + straight line
    node_y = 0.15 # y value of the junction of curve and line
    gamma = 1.5 # gamma of shadow curve
    slopeFactor, y_intercept = \
                    get_slope_parameters(gamma, node_y)
    # needs * L_max / y_intercept is munus!
    impGreenC_mask = add_shadow_curve2mask\
        (impGreenC_mask, gamma, node_y, y_intercept, slopeFactor)
#
    print("impGreec_maskout")
    impGreenC_maskout = \
        outputImg(impGreenC_mask, Mode, L_max, TRCType, MASK_OUTPUT)

    IJ.saveAsTiff(impGreenC_maskout, folder + filename + \
                                        "_Green_Adjst_Mask.tif")

    impGreenC_maskout.close()
    imp_c_b.close() # pseud FFc image of B
    imp3.close() # Fg correction image
    #---------------------
    if Show == "Yes":
    #Hide the imp9 window to close
        imp9.hide()
        impB.hide()
    imp9.close()
    impR.close()
    impG.close()
    impB.close()

#----------------------
def make_yellow_parts(process, impR, imp3, outMode, \
                            outL_max):
    # Output is not 32bit!! bit-depth may be various
    impRtmp = Duplicator().run(impR)
    ic = plugin.ImageCalculator()
    if process == "A": # A or B
        # impRinv is impR - imp3 (Revised impB)
        statR = impR.getStatistics()
        stat3 = imp3.getStatistics()
        if statR.mean < stat3.mean:
            impRtmp.getProcessor().add(stat3.mean - statR.mean)
            impRtmp = clipValue(impRtmp, MODE32, LMAX32) ## modifyed 2024.6.15
        impRinv =  ic.run("Subtract create", impRtmp, imp3) # relative R image
        impRinv = clipUnder0(impRinv, MODE32, LMAX32) ## modifyed 2024.6.15
        statImpR_3 = impRinv.getStatistics()
        impRtmp.close()
        impRinv.show()
        IJ.showMessage(str(statImpR_3.max))
        impRinv.hide()
        factor = (1.0 / statImpR_3.max) * 2
        factor = 10.0
        impRinv.getProcessor().multiply(factor)
        print("impRinvout")
        impRinvout = outputImg(impRinv, outMode, outL_max, TRCType)
        IJ.saveAsTiff(impRinvout, path + "_impR-imp3.tif")
        impRinvout.close()
    else: ## process B impRinv is only invert impR
        impRinv = impRtmp
        stat = impRinv.getStatistics()
        printStat(stat, "impRinv")
        impRinv.setDisplayRange(0.0, LMAX32)
        impRinv.getProcessor().invert()
        impRinv.setDisplayRange(0.0, LMAX32)
        statRinv = impRinv.getStatistics()
        printStat(stat, "impRinv")
        if statRinv.min > 0.0:
                    #impRinv.getProcessor().subtract(statRinv.min)
            impRinv = clipUnder0(impRinv, MODE32, LMAX32) ## modifyed 2024.6.15
        print("impRinvout")
        impRinvout = outputImg(impRinv, outMode, outL_max, TRCType)
        IJ.saveAsTiff(impRinvout, path + "_impR-imp3_inv.tif")
        impRinvout.close()
    return impRinv

#------------------------------------------------
# making PFFcorrected B image
def make_exPFFc_B_image(impG, impB, parm_FFC_sigma, \
    parm_subtractBv, parm_leveladj, parm_correct_fac, \
                                    parm_ReadMask):
    #   always execute pseudo_Flat_field_C for making mask
    # 32bit process
    imp_c_b_tmp, imp_dif_gb = \
        pseudo_Flat_field_C(impG, impB, parm_FFC_sigma, \
            parm_subtractBv, parm_leveladj, parm_correct_fac)
    # Make corrected B image with exPFFc
    #--   make blue higer image and combine imp_c_b
    if parm_ReadMask == True:
        impT = Duplicator().run(impB) # corrected B ch.
        impRf2 = Duplicator().run(impG) # G ch.

        impT_highlight, impRf_highlight, impUserMask = \
                make_imp_blueHigh(impRf2, impT)
        # impUserMask: user made mask
        # make bluer region correction
        imp_c_b_high, imp_dif_gb = \
            pseudo_Flat_field_C(impRf_highlight, impT_highlight, \
            parm_FFC_sigma, parm_subtractBv, "No", 0.0)
            # option with parm_leveladj: "BlueAdjustment"
            # Make corrected B image in high B value region with exPFFc
        imp_dif_gb.close()
        ic = plugin.ImageCalculator()
        imp_c_b =  ic.run("Max create", imp_c_b_tmp, imp_c_b_high)
        imp_c_b.setDisplayRange(0.0, LMAX32)

        imp_c_b_tmp.close()
        imp_c_b_high.close()
        impT.close()
        impRf2.close()
    else:
        imp_c_b = imp_c_b_tmp
        impUserMask = imp_c_b_tmp.duplicate()
    return imp_c_b, impUserMask, imp_dif_gb

# -------- making impB03 image -----------
##      Make impB03 as B x 0.3 Image > B:G ratio variable
def make_impB03(bool_PFFcOnly, HybridC, \
                imp_c_b, impB, Gmixrate):
    # 32bit process
    # imp_c_b: PFFc applyed B image
    # impB: B image
    # bool_PFFcOnly: PFFc applyed or not (True/False)
    # Gmixrate: G ch. mixing rate to B ch. (normally 0.7)
    # HybridC: Do hybrid correction? (On/OFF)
    if bool_PFFcOnly == True: # EPFFc only
        impB03 = Duplicator().run(imp_c_b)
            # use extended pseudo FFc image
        Bmixrate = 1.0 - Gmixrate # B:G mixing rate variable
    elif (HybridC == "On") or (HybridC == "New"):
                    # Hybrid & New Hybrid algorithm
        impB03 = Duplicator().run(imp_c_b)
            # use extended pseudo FFc image
        Bmixrate = 0.3 # B:G mixing rate fixed
    else: # existing algorithm
        impB03 = Duplicator().run(impB) # not to use FFcorection image
        Bmixrate = 0.3 # B:G mixing rate fixed
            # use normal B image
    impB03.getProcessor().multiply(Bmixrate)
    impB03.setDisplayRange(0.0, LMAX32 * Bmixrate)
    return impB03
#-----------------------------------------------
##        Make impG07 as G x 0.7 Image
def make_impG07(bool_PFFcOnly, HybridC, \
                imp_c_b, impG, Gmixrate, parm_FFC_sigma):
    # 32bit process
    # imp_c_b: PFFc applyed B image
    # impG: G image
    # parm_FFC_sigma: Sigma for Gaussian Blur for PFFc applyed to G ch.
    # Gmixrate / strValueG: G ch. mixing rate to B ch. (normally 0.7)
    # HybridC: Do hybric correction? (On/Off/New)
    # statB03: statistics of impB03
    if bool_PFFcOnly == True: # EPFFc only
        mixrate = Gmixrate
    else:
        mixrate = 0.7
    if HybridC != "New":
        impG07 = Duplicator().run(impG)
        impG07.setDisplayRange(0.0, LMAX32)

    else: # HybridC == "New"
        imp_c_g, imp_Gauss_g2, imp_Gauss_b2 = \
            pseudo_Flat_field_C(imp_c_b, impG, parm_FFC_sigma * 0.5, \
                                                True, "Auto", 1.0)
            # modified in ver. / 5.01 Make G image closer to B image
            # parm_subtractBv: True / parm_leveladj: Auto /
            # parm_correct_fac 1.0 / parm_FFC_sigma: 5.0 (in HybridC == "New")
        impG07 = imp_c_g
        imp_Gauss_g2.close()
        imp_Gauss_b2.close()

    impG07.getProcessor().multiply(mixrate)
    impG07.setDisplayRange(0.0, LMAX32 * mixrate)

    return impG07
#-----------------------------------------------
#--- making foreground layer image
def making_Fg_image(impG, impB, parm_FFC_sigma, HybridC, \
    parm_subtractBv, parm_leveladj, parm_correct_fac, \
    bool_PFFcOnly, parm_Gmixrate, parm_ReadMask, Show):
    # [32bit] making extended pseudo Flat-Field corrected B image
    imp_c_b, impUserMask, imp_dif_gb = make_exPFFc_B_image(impG, impB, \
        parm_FFC_sigma, parm_subtractBv, parm_leveladj, \
                    parm_correct_fac, parm_ReadMask)
    # imp_c_b: extended pseudo flat-field corrected B image
    ##      Make impB03 as B x 0.3 Image > B:G ratio variable
    impB03 = make_impB03(bool_PFFcOnly, HybridC, \
                    imp_c_b, impB, parm_Gmixrate)
                    # for Ln process Mode, L_max > 32-bit, 1.0
    if Show == "Yes":
        impB03.show()
    # G ch. x 0.7
    ##        Make impG07 as G x 0.7 Image
    #    -----Next version modification will be here!
    impG07 = make_impG07(bool_PFFcOnly, \
        HybridC, imp_c_b, impG, parm_Gmixrate, parm_FFC_sigma)
        # attention! Reflect B information on G channel to be mixed
    # adjustment mead differance of imp_c_b and impG
    if Show == "Yes":
        impG07.show()
    # B x 0.3 + G x 0.7
    #---  making foreground layer image & save
    ic = plugin.ImageCalculator()
    imp3 = ic.run("Add create", impB03, impG07) # No window made
    imp3 = clipValue(imp3, MODE32, LMAX32)
    if Show == "Yes":
        impG07.hide()
    impG07.close()
    return imp3, impB03, imp_c_b, impUserMask, imp_dif_gb

#---
def calculate_cent_mean_diff(impA, impB):
    """
    Calculate mean differance of 2 imps from cropped images
    """
    width = impA.getWidth()
    height = impA.getHeight()
    # size of impA and impB is same
    roi_x = int(width * 0.15)
    roi_y = int(height * 0.15)
    roi_width = int(width * 0.7)
    roi_height = int(height * 0.7)

    impA.setRoi(roi_x, roi_y, roi_width, roi_height)
    impB.setRoi(roi_x, roi_y, roi_width, roi_height)
    impA1 = impA.crop()
    impB1 = impB.crop()
    statA = impA1.getStatistics()
    statB = impB1.getStatistics()
    mean_diff = statA.mean - statB.mean
    impA1.close()
    impB1.close()
    impA.resetRoi()
    impB.resetRoi()
    return mean_diff

#---making bg mask--------------
def make_bg_mask(impOrg, lowerV, Mode, L_max):
    # add in Ver. 5.3a
    # make layer mask of background correction layer
    # lowerV: lowerThreshold / upperV: upperThreshold (double)
    # roiColor: Color Value, ROI filled with (double)
    # maskname: name of mask image (string) / impOrg: impR
    imp = Duplicator().run(impOrg)
    ip = imp.getProcessor()
    ip_threshold = ip.createProcessor(ip.width, ip.height)
    ip_threshold.setValue(((lowerV * 0.9) / 255.0) * L_max)
                    # change for Ln Process
    ip_threshold.fill()
    impThreshold = ImagePlus("Bg Mask Threshold", ip_threshold)
    ic = plugin.ImageCalculator()
    impMask = ic.run("Subtract create", impOrg, impThreshold)
    # under threshold > Value: 0
    multFac = 1.0 / 0.2 # slope factor for gradation of mid range
    impMask.getProcessor().multiply(multFac)
    impMask = clipValue(impMask, Mode, L_max)
        ##2024.6.9

    imp.close()
    impThreshold.close()
    # under threshold: 0 over threshold: white point
    return impMask

#--- making background layer image
# (G ch.+30) x 0.7
def making_Bg_image(impG, imp3, \
        bool_PFFcOnly, bgG_gamma, parm_Gain, Show): # [32bit]
    # parm_Prph: threshold for periphery correction
    # bool_PFFcOnly: Do exPFFc only algorism? (Ture/False)
    # bgG_gamma: Gamma of Background layer to make image brighter
    # parm_Gain: adding value to make background layer brighter (8-bit value)
    # Bg image = (corrected B + 15) x 0.50 + Gamma corrected G x 0.50 (Dec 2024)

    #   make corrected B x 0.50
    impB05 = Duplicator().run(imp3)
    #  add B + parm_Gain
    impB05.getProcessor().add((parm_Gain / 255.0) * LMAX32)
    impB05.getProcessor().multiply(0.5)
    ##    Make impG30x07 as (G Gamma corrected) x 0.5 (< 0.7) Image
    if bool_PFFcOnly == False:  # Applying Hybrid or Traditional algorism
        # Mixing lighter G image with B for background layer
        impG30x07 = Duplicator().run(impG)
        # Ver. 5.3 / following code applying for all bit depth image
        impG30x07.setDisplayRange(0.0, LMAX32)
        if Show == "Yes":
            impG30x07.show()
        # Gain for background layer image
        # appropriate gamma is /1.45(= 0.69) for perceptive TRC image
        IJ.run(impG30x07, "Gamma...", "value=" + str(bgG_gamma))
                    # originally +30 for all ##2024.6.10
        impG30x07.setDisplayRange(0.0, LMAX32)
        impG30x07.getProcessor().multiply(0.5)
    ## B x 0.5 + (G+30) x 0.5 + x(parm_Gain)
        ic = plugin.ImageCalculator()
        imp4 = ic.run("Add create", impB05, impG30x07) # No window made
        imp4 = clipValue(imp4, MODE32, LMAX32)
            # x add Value parm_Gain
        imp4 = clipValue(imp4, MODE32, LMAX32)
        if Show == "Yes":
            impG30x07.hide()
        impG30x07.close()
    else:  # Apply Flat-field correction (exclude hybird)
        # No G ch. mixing in background layer
        imp4 = Duplicator().run(imp3)
        imp4.getProcessor().add((parm_Gain / 255.0) * LMAX32)
        imp4 = clipValue(imp4, MODE32, LMAX32)
    return imp4
#-------------------------------------------------
#--- making Periphery Adjustment Mask
# Periphery Adjustment Mask: B - ((R+G)/2-10)
def making_Prph_adj_Mask(imp5, impB, parm_Prph, bool_PFFcOnly, Show):
    # [32bit] / always perceptural
    # imp5: (impG + impR) / 2
    # parm_Prph: threshold for periphery correction
    # bool_PFFcOnly: Do exPFFc only algorism? (Ture/False)
    # Mode: bitdepth
    # 1.0: maximum value of image
    impGR = Duplicator().run(imp5) #imp5: (G+R)/2 image
    impBTmp = Duplicator().run(impB) #imp5: (G+R)/2 image
    impGR.setDisplayRange(0.0, LMAX32)

    if Show == "Yes":
        impGR.show()
    impGR.getProcessor().subtract((parm_Prph / 255.0) * LMAX32)
            # subtract Value parm_Prph /10
    if Show == "Yes":
        impGR.hide()
    impGR = clipValue(impGR, MODE32, LMAX32)
    impBTmp = clipValue(impBTmp, MODE32, LMAX32)
    ic = plugin.ImageCalculator()
    if bool_PFFcOnly == False: # Hybrid algorism
        imp2GR = ic.run("Subtract create", impBTmp, impGR)
                #impB - impGR:(G+R/2)
    else: # EPFFc only algorism
        imp2GR = ic.run("Subtract create", impBTmp, impGR)
                            #corrected impB - impGR:(G+R/2)
    imp2GR.getProcessor().multiply(4.0) # Save the Subtracted image x 4
        # modify for Ver. 5.8  as masks are always perceptural
    imp2GR = clipValue(imp2GR, MODE32, LMAX32)
    if Show == "Yes":
        imp2GR.show()
        impGR.hide()
    impGR.close()

    return imp2GR

#------2024.10---------
def makeDiffRG(impR, impG, maxrate):
    # [32bit] to make minimum value at R:G = maxrate:1 in green protect mask
    impRTmp = impR.duplicate()
    impRTmp.getProcessor().multiply(maxrate)
    ic = plugin.ImageCalculator()
    impTmp1 = ic.run("Difference create", impG, impRTmp)
    stat = impTmp1.getStatistics()
    # adjust contrast of impTmp1 with stdDev
    v = stat.mean + 2.0 * stat.stdDev
    impTmp1.getProcessor().multiply(1.0 / v)
    impTmp1 = clipValue(impTmp1, MODE32, LMAX32)
    # excluding mask of relative green area
    impTmp2 = ic.run("Subtract create", impG, impR)
    impTmp2 = clipValue(impTmp2, MODE32, LMAX32)
    impTmp2.getProcessor().multiply(500) # to make excluding G mask binary
    impTmp2 = clipValue(impTmp2, MODE32, LMAX32)
    impTmp2 = clipValue(impTmp2, MODE32, LMAX32)
    # exclude rel. G area from mask
    impTmp3 = ic.run("Max create", impTmp1, impTmp2)
    impTmp3 = clipValue(impTmp3, MODE32, LMAX32)
    impTmp3.getProcessor().invert()
    impRTmp.close()
    impTmp1.close()
    impTmp2.close()
    return impTmp3
#------ making mask of Fg brown correction layer mask------
def making_brownC_layer_mask(impR, impG, lowerV):
    # [32bit] / always perceptural
    # making mask of R - G for brown corrction layer [in 32bit]
    # not to evaluate B ch. image to correct Magenta image
    impTmpR = Duplicator().run(impR)
    impTmpV = Duplicator().run(impG)
    meanDiff = calculate_cent_mean_diff(impR, impG)
    impTmpR.getProcessor().subtract(meanDiff)
    # -- making R - G image / max R:G = 1: 0.65
    difRG = makeDiffRG(impTmpR, impG, 0.65)
    difRG = clipValue(difRG, MODE32, LMAX32)
    #impTmpG.getProcessor().gamma(2.0)
        # G ch. modified ABS(G - R * 0.65) 2024.6 > 0.75 2024.10
        # to make min value at brown G:R = 1:0.65
    # --- make V(Lightness) mask ---
    #     basically it is binary mask, but add gradation to mid range
    #     over mid gray: black / under mid gray: white
    # impMaskV: highlight area excluding mask
    impMaskV = make_bg_mask(impTmpV, lowerV, MODE32, LMAX32)
        # lowerV (Mid gray) must be 8bit based data
        # over mid gray white, and under mid gray black
    impMaskV.setDisplayRange(0.0, LMAX32)
    impMaskV.getProcessor().invert()
    impMaskV.getProcessor().subtract(0.1)
    impMaskV = clipValue(impMaskV, MODE32, LMAX32)
        # to make over mid gray black, and under mid gray white
    # --- exclude highlight area ---
    ic = plugin.ImageCalculator()
    mask = ic.run("Min create", impMaskV, difRG)
    mask = clipValue(mask, MODE32, LMAX32)
    impMaskV.close()
    # -- adjustment of mean value over 0.3 of mask ---
    stat = mask.getStatistics()
    mask.getProcessor().multiply(1.0 / (stat.mean + stat.stdDev * 2.5)) ###(5.9-3)
    mask = clipOverLmax(mask, MODE32, LMAX32)
    stat = mask.getStatistics()
    if stat.mean > 0.3:
        mask.getProcessor().gamma(math.log(0.10, stat.mean)) # 2023.12.05
        # adjusting mask lightness mean value to under 0.3
    # 2025.01 making brown mask with shadow curve + straight line
    # parameters
    node_y = 0.15 # y value of the junction of curve and line
    gamma = 1.5 # gamma of shadow curve
    slopeFactor, y_intercept = \
                      get_slope_parameters(gamma, node_y)
    # needs * L_max / y_intercept is munus!
    mask = add_shadow_curve2mask\
        (mask, gamma, node_y, y_intercept, slopeFactor)
    return mask
#---------------------------------------------------
def get_slope_parameters(gamma, node_y):
    node_x = node_y ** (1.0 / gamma)
    # node is the junction of straight line and gamma curve
    y_intercept = node_y - node_x # minus value!
    x_intercept = -1.0 * y_intercept
    slopeFactor = 1 / (1.0 + y_intercept)
    return slopeFactor, y_intercept

def make_splited_imp(imp, node_y, y_intercept, undernode, L_max):
    # undernode: making over node image or under node image?
    impTmp = imp
    if undernode == True:
        imp_bin_mask = imageBinarySmpl\
            (impTmp, node_y, L_max, L_max, "over node_y white")
    else:
        imp_bin_mask = imageBinarySmpl\
            (impTmp, y_intercept, node_y, L_max, "under node_y white")
    ic = plugin.ImageCalculator()
    imp_combined = ic.run("Max create", impTmp, imp_bin_mask)
    impTmp.close()
    imp_bin_mask.close()
    return imp_combined

def add_shadow_curve2mask(mask0, gamma, node_y, y_intercept, slopeFactor):
    impMaskCurve = Duplicator().run(mask0)
    impMaskStraight = mask0
    impMaskCurve.getProcessor().gamma(gamma)
    impMaskStraight.getProcessor().add(y_intercept) # need to be 32bit y_intercept is minus!
    impMaskCurve1 = make_splited_imp(impMaskCurve, node_y, y_intercept, True, LMAX32)
    impMaskStraight1 = make_splited_imp(impMaskStraight, node_y, y_intercept, False, LMAX32)
    ic = plugin.ImageCalculator()
    mask = ic.run("Min create", impMaskCurve1, impMaskStraight1)
    impMaskCurve1.close()
    impMaskStraight1.close()
    mask.getProcessor().multiply(slopeFactor) # need to be 32bit
    mask = clipValue(mask, MODE32, LMAX32)
    return mask

#---------------------------------------------------
def making_brownC_layer(imp): # [32bit]
    # imp: fg correcting layer
    impTmp0 = Duplicator().run(imp)
    if LnProcess == False:
        impTmp1 = sRGB_TRC_decode(impTmp0, LMAX32)
        impTmp0.close()
        impTmp1.getProcessor().subtract(0.05 * LMAX32)
        impTmp1 = clipUnder0(impTmp1, MODE32, LMAX32)  ##2024.6.9
        impTmp2 = sRGB_TRC_encode(impTmp1, LMAX32)
        impTmp2 = clipUnder0(impTmp2, MODE32, LMAX32) ##2024.6.9
    else: ## Linear Process
        impTmp0.getProcessor().subtract(0.05 * LMAX32)
        impTmp2 = clipUnder0(impTmp0, MODE32, LMAX32)
    if LnProcess == False:
        impTmp1.close()
    return impTmp2

#----
def sRGB_TRC_encode(imp, L_max):
    # revised 2024.6 / this must be operateted in 32-bit
    # if imp_Shadow max value > shadow threshold: only use imp_nonShadow
    stat0 = imp.getStatistics()
    impTrc1 = Duplicator().run(imp)
    impTrc2 = Duplicator().run(imp)
    cshadowThld = 0.0031308 * L_max
    # non shadow region
    impmask = imageBinarySmpl(impTrc1, \
            cshadowThld, L_max, L_max, "non_shadow_mask")
    impTrc1.setDisplayRange(0.0, L_max)
    impTrc1.getProcessor().gamma(1.0 / 2.4)
    impTrc1.getProcessor().multiply(1.055)
    impTrc1.getProcessor().subtract(0.055 * L_max)
    ic = plugin.ImageCalculator()
    imp_nonShadow =  ic.run("Min create", impmask, impTrc1)

    # shadow region
    if stat0.min < 0.0:
        print ("sRGB encode / impTrc2 min: " + str(stat0.min))
    if stat0.min <= cshadowThld:
        impmaskShadow = imageBinarySmpl(impTrc2, \
                            0.0, cshadowThld, L_max, "shadow_mask")
        impTrc2.getProcessor().multiply(12.92)
        imp_Shadow =  ic.run("Min create", impmaskShadow, impTrc2)
        impmaskShadow.close()

    else: # if imp minvalue > cshadowThld
        imp_Shadow = impTrc2.duplicate()
        imp_Shadow.getProcessor().setValue(0.0)
        imp_Shadow.getProcessor().fill()

    impTrc2.setDisplayRange(0.0, L_max)

    # shadow + non shadow
    impTrc3 =  ic.run("Max create", imp_Shadow, imp_nonShadow)
    impTrc3.setDisplayRange(0.0, L_max)
    impTrc3.setTitle("sRGB TRC encoded")

    impmask.close()
    imp_Shadow.close()
    imp_nonShadow.close()
    impTrc1.close()
    impTrc2.close()
    return impTrc3

#-----------
def sRGB_TRC_decode(imp, L_max):
    # revised 2024.6 / this must be operateted in 32-bit
    # if imp_Shadow max value > shadow threshold: only use imp_nonShadow
    imp.setDisplayRange(0.0, L_max)
    stat0 = imp.getStatistics()
    impTrc1 = Duplicator().run(imp)
    impTrc2 = Duplicator().run(imp)
    cshadowThld = 0.04045 * L_max

    # not shadow region
    impmask = imageBinarySmpl(impTrc1, \
                cshadowThld, L_max, L_max, "shadow_mask")
        # ROI: white back: black
    impTrc1.setDisplayRange(0.0, L_max)
    impTrc1.getProcessor().add(0.055 * L_max)
    impTrc1.getProcessor().multiply(1 / 1.055)
    impTrc1.getProcessor().gamma(2.4)
    ic = plugin.ImageCalculator()
    imp_nonShadow =  ic.run("Min create", impmask, impTrc1)
    # shadow region
    if stat0.min <= cshadowThld:
        impmaskShadow = imageBinarySmpl(impTrc2, 0.0, \
                    cshadowThld, L_max, "non_shadow_mask")
        impTrc2.getProcessor().multiply(1 / 12.92)
        imp_Shadow =  ic.run("Min create", impmaskShadow, impTrc2)
        impmaskShadow.close()
    else: # if imp minvalue > cshadowThld
        imp_Shadow = impTrc2.duplicate()
        imp_Shadow.getProcessor().setValue(0.0)
        imp_Shadow.getProcessor().fill()
    impTrc2.setDisplayRange(0.0, L_max)

    stat = imp_Shadow.getStatistics()
    if stat.max < cshadowThld:
        impTrc3 =  ic.run("Max create", imp_Shadow, imp_nonShadow)
    else:
        impTrc3 =  imp_nonShadow.duplicate()
    impmask.close()
    imp_Shadow.close()
    imp_nonShadow.close()
    impTrc1.close()
    impTrc2.close()
    impTrc3.setDisplayRange(0.0, L_max)

    return impTrc3

#-------------------------------------------------
class DialogPreviewer(ItemListener, AdjustmentListener):
    def __init__(self, choice, slider, nfield, \
                            nfield1, nfield3, nfield2=None):
        """
          ##choice-TRC > parameter fields
          ##choice-Hybrid correction (novice)> parameter fields
            choice: a java.awt.Choice
            processtype: string
            nfield:  a java.awt.TextField
            slider:  a java.awt.Scrollbar
        """
        self.slider = slider # Mixing G ch. Gamma
        self.nfield = nfield # Background mask threshold
        self.nfield1 = nfield1 # Shadow area threshold
        self.nfield2 = nfield2 # Periphery threshold
        self.nfield3 = nfield3 # slider numeric field
        # default: self.nfield3.setText("1.56")
        # default: self.slider.setValue(156) # 1.56
        self.choice1 = choice


    def itemStateChanged(self, event):
        """ event: an ItemEvent with data on what happened to the checkbox. """
        choice_item  = self.choice1.getSelectedItem()
        if choice_item == "Perceptual": # Select TRC
            self.nfield.setText("128")
            self.nfield1.setText("25")
            if self.nfield2 != None:
                self.nfield2.setText("10")
                self.nfield2.setBackground(Color.white)
            self.nfield.setBackground(Color.white)
            self.nfield1.setBackground(Color.white)
        elif choice_item == "Linear": # Select TRC
            self.nfield.setText("128")
            self.nfield1.setText("25")
            if self.nfield2 != None:
                self.nfield2.setText("0")
                self.nfield2.setBackground(Color.cyan)
            self.nfield.setBackground(Color.cyan)
            self.nfield1.setBackground(Color.cyan)
        elif choice_item == "Off": # Select Hypbid Correction
            self.nfield3.setText("1.45")
            self.slider.setValue(145) #1.46
            self.nfield3.setBackground(Color.pink)
        elif choice_item == "On" or choice_item == "New Method On" :
                        # Select Hypbid Correction
            self.nfield3.setText("1.56")
            self.slider.setValue(156) # 1.56
            self.nfield3.setBackground(Color.white)

    def adjustmentValueChanged(self, event):
        """ event: an AdjustmentEvent with data on the state of the scroll bar. """
        value = self.nfield3.getText()
        if value == "1.00":
            self.nfield3.setBackground(Color.lightGray)
        elif value == "1.45":
            self.nfield3.setBackground(Color.pink)
        elif value == "1.56":
            self.nfield3.setBackground(Color.white)
        else:
            self.nfield3.setBackground(Color.yellow)
        return
#------------------------------------------------
class DialogPreviewer1 (ItemListener):
    def __init__(self, choice, choice2, slider, nfield,  nfield1, \
                                        nfield3, nfield4, nfield5, nfield6):
        """
           ##choice-Hybrid correction (expert)> parameter fields
             choice: a java.awt.Choice
             nfield:  a java.awt.TextField
             slider:  a java.awt.Scrollbar
        """
        self.slider = slider # Mixing G ch. Gamma
        self.nfield = nfield # Background mask threshold
        self.nfield1 = nfield1 # Shadow area threshold
        self.nfield3 = nfield3 # G's Gamma slider numeric field
        self.choice1 = choice  # Hybrid correction
        self.nfield4 = nfield4 # Sigma
        self.nfield5 = nfield5 # G mix ratio
        self.nfield6 = nfield6 # incremental amount
        self.choice2 = choice2 # incremental amount on/off
        self.choice_item2  = self.choice2.getSelectedItem()

    def itemStateChanged(self, event):
        """ event: an ItemEvent with data on what happened to the checkbox. """
        choice_item  = self.choice1.getSelectedItem()
        if choice_item == "G Mixing":
                                        # Select Traditional algorism
            self.nfield3.setText("1.45")
            self.slider.setValue(145) #1.45
            self.nfield3.setBackground(Color.pink)
            self.nfield4.setBackground(Color.gray) # Sigma
            self.nfield5.setBackground(Color.gray) # G mix ratio
            self.nfield6.setBackground(Color.gray) # incremental amount
        elif choice_item == "Hybrid" or choice_item == "New Hybrid" :
                                            # Select Hypbid Correction
            self.nfield3.setText("1.56")
            self.slider.setValue(156) # 1.56
            self.nfield3.setBackground(Color.white)
            self.nfield4.setBackground(Color.green) # Sigma
            if choice_item == "New Hybrid":
                self.nfield4.setText("5.0")
            self.nfield5.setBackground(Color.gray) # G mix ratio
            if self.choice_item2  == "Manual":
                self.nfield6.setBackground(Color.green)
        elif choice_item == "EPFFc":
                        # Select EPFFc
            self.nfield3.setText("1.00")
            self.slider.setValue(100) # 1.00
            self.nfield3.setBackground(Color.lightGray)
            self.nfield4.setBackground(Color.green) # Sigma
            self.nfield5.setBackground(Color.green) # G mix ratio
            if self.choice_item2  == "Manual":
                self.nfield6.setBackground(Color.green)
        return
#------------------------------------------------
class DialogPreviewer2(ItemListener):
    def __init__(self, choice, nfield3, nfield4, nfield5, \
                                    nfield6, choice2, choice3, checkbox2):
        """
           ##checkbox-pFF correction > parameter fields
             choice: a java.awt.Choice
             checkbox:  a java.awt.Checkbox
        """
        self.choice1 = choice # Hybrid c on/off
        choice_item1  = self.choice1.getSelectedItem()
        self.nfield3 = nfield3 # Mixing G ch. Gamma: slider's numeric field
        self.nfield4 = nfield4 # Sigma
        self.nfield5 = nfield5 # G mix ratio
        self.nfield6 = nfield6 # incremental amount
        self.choice2 = choice2 # incremental amount on/off
        choice_item2  = self.choice2.getSelectedItem()
                            # adjustment amount auto / manual / none
        self.choice3 = choice3 # imege conditon
        choice_item3  = self.choice3.getSelectedItem()
        self.checkbox2 = checkbox2 # Apply pre-mixing to B
        if choice_item1 == "EPFFc": # apply FF correction only
            self.nfield4.setBackground(Color.green)
            self.nfield5.setBackground(Color.green)
            if choice_item2  == "Manual":
                self.nfield6.setBackground(Color.green)
            else:
                self.nfield6.setBackground(Color.gray)
        elif  choice_item1 == "Hybrid" or  choice_item1 == "New Hybrid":
                                                            # apply Hybrid
            self.nfield4.setBackground(Color.green)
            self.nfield5.setBackground(Color.gray)
            if choice_item2  == "Manual":
                self.nfield6.setBackground(Color.green)
            else:
                self.nfield6.setBackground(Color.gray)
        else:  # G mixing only
            self.nfield4.setBackground(Color.gray)
            self.nfield5.setBackground(Color.gray)
            self.nfield6.setBackground(Color.gray)
        if nfield3.getText() == "1.00": # if Gamma slider value is 100
            self.nfield3.setBackground(Color.lightGray)
        elif nfield3.getText() == "1.45": # if Gamma slider value is 145
            self.nfield3.setBackground(Color.pink)
        elif nfield3.getText() == "1.56": # if Gamma slider value is 145
            self.nfield3.setBackground(Color.white)
        else:
            self.nfield3.setBackground(Color.yellow)
        if choice_item3 == "Normal": # image conditionp
            self.nfield4.setText("10.0") # set Sigma value
            self.checkbox2.setState(False)
        else: # "Coarse"
            choice_item1  = self.choice1.getSelectedItem()
            if choice_item1 != "G Mixing":
                self.nfield4.setText("5.0")

    def itemStateChanged(self, event):
        """ event: an ItemEvent with data on what happened to the checkbox. """
        choice_item1  = self.choice1.getSelectedItem()
                                        # algorism selection
        choice_item2  = self.choice2.getSelectedItem()
                                    # adjustment amount auto / manual / none
        choice_item3  = self.choice3.getSelectedItem()
                # image condition normal / coarse

        if choice_item2 == "Manual": # pseudo FF correction On
            self.nfield6.setBackground(Color.green)
        else:
            self.nfield6.setBackground(Color.gray)
        if choice_item3 == "Normal": # image conditionp
            if choice_item1 != "New Hybrid":
                self.nfield4.setText("10.0") # set Sigma value
            self.checkbox2.setState(False)
        else: # "Coarse"
            choice_item1  = self.choice1.getSelectedItem()
            if choice_item1 != "G Mixing":
                self.nfield4.setText("5.0")
            self.checkbox2.setState(True)
        return

#------------------------------------------------
# Novice Mode: Get parameters from the input DialogBox
def NoviceMenu(path, Vmean):
    global LnProcess # Linear processing?
    gd = GenericDialog("Novice Menu: Enter Parameters")
    TRCType = ["Perceptual", "Linear"]
    gd.addChoice("Image TRC Type", TRCType, TRCType[0])
    if Vmean < 128.0:
        BkThs = 128.0
    else:
        BkThs = int(Vmean)
    gd.addNumericField("Background Mask Threshold (0 to 255)", int(BkThs) , 0)
    gd.addSlider("Background mixing G ch. Gamma (increase > bluer)",\
                                                    1.00, 1.70, 1.56 ,0.01)
    gd.addNumericField("Shadow Area Threshold (0 to 255)", 25, 0)
    HybridC_Choice = ["Off", "On", "New Method On"]
    gd.addChoice("Apply hybrid correction?", \
                                        HybridC_Choice, HybridC_Choice[1])
    condition_Choice = ["Normal","Coarse"]
    gd.addChoice("Image condition", condition_Choice, condition_Choice[0])
    showWin = ["Yes","No"]
    gd.addChoice("Show working windows?", showWin, showWin[0])
    gd.addMessage("If you would like to use Expert Menu,"+\
                                            " Please cancel this menu.")
    gd.addCheckbox("Apply Linear workflow", LnProcess)

    # The UI elements for the above inputs
    choice = gd.getChoices().get(0) # TRC Type
    nfield = gd.getNumericFields().get(0) # Background mask threshold
    slider = gd.getSliders().get(0) # Mixing G Gamma
    nfield3 = gd.getNumericFields().get(1) # slider numeric field
    # Attention! Sliders has also NumericFields, so attention
    #                             to index number for NFields!
    nfield1 = gd.getNumericFields().get(2) # Shadow area threshold
    choice1 = gd.getChoices().get(1) # Hybrid correction on/off
    # Set event previewer
    previewer = DialogPreviewer(choice, slider, nfield, \
                                        nfield1, nfield3)
    previewer1 = DialogPreviewer(choice1, slider, nfield, \
                                        nfield1, nfield3)
    choice.addItemListener(previewer)
    slider.addAdjustmentListener(previewer)  # Slider
    choice1.addItemListener(previewer1)

    gd.showDialog()

    # get values
    parm_Mode   = "Auto"
    TRCselected   = gd.getNextChoice()
    if TRCselected == "Perceptual":
        parm_Fg_tL = 128
        parm_Prph = 10
    else: ## TRC: Linear
        parm_Fg_tL = 56
        parm_Prph = 0
    parm_Binary_Rsv   = gd.getNextNumber() # Fg / Bg threshold
    parm_Gain = gd.getNextNumber() # (5.01a) Gamma value for Bg
    parm_Shadow_Rsv = gd.getNextNumber() # Shadow correction threshold
    parm_HybridC   = gd.getNextChoice() # Correction method
    if parm_HybridC == "New Method On":
        parm_HybridC = "New"
    parm_Condition   = gd.getNextChoice() # Image condition Normal / Coarse
    parm_Show   = gd.getNextChoice() # Show windows?
    parm_LnWorkflow = gd.getNextBoolean() # apply linear workflow?
    LnProcess = parm_LnWorkflow
    if parm_LnWorkflow == True:
        parm_Binary = int((float(parm_Binary_Rsv / 255.0)** 2.2) * 255)
        parm_Shadow = int((float(parm_Shadow_Rsv / 255.0)** 2.2) * 255)
    else:
        parm_Binary = parm_Binary_Rsv
        parm_Shadow = parm_Shadow_Rsv

    # following parameters are fixed in Novice Mode
    bool_PFFcOnly = False # apply only pseudo flat-field correction > false
    if parm_Condition == "Normal" and parm_HybridC !="New":
        parm_FFC_sigma = 10.0
        DoPremixing = False
    else: # image condition: "Coarse" or parm_HybridC: "New"
        parm_FFC_sigma = 5.0
        DoPremixing = True
    parm_Gmixrate = 0.0
    parm_leveladj  = "Auto" # apply level adjustment between channels
    parm_correct_fac = 0.0 # correction adjustment factor
    parm_subtractBv = False
    if parm_Gain > 2.2:
        parm_Gain = 2.2
    bgG_gamma = 1.0 / parm_Gain # inverse value of gamma (actual applying power)
    parm_Gain = 0.0
    if parm_Binary > 255: #Set Max Value
        parm_Binary = 255
    if parm_Shadow > 255: #Set Max Value
        parm_Shadow = 255
    Fg_mask_type = "Type2"
    parm_ReadMask = False
    discolorOut = False
    if gd.wasCanceled():
        choice.removeItemListener(previewer)
        choice1.removeItemListener(previewer1)
        parm_Mode   = "Expert"
        IJ.log("Expert Menu called")
#        return str(parm_Mode), parm_Binary, parm_Gain, parm_Shadow,\
        return str(parm_Mode), parm_Binary, parm_Shadow,\
            str(parm_Show), parm_Prph, parm_Fg_tL, bool_PFFcOnly, \
            parm_FFC_sigma, parm_Gmixrate, parm_subtractBv, parm_leveladj, \
            parm_correct_fac, parm_HybridC, Fg_mask_type, bgG_gamma, \
            parm_ReadMask, DoPremixing, discolorOut
    write_out(path, parm_Binary_Rsv, parm_Gain, parm_Shadow_Rsv, parm_Prph, \
            parm_Fg_tL, bool_PFFcOnly, parm_FFC_sigma, parm_Gmixrate, \
            parm_subtractBv, parm_leveladj, parm_correct_fac, parm_HybridC, \
            Fg_mask_type, bgG_gamma, parm_Condition)
    choice.removeItemListener(previewer)
    choice1.removeItemListener(previewer1)
#    return str(parm_Mode), parm_Binary, parm_Gain, parm_Shadow, \
    return str(parm_Mode), parm_Binary, parm_Shadow, \
        str(parm_Show), parm_Prph, parm_Fg_tL, bool_PFFcOnly, \
        parm_FFC_sigma, parm_Gmixrate, parm_subtractBv, parm_leveladj, \
        parm_correct_fac, parm_HybridC, Fg_mask_type, bgG_gamma, \
        parm_ReadMask, DoPremixing, discolorOut
#----------------------------------------------------
# Expert Mode: Get parameters from the input DialogBox
def getParameters(path, Vmean):
    global LnProcess # Linear processing?
    oldparms = read_in(path, Vmean)
    gd = GenericDialog("Expert Menu: Enter Parameters")
    TRCType = ["Perceptual", "Linear"]
    if int(oldparms["parm_Fg_tL"]) == 128:
        gd.addChoice("Image TRC Type", TRCType, TRCType[0])
    else:
        gd.addChoice("Image TRC Type", TRCType, TRCType[1])

    gd.addNumericField("Background Mask Threshold (0 to 255)",\
                                            float(oldparms["parm_Binary"]), 0)
    gd.addSlider("Background mixing G ch. Gamma (increase > bluer)", \
                        1.00, 1.70, 1.00 / float(oldparms["bgG_gamma"]), 0.01)
    gd.addNumericField("Shadow Area Threshold (0 to 255)", \
                                            float(oldparms["parm_Shadow"]), 0)
    gd.addNumericField("Periphery Adjst Threshold (-10 to 20)", \
                                            float(oldparms["parm_Prph"]), 0)

    gd.addMessage("                        in algorism /" + \
                    " EPFFc: Apply Extended Pseudo Flat-Field Correction only")

    HybridC_Choice = ["G Mixing", "Hybrid", "New Hybrid", "EPFFc"]
    if float(oldparms["bgG_gamma"]) == 0.69:
        gd.addChoice("Select algorism", HybridC_Choice, HybridC_Choice[0])
    elif float(oldparms["bgG_gamma"]) == 1.00:
        gd.addChoice("Select algorism", HybridC_Choice, HybridC_Choice[3])
    else:
        gd.addChoice("Select algorism", HybridC_Choice, HybridC_Choice[1])
    mask_type = ["Type1", "Type2", "Type3", "Type4","None"]
    gd.addMessage("-----------------------------------")
    gd.addMessage("[Extended Pseudo Flat-Field correction (EPFFc) parameters]")
    gd.addNumericField("Sigma for Flat-Field correction", \
                                    float(oldparms["parm_FFC_sigma"]), 1)
    gd.addNumericField("G mix rate to B (available in EPFFc only mode)", \
                                    float(oldparms["parm_Gmixrate"]), 1)
    leveladj = ["No","Auto","Manual"]
    if oldparms["parm_leveladj"] == "Auto":
        gd.addChoice("Correction Amount adjustment", leveladj, leveladj[1])
    elif oldparms["parm_leveladj"] == "Manual":
        gd.addChoice("Correction Amount adjustment", leveladj, leveladj[2])
    else:
        gd.addChoice("Correction Amount adjustment", leveladj, leveladj[0])
    gd.addNumericField("Incremental correcting amount (in Manual 0-255)", \
                                    float(oldparms["parm_correct_fac"]), 0)
    gd.addCheckbox("Subtract Pixel Value if B > G",\
                                        eval(oldparms["parm_subtractBv"]))
    gd.addCheckbox("Read user made bluer mask", False)
    gd.addCheckbox("Apply G texture info to original B ", True)
    gd.addMessage("-----------------------------------")
    condition_Choice = ["Normal","Coarse"]
    if oldparms["parm_Condition"] == "Normal":
        gd.addChoice("Image condition", condition_Choice, condition_Choice[0])
    elif oldparms["parm_Condition"] == "Coarse":
        gd.addChoice("Image condition", condition_Choice, condition_Choice[1])
    showWin = ["Yes","No"]
    gd.addChoice("Show working windows?", showWin, showWin[0])
    gd.addCheckbox("Apply Linear workflow", LnProcess)
    gd.addCheckbox("Output calculated discolor area", False)

    # The UI elements for the above inputs
    choice = gd.getChoices().get(0) # TRC Type
    nfield = gd.getNumericFields().get(0) # Background mask threshold
    slider = gd.getSliders().get(0) # Mixing G Gamma
    nfield3 = gd.getNumericFields().get(1) # slider numeric field
    # Attention! Sliders has also NumericFields,
    #           so attention to index number for NFields!
    nfield1 = gd.getNumericFields().get(2) # Shadow area threshold
    nfield2 = gd.getNumericFields().get(3) # Periphery threshold
    choice1 = gd.getChoices().get(1) # Hybrid correction on/off
    #choice2: mask type choice > deleted!
    choice3 = gd.getChoices().get(2) # increment adjustment
    choice4 = gd.getChoices().get(3) # image condition
    nfield4 = gd.getNumericFields().get(4) # Sigma for FF correction
    nfield5 = gd.getNumericFields().get(5) # G mix ratio
    nfield6 = gd.getNumericFields().get(6) # Incremantarl correction amount
    #checkbox0: Subtract Pixcel Value if B > G
    #checkbox1: Using user made bluer mask
    checkbox2 = gd.getCheckboxes().get(2) # Apply pre mixing to B


    # Set event previewer
    #  TRC selection
    previewer = DialogPreviewer(choice, slider, nfield, \
                                        nfield1, nfield3, nfield2)
    #  Hybric correction
    previewer1 = DialogPreviewer1(choice1, choice3, slider, nfield, \
                                nfield1, nfield3, nfield4, nfield5, nfield6)
    #  Apply extended pseudo FF correction
    previewer2 = DialogPreviewer2(choice1, nfield3, nfield4, \
                        nfield5, nfield6, choice3, choice4, checkbox2)

    choice.addItemListener(previewer)  # TRC Type
    slider.addAdjustmentListener(previewer)  # Slider
    choice1.addItemListener(previewer1)  # Hybrid correction on/off
    choice3.addItemListener(previewer2)  # Apply FF correction - Manual
    choice4.addItemListener(previewer2)  # Apply FF correction - Manual

    gd.showDialog()

    #Get Parameters
    parm_Mode   = "Auto"
    TRCselected   = gd.getNextChoice()
    if TRCselected == "Perceptual":
        parm_Fg_tL = 128
    else:
        parm_Fg_tL = 56
    parm_Binary_Rsv   = gd.getNextNumber() # Fg / Bg threshold
    parm_Gain = gd.getNextNumber() # (5.01a) Gamma value for Bg
    parm_Shadow_Rsv = gd.getNextNumber() # Shadow correction threshold
    parm_Prph = gd.getNextNumber() # Peripheral fading correction threshold
    parm_HybridC   = gd.getNextChoice() # Correction method
    if parm_HybridC == "New Hybrid":
        parm_HybridC = "New"
        bool_PFFcOnly = False
    elif parm_HybridC == "G Mixing":
        parm_HybridC = "Off"
        bool_PFFcOnly = False
    elif parm_HybridC == "Hybrid":
        parm_HybridC = "On"
        bool_PFFcOnly = False
    elif parm_HybridC == "EPFFc":
        parm_HybridC = "Off"
        bool_PFFcOnly = True
    Fg_mask_type  = "Type2" # Fg layer mask type
    parm_FFC_sigma = gd.getNextNumber()
                # Sigma for Gaussian filtering for pseudo FF correction
    parm_Gmixrate = gd.getNextNumber() # G mixing rate for B
    parm_leveladj  = gd.getNextChoice() # apply level adjustment between channels?
    parm_correct_fac = gd.getNextNumber() # correction adjustment factor
    parm_subtractBv = gd.getNextBoolean() #get value of checkbox
    parm_ReadMask = gd.getNextBoolean() # read user made mask?
    DoPremixing = gd.getNextBoolean() # apply premixing for original B image?
    parm_Condition   = gd.getNextChoice() # Image condition Normal / Coarse

    parm_Show   = gd.getNextChoice() # show correcting process?
    parm_LnWorkflow = gd.getNextBoolean() # apply lenear workflow?
    discolorOut =  gd.getNextBoolean() # output discolor region?
    LnProcess = parm_LnWorkflow
    if parm_LnWorkflow == True:
        parm_Binary = int((float(parm_Binary_Rsv / 255.0)** 2.2) * 255)
        parm_Shadow = int((float(parm_Shadow_Rsv / 255.0)** 2.2) * 255)
    else:
        parm_Binary = parm_Binary_Rsv
        parm_Shadow = parm_Shadow_Rsv
    bgG_gamma = 0.69
    if parm_Binary > 255: #Set Max Value
        parm_Binary = 255
    if parm_Gain > 2.2:
        parm_Gain = 2.2
    bgG_gamma = 1.0 / parm_Gain
    parm_Gain = 0.0
    if parm_Shadow > 255: #Set Max Value
        parm_Shadow = 255
        if parm_Gmixrate > 1.0:
            parm_Gmixrate = 1.0
        elif parm_Gmixrate < 0.0:
            parm_Gmixrate = 0.0
    if gd.wasCanceled():
        parm_Mode   = "Stop"
        IJ.log("Canceled")
#        return str(parm_Mode), parm_Binary, parm_Gain, parm_Shadow,\
        return str(parm_Mode), parm_Binary, parm_Shadow,\
            str(parm_Show), parm_Prph, parm_Fg_tL, bool_PFFcOnly, \
            parm_FFC_sigma, parm_Gmixrate, parm_subtractBv, parm_leveladj, \
            parm_correct_fac, parm_HybridC, Fg_mask_type, \
            bgG_gamma, parm_ReadMask, DoPremixing, discolorOut
    write_out(path, parm_Binary_Rsv, parm_Gain, parm_Shadow_Rsv, parm_Prph, \
            parm_Fg_tL, bool_PFFcOnly, parm_FFC_sigma, parm_Gmixrate, \
            parm_subtractBv, parm_leveladj, parm_correct_fac, parm_HybridC, \
            Fg_mask_type, bgG_gamma, parm_Condition)
#    return str(parm_Mode), parm_Binary, parm_Gain, parm_Shadow, \
    return str(parm_Mode), parm_Binary, parm_Shadow, \
        str(parm_Show), parm_Prph, parm_Fg_tL, bool_PFFcOnly, \
        parm_FFC_sigma, parm_Gmixrate, parm_subtractBv, parm_leveladj, \
        parm_correct_fac, parm_HybridC, Fg_mask_type, bgG_gamma, \
        parm_ReadMask, DoPremixing, discolorOut
#----------------
def write_out(path, parm_Binary, parm_Gain, parm_Shadow, parm_Prph, \
            parm_Fg_tL, bool_PFFcOnly, parm_FFC_sigma, parm_Gmixrate, \
            parm_subtractBv, parm_leveladj, parm_correct_fac, parm_HybridC, \
            Fg_mask_type, bgG_gamma, parm_Condition):
    wo_parm = dict(parm_Binary=parm_Binary, parm_Gain=parm_Gain, \
        parm_Shadow=parm_Shadow, parm_Prph=parm_Prph, \
        parm_Fg_tL=parm_Fg_tL, bool_PFFcOnly=bool_PFFcOnly, \
        parm_FFC_sigma=parm_FFC_sigma, parm_Gmixrate=parm_Gmixrate, \
        parm_subtractBv=parm_subtractBv, parm_leveladj=parm_leveladj, \
        parm_correct_fac=parm_correct_fac, parm_HybridC=parm_HybridC, \
        Fg_mask_type=Fg_mask_type, bgG_gamma=bgG_gamma, \
        parm_Condition=parm_Condition)
#  -- writing "filename_parameter.txt" file
    writing_file0 = open(path  + '_parameter.txt','a')
    # new_file_number = int(file_number) + 1
    # i = 0
    for key in wo_parm.keys():
        writing_file0.write(key + ": " + str(wo_parm[key]) + "\n")
    timenow = datetime.datetime.now()
    writing_file0.write("date time: " + \
        timenow.strftime('%Y/%m/%d/%H:%M:%S') + "\n")
    writing_file0.close()
#-------------
def read_in(path, Vmean):
    existing_parms = {}
        # define dic type Variable
    # set default value
    existing_parms["parm_FFC_sigma"] = 10.0
    existing_parms["parm_subtractBv"] = "False"
    existing_parms["parm_leveladj"] = "Auto"
    existing_parms["parm_Shadow"] = 25.0
    existing_parms["parm_Prph"] = 10.0
    existing_parms["parm_correct_fac"] = 0.0
    if Vmean < 128.0:
        BkThs = 128.0
    else:
        BkThs = int(Vmean)

    existing_parms["parm_Binary"] = BkThs
    existing_parms["parm_Gmixrate"] = 0.0
    existing_parms["parm_Gain"] = 0.0
    existing_parms["bool_PFFcOnly"] = "False"
    existing_parms["parm_Fg_tL"] = 128
    existing_parms["parm_HybridC"] = "Off"
    existing_parms["Fg_mask_type"] = "Type2"
    existing_parms["bgG_gamma"] = 0.64
    existing_parms["parm_Condition"] = "Normal"
    existing_parms["existing"] = "No"
    is_file = os.path.isfile(path + '_parameter.txt')
    if is_file:
        # reading existing parameters
        with open(path + '_parameter.txt','r') as read_file:
            lines = read_file.readlines()
            if len(lines) >= 15:
                for l in range(len(lines)-15, len(lines)-1):
                    eachline = lines[l].strip()
                    i = eachline.find(":")
                    key = eachline[:i]
                    value = eachline[i+2:]
                    existing_parms[key] = value
                    existing_parms["existing"] = "Yes"

    return existing_parms

# ------ B level adjustment ----------
def B_level_adjustment(impsCh, Mode, L_max, TRCType):
    # instant pre-processing for very yellowed image
    impsCht2 = []
    for i in range(3):
        if TRCType == "Perceptual" and LnProcess == False:
            impsCht2.append(sRGB_TRC_decode(Duplicator().run(impsCh[i]), L_max))
        else:
            impsCht2.append(Duplicator().run(impsCh[i]))
    statR = get_cropped_imp_stat(impsCht2[0])
    statG = get_cropped_imp_stat(impsCht2[1])
    statB = get_cropped_imp_stat(impsCht2[2])
    RGmean = (statR.mean + statG.mean) / 2.0
    Conversion = False
    if RGmean * 0.7  > statB.mean:
        Conversion = IJ.showMessageWithCancel("Very yellowed image was detected.",\
                                              "B level will be adjusted automatically.")
        if Conversion == True:
            factor = RGmean * 0.8 / statB.mean
            impsCht2[2].getProcessor().multiply(factor)
            impsCht2[2].getProcessor().gamma(0.8)
            statB2 =  get_cropped_imp_stat(impsCht2[2])
            if statG.max > statB2.max:
                factor = 1.05 * statG.max / statB2.max
                impsCht2[2].getProcessor().multiply(factor)
    impsCht1 = []
    for i in range(2):
        impsCht1.append(impsCh[i])
    if Conversion == True:
        if TRCType == "Perceptual" and LnProcess ==False:
            adjustedB = sRGB_TRC_encode(impsCht2[2], L_max)
        else:
            adjustedB = impsCht2[2]
    else:
        adjustedB =impsCh[2]
    return adjustedB, Conversion

#-------------
# Main
LnProcess = True # Do Linear Process?
CountN = 0
FileCount = 0
JobDo = True
MODE32 = "32-bit"
LMAX32 = 1.0
path = ""
Mode0 = ""
window = WM.getActiveWindow()
imgCurrent = WM.getCurrentImage()
if not imgCurrent:
    od = OpenDialog("Choose a file")
    folder = od.getDirectory()
    file = od.getFileName()
    if file:
        path = folder + file
        IJ.log(path)
        imps = BF.openImagePlus(path) # Open file with Bio-format Plugin
        #imp0 = imps[0]
    else:
        IJ.log("Job Canceled")
        JobDo = False
else: # target image is already open
    impgot = IJ.getImage()
    imps = [impgot]
    file = imps[0].getTitle()
    try:
        folder = imps[0].getOriginalFileInfo().directory
    except:
        folder = IJ.getDirectory("Output_directory")
    path = folder + file
if JobDo == True:
    bitdepth = imps[0].getBitDepth()
    setMaxValueTo1(imps[0], bitdepth) # only effective in original 32bit
    if bitdepth != 0:
        L_max = set_Lmax(imps[0])
        imps[0].setDisplayRange(0.0, L_max)
        stat0 = imps[0].getStatistics()
        Vmean = stat0.mean
            # Vmean: for culculating threshold of Bg correction layer
        if bitdepth == 16:
            Vmean = int(Vmean / 255.99)
            Mode0 = "16-bit"
        elif bitdepth ==32:
            Vmean = int(Vmean * (255.0 / L_max))
            Mode0 = "32-bit"
        else: # 8 or 24bit (colorProcessor)
            Mode0 = "8-bit"
        Vmean = Vmean + 3
        if bitdepth == 32:
            IJ.showMessage("32bit Max L Value: " + str(L_max))
        #### calling parameter input dialogs
        parm = NoviceMenu(path, Vmean)
        if parm[0] == "Expert":
            parm = getParameters(path, Vmean)
        print ("Parm[0]=", parm[0])
        #### parameter input end
        if parm[0] != "Stop":
            if parm[0] == "Auto": # parm[0] = Mode
                Mode = Mode0 # set bitDepth info
            imps[0].show()
            if parm[6] == 56: # parm_Fg_tL (56: linear / 128: Perceptual)
                TRCType = "Linear"
            else:
                TRCType = "Perceptual"
            impR, impG, impB, impRprc, impGprc, impBprc = RGBmanipulation\
                    (folder, file, parm, imps[0], L_max, Mode, TRCType)
            base(impR, impG, impB, impRprc, impGprc, impBprc, folder, file, \
                 parm, L_max, Mode, TRCType)
            imps[0].close()
    else:
        IJ.showMessage("This image is not compatible with this program.")
IJ.log("Finish")
