function XT_Jove_Labkit_TH(aImarisApplicationID)
% PAM TH Quantification
% This XT use a Imaris window to batch process all ims files inside a folder.
% If random error appeared and the batch failed in the middle:
    % no worry, this XT can be re-run in a new Imaris window.
    % all the processed files will be skipped, and directly start from the failed file.
% Written by Ren Mengda
%     <CustomTools>
%      <Menu>
%        <Item name="PAM_TH_Processing" icon="Matlab">
%          <Command>MatlabXT::PAM_TH_Processing(%i)</Command>
%        </Item>
%      </Menu>
%    </CustomTools>
if ~nargin;aImarisApplicationID = 0;end % for debug
%%
if ~isa(aImarisApplicationID, 'Imaris.IApplicationPrxHelper')
    javaaddpath ImarisLib.jar
    vImarisLib = ImarisLib;
    if ischar(aImarisApplicationID)
        aImarisApplicationID = round(str2double(aImarisApplicationID));end
    vImarisApplication = vImarisLib.GetApplication(aImarisApplicationID);
else;vImarisApplication = aImarisApplicationID;end
%% User input ---1/2: General Settings
% rtFolder = uigetdir('','select the folder of ims files (containing at least DAPI channel) to batch process');
ims_Folder = 'D:\QMDownload\9\4\jove\ims\images_ims';
labkitOut_Folder = 'D:\QMDownload\9\4\jove\ims\images_ims_ChannelSplitOut_LabkitOut';
Ch_DAPI = 1;
reVoxelSizeX = [];
reVoxelSizeY = [];
reVoxelSizeZ = [];
% Whether to attach the unprocessed Labkit images?
    % default = 0 to skip. Change to 1 only if to visualize/debug the process of refinement of Labkit segmented images
attchLabkitRawAsk = 0; 
%% User input ---2/2: Setting Detect DAPI spots
SpotsParameterAss = {% diameter, FilterString
    1.2,['"Quality" above 94.5,"Intensity Center Ch=aSizeC+1" above 0.5'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.0001'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.001'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.01'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.05'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.07'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.08'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.09'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.10'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.15'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.17'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.20'];
    1.2,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.25'];
    0.3,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.000001'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.001'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.01'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.10'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.15'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.20'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.25'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.30'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.35'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.40'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.50'];
    0.6,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.60'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.01'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.05'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.10'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.15'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.20'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.25'];
    0.9,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.30'];
    1.5,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.05'];
    1.5,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.10'];
    1.5,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.15'];
    1.5,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.20'];
    1.5,['"Quality" above 94.5,"Intensity Mean Ch=aSizeC+2" below 0.25'];
};
%% ===================================================================
outputFolder_savedIms = [ims_Folder,'_processed'];mkdir(outputFolder_savedIms);
aDir = dir([ims_Folder,filesep,'*.ims']);
%% Check and prepare files
aBar = waitbar(0,'Stage 1/3: Initialization ims files. May take quite a while');actioned = 0;
for iFile = 1:length(aDir);waitbar((iFile-1)/length(aDir),aBar);
    imsPath = [ims_Folder,filesep,aDir(iFile).name];
    try;aTemp = h5info(imsPath,['/Scene']);
    catch;disp(['...call ...vImarisApplication.FileOpen(''',imsPath,''') @ ',datestr(now,31)]);
        vImarisApplication.FileOpen(imsPath, ''); pause(2);
        disp(['   pass ...vImarisApplication.FileOpen(''',imsPath,''') @ ',datestr(now,31)]);
        imsPath_echo = char(vImarisApplication.GetCurrentFileName);
        if ~isequal(imsPath_echo,imsPath);
            uiwait(msgbox(['Unpredictable Communication Error occured during Stage 1.',char(10),...
                'No worry. Make following actions to continue:',char(10),...
                '--(1)Close all visible imaris window if have, then wait for 10 s',char(10),...
                '--(2)Manually open a new Imaris window',...
                '--(3)Re-run the script to continue.'],'Action required to continue'));killImaris;
            return;end
        vImarisApplication.FileSave(imsPath,'writer="Imaris5"');pause(2);
        actioned = 1;
    end;end
if actioned && false; % no need?
   uiwait(msgbox(['Stage 1:ims file checking/initialization is finished.',char(10),...
                'Make following actions to continue with the rest of the task:',char(10),...
                '--(1)Close all visible imaris window if have, then wait for 10 s',char(10),...
                '--(2)Manually open a new Imaris window',...
                '--(3)Re-run the script to continue.'],'Action required to continue'));
            killImaris;return;end
%% Quick check that all ims file are not dirty
for iFile = 1:length(aDir);waitbar((iFile-1)/length(aDir),aBar);
    imsPath = [ims_Folder,filesep,aDir(iFile).name];
    point_h5id = ['Points0'];
     try;try;aTemp = h5info(imsPath,['/Scene/Content/',point_h5id,'/CoordsXYZR']);
            catch;aTemp = h5info(imsPath,['/Scene8/Content/',point_h5id,'/Spot']);end
            aMsg = ['Error: current ims file is not newly converted and contains Spots history:',...
                char(10),imsPath,char(10),'Please always use newly converted ims files. Program will abort now.'];
            uiwait(msgbox(aMsg,'Error'));error(aMsg);end;end;disp('---All ims are confirmed with no Spots history.');
%% Batch process
waitbar(0,aBar,'Stage 2/3: Batch Process');
for iFile = 1:length(aDir);waitbar((iFile-1)/length(aDir),aBar);
    imsPath = [ims_Folder,filesep,aDir(iFile).name];
    %% Check if already processed and saved
    [~,trunk,ext] = fileparts(imsPath);
    imsPath_save = [outputFolder_savedIms,filesep,trunk,ext];
    if exist(imsPath_save,'file')>0;
        disp(['...skip this file because target already exist:',imsPath_save]);
        continue;end
    %% Open file
    disp(['...call ...vImarisApplication.FileOpen(''',imsPath,''') @ ',datestr(now,31)]);
    vImarisApplication.FileOpen(imsPath, ''); pause(2);
    disp(['   pass ...vImarisApplication.FileOpen(''',imsPath,''') @ ',datestr(now,31)]);
    imsPath_echo = char(vImarisApplication.GetCurrentFileName);
    %% Check file is correct
    if ~isequal(imsPath_echo,imsPath);
            uiwait(msgbox(['Unpredictable Communication Error occured during Stage 2.',char(10),...
                'No worry. Make following actions to continue:',char(10),...
                '--(1)Close all visible imaris window if have, then wait for 10 s',char(10),...
                '--(2)Manually open a new Imaris window',...
                '--(3)Re-run the script to continue.'],'Action required to continue'));
            killImaris;return;end
    %% Clear surpass scene to keep only top 3 items
    vScene = vImarisApplication.GetSurpassScene;
    nObj = vScene.GetNumberOfChildren;
    for iObj=(nObj-1):-1:3
        aObj = vScene.GetChild(iObj);
        vImarisApplication.GetSurpassScene.RemoveChild(aObj);end
    %% Get Dataset info
    aExtendMaxX = vImarisApplication.GetDataSet.GetExtendMaxX;
    aExtendMaxY = vImarisApplication.GetDataSet.GetExtendMaxY;
    aExtendMaxZ = vImarisApplication.GetDataSet.GetExtendMaxZ;
    aExtendMinX = vImarisApplication.GetDataSet.GetExtendMinX;
    aExtendMinY = vImarisApplication.GetDataSet.GetExtendMinY;
    aExtendMinZ = vImarisApplication.GetDataSet.GetExtendMinZ;
    aSizeX = vImarisApplication.GetDataSet.GetSizeX;
    aSizeY = vImarisApplication.GetDataSet.GetSizeY;
    aSizeZ = vImarisApplication.GetDataSet.GetSizeZ;
    aSizeC = vImarisApplication.GetDataSet.GetSizeC;aSizeC_initial = aSizeC;
    aSizeT = vImarisApplication.GetDataSet.GetSizeT;
    aType  = vImarisApplication.GetDataSet.GetType;
    aVersion = char(vImarisApplication.GetVersion);
    %% constant-0base
    vTime = 0;
    Ch_TH_labkit = aSizeC+1;
    Ch_TH_dist = aSizeC+2;
    Ch_TH_labkit_raw = aSizeC+3;
    Ch_TH_labkit_rawDenoise = aSizeC+4;
    dist_dark = 0;
    %% Reset the voxelsize to any of X,Y,Z if necessary.
    % Check the voxel size information of ims file in Imaris: Image Property
    % Usually the commercial confocal raw file are directly converted to ims file and should have correct voxel size (um) information.
    % If using tif to convert to ims file, the voxel size infomation may be not accurate, thus can specify here.
    % Anyway, you can also specify the voxel size Z (um) if only Z size is not accurate in the ims file.
    if ~isempty(reVoxelSizeX)
        vImarisApplication.GetDataSet.SetExtendMaxX(aExtendMinX + reVoxelSizeX * aSizeX);end
    if ~isempty(reVoxelSizeY)
        vImarisApplication.GetDataSet.SetExtendMaxY(aExtendMinY + reVoxelSizeY * aSizeY);end
    if ~isempty(reVoxelSizeZ)
        vImarisApplication.GetDataSet.SetExtendMaxZ(aExtendMinZ + reVoxelSizeZ * aSizeZ);end
    %% Update Info
    aExtendMaxX = vImarisApplication.GetDataSet.GetExtendMaxX;
    aExtendMaxY = vImarisApplication.GetDataSet.GetExtendMaxY;
    aExtendMaxZ = vImarisApplication.GetDataSet.GetExtendMaxZ;
    aExtendMinX = vImarisApplication.GetDataSet.GetExtendMinX;
    aExtendMinY = vImarisApplication.GetDataSet.GetExtendMinY;
    aExtendMinZ = vImarisApplication.GetDataSet.GetExtendMinZ;
    Xvoxelspacing= (aExtendMaxX-aExtendMinX)/aSizeX;
    Yvoxelspacing= (aExtendMaxY-aExtendMinY)/aSizeY;
    Zvoxelspacing= (aExtendMaxZ-aExtendMinZ)/aSizeZ;
    %% Clone the current dataset
    disp(['...make clone of the current dataset']);
    vDataSetClone = vImarisApplication.GetDataSet.Clone;pause(1);
    if attchLabkitRawAsk;vDataSetClone.SetSizeC(aSizeC_initial+4);
        vDataSetClone.SetChannelName(Ch_TH_labkit_raw-1,'Labkit_raw');
        vDataSetClone.SetChannelName(Ch_TH_labkit_rawDenoise-1,'Labkit_rawDenoise');
        vDataSetClone.SetChannelRange(Ch_TH_labkit_raw-1,0,3);
        vDataSetClone.SetChannelRange(Ch_TH_labkit_rawDenoise-1,0,3);
        vDataSetClone.SetChannelColorRGBA(Ch_TH_labkit_raw-1,sum([0,255,0,0].*[1,256,256^2,256^3]));
        vDataSetClone.SetChannelColorRGBA(Ch_TH_labkit_rawDenoise-1,sum([0,255,255,0].*[1,256,256^2,256^3]));
    else;vDataSetClone.SetSizeC(aSizeC_initial+2);end
    vDataSetClone.SetChannelName(Ch_TH_labkit-1,'Labkit_optimized');
    vDataSetClone.SetChannelRange(Ch_TH_labkit-1,0,3);
    vDataSetClone.SetChannelColorRGBA(Ch_TH_labkit-1,sum([255,0,0,0].*[1,256,256^2,256^3]));
    vDataSetClone.SetChannelName(Ch_TH_dist-1,'TH_distanceTransform');
    vDataSetClone.SetChannelRange(Ch_TH_dist-1,0,10);
    vDataSetClone.SetChannelColorRGBA(Ch_TH_dist-1,sum([255,255,255,0].*[1,256,256^2,256^3]));
    %% import the Labkit tif as a new channel
    if true
        aLabkitDir = dir([labkitOut_Folder,filesep,'*',trunk,'_C*.tif']);
        assert(length(aLabkitDir)==1,...
            ['Cannot find unique labkit output file for ims file:',imsPath,...
            '. Explore how the Labkit output fine name differs from the original stack name']);
        afn_tifStack = [labkitOut_Folder,filesep,aLabkitDir(1).name];
    else %debug
        afn_tifStack = 'D:\QMDownload\9\4\jove\ims\Q.+w-wk3-TH635-0024.tif';end
    info = imfinfo(afn_tifStack);
    num_imagesZ = numel(info);
%     img_type = ['uint' num2str(max([8,info(1).BitDepth]))];
    [img_size1_Height,img_size2_Width] = size(imread(afn_tifStack, 1, 'Info', info));
    hwz_stack = zeros([img_size1_Height,img_size2_Width,num_imagesZ]);
    if attchLabkitRawAsk;hwz_stack_raw = hwz_stack;end
    for k = 1 : num_imagesZ
        A = 0+(imread(afn_tifStack,k,'Info',info)>0)';
        if attchLabkitRawAsk;hwz_stack_raw(:,:,k) = A(:,:,1);end
        %% slice denoise and fill-hole ---KEY Parameter!
        A=imgaussfilt(A,1.5)>0.25;
        A=imdilate(A,strel('square',5));
        A=~bwareaopen(~A,200);
        A=imerode(A,strel('square',5));
        hwz_stack(:,:,k) = A(:,:,1);end;
    switch char(aType)
        case 'eTypeUInt8',
            vDataSetClone.SetDataVolumeAs1DArrayBytes(uint8(hwz_stack(:)),Ch_TH_labkit-1,vTime);
            if attchLabkitRawAsk;
                vDataSetClone.SetDataVolumeAs1DArrayBytes(uint8(hwz_stack_raw(:)),Ch_TH_labkit_raw-1,vTime);
                vDataSetClone.SetDataVolumeAs1DArrayBytes(uint8(hwz_stack(:)),Ch_TH_labkit_rawDenoise-1,vTime);
            end
        case 'eTypeUInt16'
            vDataSetClone.SetDataVolumeAs1DArrayShorts(uint16(hwz_stack(:)),Ch_TH_labkit-1,vTime);
            if attchLabkitRawAsk;
                vDataSetClone.SetDataVolumeAs1DArrayShorts(uint16(hwz_stack_raw(:)),Ch_TH_labkit_raw-1,vTime);
                vDataSetClone.SetDataVolumeAs1DArrayShorts(uint16(hwz_stack(:)),Ch_TH_labkit_rawDenoise-1,vTime);
            end
        case 'eTypeFloat';
            vDataSetClone.SetDataVolumeAs1DArrayFloats(single(hwz_stack(:)),Ch_TH_labkit-1,vTime);
            if attchLabkitRawAsk;
                vDataSetClone.SetDataVolumeAs1DArrayFloats(single(hwz_stack_raw(:)),Ch_TH_labkit_raw-1,vTime);
                vDataSetClone.SetDataVolumeAs1DArrayFloats(single(hwz_stack(:)),Ch_TH_labkit_rawDenoise-1,vTime);
            end;end
    %% Gauss Background Subtract DAPI channel three times
    GuBackSubChNmAss = repmat(Ch_DAPI-1,1,3);
    GaussBackSubChWidths = [3,3,1];
    for iCh = 1:length(GuBackSubChNmAss)
        GuBackSubChNm = GuBackSubChNmAss(iCh);
        GuBackSubChWidth = GaussBackSubChWidths(iCh);
        disp(['...Gauss Background Subtract DAPI channel=',num2str(GuBackSubChNm),', sigma=',num2str(GuBackSubChWidth)]);
        vImarisApplication.GetImageProcessing.SubtractBackgroundChannel(vDataSetClone,GuBackSubChNm,GuBackSubChWidth);
    end
    %% Gauss Blur DAPI channel with sigma 0.2 um
    GaussChsNmAss = [Ch_DAPI-1];
    GaussChWidths = [0.2];
    for iCh = 1:length(GaussChsNmAss)
        GuChNm = GaussChsNmAss(iCh);
        GuChWidth = GaussChWidths(iCh);
        disp(['...Gauss Smooth DAPI channel=',num2str(GuChNm),', sigma=',num2str(GuChWidth)]);
        vImarisApplication.GetImageProcessing.GaussFilterChannel(vDataSetClone,GuChNm,GuChWidth);end
    %% Create TH Surface
    ip = vImarisApplication.GetImageProcessing;
    Threshold_binaryTH = 0.3;% for 0/1 binary volume, use 0.3 as threshold.---KEY Parameter!
    aSurface_TH_labkit = ip.DetectSurfaces(vDataSetClone,[],Ch_TH_labkit-1,Xvoxelspacing,0,false,Threshold_binaryTH,'"Number of Voxels" above 300');
    aSurface_TH_labkit.SetName('TH');
    aColor_TH = [0,255,0,0];
    aSurface_TH_labkit.SetColorRGBA(sum(aColor_TH.*[1,256,256^2,256^3]));
    vImarisApplication.GetSurpassScene.AddChild(aSurface_TH_labkit, -1);pause(1);
    %% Use TH Surface to mask TH Mask channel again to clean it for Distance Transformation, and also mask back
    vMaskingDataSet = aSurface_TH_labkit.GetMask(aExtendMinX,aExtendMinY,aExtendMinZ,...
        aExtendMaxX,aExtendMaxY,aExtendMaxZ,...
        aSizeX,aSizeY,aSizeZ,vTime);
    vMaskingLogical = (vMaskingDataSet.GetDataVolumeAs1DArrayBytes(0,0)) == 1;
    % mask TH again
    switch char(aType)
        case 'eTypeUInt8',
            vMaskedArray_TH = typecast(vDataSetClone.GetDataVolumeAs1DArrayBytes(Ch_TH_labkit-1,vTime),'uint8');
            vMaskedArray_TH(~vMaskingLogical) = 0;vMaskedArray_TH(vMaskingLogical) = 1;
            vDataSetClone.SetDataVolumeAs1DArrayBytes(uint8(vMaskedArray_TH), Ch_TH_labkit-1,vTime);
        case 'eTypeUInt16'
            vMaskedArray_TH = typecast(vDataSetClone.GetDataVolumeAs1DArrayShorts(Ch_TH_labkit-1,vTime),'uint16');
            vMaskedArray_TH(~vMaskingLogical) = 0;vMaskedArray_TH(vMaskingLogical) = 1;
            vDataSetClone.SetDataVolumeAs1DArrayShorts(uint16(vMaskedArray_TH), Ch_TH_labkit-1,vTime);
        case 'eTypeFloat';
            vMaskedArray_TH = vDataSetClone.GetDataVolumeAs1DArrayFloats(Ch_TH_labkit-1,vTime);
            vMaskedArray_TH(~vMaskingLogical) = 0;vMaskedArray_TH(vMaskingLogical) = 1;
            vDataSetClone.SetDataVolumeAs1DArrayFloats(single(vMaskedArray_TH), Ch_TH_labkit-1,vTime);end
    %% Set cloned dataset to single
    ChangeDataSetType = 'single';
    aType = Imaris.tType.eTypeFloat;
    disp(['...set clone type to: ',ChangeDataSetType]);
    vDataSetClone.SetType(aType);pause(0.5);
    disp(['...re-check current type: type = ',char(vDataSetClone.GetType)]);
    %% Distance Transformation of cleaned TH Mask to new channel
    vDataSetClone.SetDataVolumeAs1DArrayFloats(single(vMaskedArray_TH),Ch_TH_dist-1,vTime)
    vImarisApplication.GetImageProcessing.DistanceTransformChannel(vDataSetClone,Ch_TH_dist-1,1,dist_dark);
    %% Detect total DAPI spots    
    for iSpot = 1:size(SpotsParameterAss,1)
        disp(['...creating spots [',num2str(iSpot),'/',num2str(size(SpotsParameterAss,1)),']']);
        aDiameter = SpotsParameterAss{iSpot,1};
        aFilterStr = SpotsParameterAss{iSpot,2};
        % process the filter string
        aFilterStr = strrep(aFilterStr,'aSizeC+2',num2str(aSizeC_initial+2));
        aFilterStr = strrep(aFilterStr,'aSizeC+1',num2str(aSizeC_initial+1));
        aFilterStr = strrep(aFilterStr,'aSizeC',num2str(aSizeC_initial));
        Spots_DAPI = ip.DetectSpots2(vDataSetClone,[],Ch_DAPI-1,aDiameter,0,aFilterStr);
        Spots_DAPI.SetName(aFilterStr);
        aColor_DAPI = [255,0,0,0];
        Spots_DAPI.SetColorRGBA(sum(aColor_DAPI.*[1,256,256^2,256^3]));
        Spots_DAPI.SetVisible(0);
        vImarisApplication.GetSurpassScene.AddChild(Spots_DAPI, -1);pause(1);
    end;disp('...all Spots crerated');
    %%
    vImarisApplication.SetDataSet(vDataSetClone);pause(1);
    vImarisApplication.FileSave(imsPath_save,'writer="Imaris5"');
end;vImarisApplication.Quit;
%% Data extraction
bDir = dir([outputFolder_savedIms,filesep,'*.ims']);
aOmniCell = cell(length(aDir)+1,size(SpotsParameterAss,1)+1);
aOmniCell{1,1} = 'sample name';
aOmniCell(2:end,1) = {bDir.name};
aOmniCell(1,2:end) = cellfun(@(x,y) strrep(['SpotsDiameter[',num2str(x),']Filter_',y],',',''),SpotsParameterAss(:,1),SpotsParameterAss(:,2),'uni',0);
waitbar(0,aBar,'Stage 3/3: Counting spots');
for iFile = 1:length(bDir);waitbar((iFile-1)/length(bDir),aBar);
    imsPath = [outputFolder_savedIms,filesep,bDir(iFile).name];
    % use hdf5 to read the spots quantity
    for iSpot = 1:size(SpotsParameterAss,1)
        point_h5id = ['Points',num2str(iSpot-1)];
        try;try;aTemp = h5info(imsPath,['/Scene/Content/',point_h5id,'/CoordsXYZR']);
            catch;aTemp = h5info(imsPath,['/Scene8/Content/',point_h5id,'/Spot']);end
            aSize = aTemp.Dataspace.Size;
            aOmniCell{iFile+1,iSpot+1} = aSize(end);
        end;end;end;waitbar(1,aBar,'Analysis is finished.');
%% output spreadsheet
afn_csv = [outputFolder_savedIms,'\SpotsQuantificationSummary_',datestr(now,'yyyymmddHHMMSSFFF'),'.csv'];
fid = fopen(afn_csv,'w+','l','UTF-8');
for i = 1 : size(aOmniCell,1)
    for j = 1 : size(aOmniCell,2)
        fprintf(fid,'%s',num2str(aOmniCell{i,j}));
        if j<size(aOmniCell,2);fprintf(fid,',');
        elseif j==size(aOmniCell,2);
            if ispc;fprintf(fid,'\r\n');
            else;fprintf(fid,'\n');end;end
              end;end;fclose(fid);
winopen(afn_csv);
uiwait(msgbox(['Task is finished.',char(10),...
    'See the output file:',char(10),afn_csv],'Task finished'));
%%
function killImaris
    % killImaris - Terminate all Imaris-related processes across platforms
    disp('...starting to kill Imaris processes...');
    try;if ispc  % Windows
            processes = {'bitplane.exe', 'Imaris.exe', ...
                        'ImarisServerIce.exe', 'CrashReporter.exe', 'WerFault.exe'};
            for i = 1:length(processes)
                system(['taskkill /F /IM ' processes{i} ' 2>nul']);end
        else % macOS & Linux/Unix
            system('pkill -i -f "imaris|bitplane|imarisServerIce|crashreporter" 2>/dev/null');end
    catch  % Ignore errors
    end;pause(1);disp('......finished killing Imaris processes...');
end
end