I often use normalize_local to bring up the signals on microscopy images. I would like to know what are the steps in the pipeline it uses to process the images as I need to explain in a publication. Is there any documentation I can consult or even a pseudo-code somewhere explaining the algorithm?
This may not be useful to you, but you can elevate the verbosity using the following command: v +. If you require a specific level, replace + with a number. Now, you need to know how to parse this information.
[gmic]./ Normalize image [0] locally, with amplitude 3, radius 16, neighborhood smoothness 4% and average smoothness 2%.
[gmic]./normalize_local/*foreach/*local/ Erode image [0] with kernel of size 33 and neumann boundary conditions.
[gmic]./normalize_local/*foreach/*local/ Split image [0] along the 'c'-axis.
[gmic]./normalize_local/*foreach/*local/ Compute pointwise minimum of images [0,1,2].
[gmic]./normalize_local/*foreach/*local/ Dilate image [0] with kernel of size 33 and neumann boundary conditions.
[gmic]./normalize_local/*foreach/*local/ Split image [0] along the 'c'-axis.
[gmic]./normalize_local/*foreach/*local/ Compute pointwise maximum of all images [0,1,2] together.
[gmic]./normalize_local/*foreach/ Blur image [0] with standard deviation 2%, neumann boundary conditions and gaussian kernel.
[gmic]./normalize_local/*foreach/ Blur images [1,2] with standard deviation 4%, neumann boundary conditions and gaussian kernel.
[gmic]./normalize_local/*foreach/ Subtract image [1] to image [2].
[gmic]./normalize_local/*foreach/ Add 0.01 to image [4].
[gmic]./normalize_local/*foreach/ Subtract image [1] to image [0].
[gmic]./normalize_local/*foreach/ Divide images [0,4].
[gmic]./normalize_local/*foreach/ Multiply images [1,2] by 4.
[gmic]./normalize_local/*foreach/ Multiply image [3] by -3.
[gmic]./normalize_local/*foreach/ Add image [3] to image [1].
[gmic]./normalize_local/*foreach/ Add images [2,3].
[gmic]./normalize_local/*foreach/*if/ Compute pointwise maximum between image [1] and 0.
[gmic]./normalize_local/*foreach/*if/ Compute pointwise minimum between image [2] and 255.
[gmic]./normalize_local/*foreach/ Subtract image [1] to image [2].
[gmic]./normalize_local/*foreach/ Multiply images [0,2].
[gmic]./normalize_local/*foreach/ Add images [0,1].
[gmic]./normalize_local/*foreach/*if/ Cut image [0] in range [0,255].
@afre Many thanks for the verbosity tip. Indeed, not so straightforward to understand the method. I was hoping to get a high in level description, maybe something helpful from the source code.
#@cli normalize_local : _amplitude>=0,_radius>0,_n_smooth>=0[%],_a_smooth>=0[%],_is_cut={ 0 | 1 },_min=0,_max=255
#@cli : Normalize selected images locally.
#@cli : Default values: 'amplitude=3', 'radius=16', 'n_smooth=4%', 'a_smooth=2%', 'is_cut=1', 'min=0' and 'max=255'.
#@cli : $ image.jpg normalize_local 8,10
normalize_local :
check "${1=3}>=0 && ${2=16}>0 && isbool(${5=1})" skip ${3=4%},${4=2%},${6=0},${7=255}
e[^-1] "Normalize image$? locally, with amplitude $1, radius $2, neighborhood smoothness $3 and
average smoothness $4."
foreach {
+l { erode {2*$2+1} s c min } # 1. Erode by 2*radius+1 as a new image into list of images
+l.. { dilate {2*$2+1} s c max } # 2. Dilate second last image as a new image into list of images
+b... $4 b[-3,-2] $3 # 3. Blur last third image by a_smooth and add to image. Then, blur third and second last image by n_smooth
+-.. ... +. 0.01 -[-5] [-4] /[-5,-1] # 4. - means to subtract. Add new image of second last image subtracted by last third image. The +. means to add last image. Yes, + has multiple rules to it, when it is by itself only, then it's just add. So, you are adding the sum of last image by .001 as a new image. Then you subtract the fifth last image by fourth last image. Then you divide fifth last image by last image and combine result into fifth last image.
*[-3,-2] {$1+1} *. -$1 +... . +[-2,-1] # I think you can take it from here.
if $5 max.. $6 min. $7 fi
-. .. *[-3,-1] +
if $5 c $6,$7 fi
}
Don’t feel like doing everything, but I think you can get it after the steps I mentioned. You’d have to copy and paste the code into user.gmic, and use return command to see what’s going on. G’MIC is largely based on array programming, though some like myself prefer to use other programming approach whenever reasonable.
s c means split channels. So, you split channels. min generates the min of all images. same to max.
Please review Rep’s comments next to the code. I did not see them at first due to the narrow viewport of the mobile screen. Between that and the verbose output, you should be able to glean enough information. For extended support, please refer to the link below for details on the G’MIC interpreters. Essentially, what you see above are abstracted functions for ease of use, requiring less setup than your classic programming language, which is the charm of G’MIC. There is also a C++ core called CImg. In other words, G’MIC is kind of a pseudocode once you understand how to read it.
Many thanks @afre for your help and the suggestion to display step by step of the code. I did add ‘display’ and I can see the images being changed each step.
What I had in mind was to find out the ‘algorithm’ with an explanation of its steps so I can then understand why the steps taken lead to the local normalization. For example, this is a local normalization approach (maybe the same one in gmic ?),
Unfortunately, just by displaying the changing images doesn’t help me understand the algorithm. Is gmic’s normalize_local a textbook algorithm or one introduced by the developers? If known, please share a reference.