-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathimfuse
executable file
·5914 lines (5491 loc) · 196 KB
/
imfuse
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#! /bin/bash
# imfuse - Combines focus stackshot images to one overall sharp image.
Version="0.9.14-beta"
### Information
usage() {
echo "imfuse: Combines focus stackshot images to one overall sharp image.
Usage:
imfuse [OPTIONS] -- IMAGES...
imfuse assumes that the alphanumerical order of the input images goes
from front to back; for the other way around use option --revert.
imfuse results suffer a lot from JPG compression. If you have JPG images
as a source only, first convert them to TIF or PNG before doing anything else.
Tool stackprepare can do that.
Dependencies:
ImageMagick 7 Mandatory (command 'magick').
exiftool Optional, needed to set and transfer image metadata.
enfuse Optional, needed for options --enfuse and --bg=enfuse.
geeqie Optional, needed for option -V to show progress..
feh Optional, needed for option -W to show result image.
focus-stack Optional, needed for option --align.
https://github.com/PetteriAimonen/focus-stack
General options:
--align Align images. Rather use tool stackalign, because
this option is not well integrated in cache reusage.
-B, --basename [=NAME] Base name [+path] for output image.
If empty, the name of current directory is used.
-C, --cache [=DIR] Store generated masks and images in directory DIR.
They can be re-used in later runs of imfuse. See also
option --rmcache. Default DIR: ~/.cache/imfuse
For best performance the cache should be on an SSD.
--cacheformat=FORMAT Store cache files in format FORMAT. Default: $(maskarg_defaultvalue cacheformat word1)
Should be a lossless format like tif or png.
mpc is optimized for fast access by ImageMagick,
but needs lots of disk space. In case you run out
of disk space, use 'tif'.
--exif [=IMAGE] Transfer exif meta data from IMAGE or first image.
-f, --force Force imfuse to run even if output image exists.
--format=FORMAT Store result in image format FORMAT. Default: $(maskarg_defaultvalue format word1)
-h, --help Show this help and exit.
--layered Store backgrounds and substacks in layered TIF.
--license Show license (GPLv2) and exit.
--limit-memory=ARG Limit amount of used memory. Default: 80%
ARG can be a % value or an absolute value for MB.
-L, --longname Create long filename containing all options.
-o, --output=FILE Result image file name. See also --basename.
--revert Revert order of source images.
--rmcache Clean cache and exit.
--version Show imfuse version and exit.
--video Generate a video of shown intermediate images.
-V Show intermediate images with image viewer geeqie.
-W Show result image with image viewer feh.
For keyboard shortcuts see 'man feh'. Examples:
arrow left/right: switch images
arrow up/down: zoom in/out
/: zoom to window size
*: zoom 100%
i: show/hide imfuse options
d: show/hide file name
-X Store [and show] mask and depth map, too.
The options below can take additionally arguments [=ARG].
Multiple arguments are comma-separated. Example: --morphology=r3,blur
Arguments taken by all mask generating options: (Mostly you only need w).
w Weight of mask. Percent value from 0 to 100.
Of interest if specifying more than one mask generation method.
Example: --morphology --darkness=w15
This will generate two masks. The saturation mask will only have
noteable effect where the morphology mask strength is below 25%.
mask [=yes|no] Apply --mask* options. Default is yes except for channel and
image comparision options.
C= Colorspace to use instead of default $(maskarg_defaultvalue colorspace colorspace). Compare --colorspace.
c Colorspace channel to use. Counting up from 0 for first channel.
Example: C=HSL,c2 will use the saturation channel from HSL.
I= Image creation and comparision. For possible args see --background.
Example: --statistic=E=max will generate a mask from an evaluated
max image that was compared with the source image.
Compare step 2b: Mask generation - image comparision masks.
diff [=yes|no] Use difference of command result to source image.
Normally not neded to be set manually.
neg Negate / invert the mask. Most of interest for color channels.
t Threshold (removal) of low contrast areas.
T Threshold (removal) of high contrast areas.
level [=yes|no] Equalize histogram distribution to get compareable masks.
Default is yes except for image comparision options
and for some colorspace channel options.
Arguments additionally taken by some options:
r Radius. Must be an integer value.
R Second radius.
s Sigma. Takes also non-integer values like 0.75
S Second Sigma.
p A percent value.
P Second percent value
n An integer value.
N Second integer value.
(word) Some options take one or two word arguments.
Generally spoken, increased radius or sigma enhances contrast, reduces noise,
looses details and increases undesired seams. There's always a tradeoff.
The default values are set rather low and mostly profit from increasing.
As a metapher, think of radius and sigma as of brush sizes.
Step 1: Source image adjustment
These option do not modify the original files, but only adjust them in RAM.
In general you can skip this step.
--colorspace [=ARG] Colorspace in which to generate the masks.
Normally you don't need to adjust this option.
ARG takes arguments:
c channel
(word) colorspace Default: $(maskarg_defaultvalue colorspace colorspace)
colorspace can be one of 'magick -list colorspace'.
Some of interest: sRGB, CMYK, RGB
https://legacy.imagemagick.org/Usage/color_basics
--prepresize [=ARG] Resize images before mask generation. The result will
have the original size nonetheless.
ARG takes arguments:
p percent Default: $(maskarg_defaultvalue prepresize percent1)
(word) interpolation Default: $(maskarg_defaultvalue prepresize word1)
percent values >100% can make sense to get more detail.
Values <100% can speed up processing of large images
at the cost of precision.
word must be one of 'magick -list interpolate'
and adjusts resize interpolation.
Note that you would need to adjust radius and sigma in
mask options proportinally for similar results.
Percent values like in --fft are less affected.
Step 2: Mask generation
Options can be specified multiple times. The order does not matter.
The generated masks will be composed into one mask containing them all.
Most recommended:
--blur, --fft, --wavelet and --diffstat for fine detail.
--statistic and --morphology for strong edges.
Contrast masks:
The contrast mask options detect edges that indicate focused areas.
--blur [=ARG] Difference of blur.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue cutblur radius1)
s sigma1 Default: $(maskarg_defaultvalue cutblur sigma1)
S sigma2 Default: 1.6 * sigma1
(word) gaussian
Adjust only sigma, radius 0 is adjusted automatically.
If sigma2 is set to 0, --blur will compare the source
with a blurred version of itself directly.
gaussian enables slower but more accurate gaussian blur.
Compare quite similar but slower option --dog.
--cmd [=ARG] Custom ImageMagick option to create a mask.
Compare --cmddiff.
ARG additionally takes argument:
(word) string
string can be e.g. '-edge 2'. Avoid comma , in string.
There's also a use case without a command at all
but setting arguments C=,c only.
https://imagemagick.org/script/command-line-options.php
--cmddiff [=ARG] Custom ImageMagick option to create a mask.
The resulting mask will be a '-compose Difference'
comparision from command result with source image.
There's also a use case without a command at all
but setting arguments C=,c or I= only.
ARG additionally takes argument:
(word) string
string can be e.g. '-blur 0x5'. Avoid comma , in string.
https://imagemagick.org/script/command-line-options.php
--comet [=ARG] Comet edge detection. Compares rotated mean areas.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue comet radius1)
s sigma Default: $(maskarg_defaultvalue comet sigma1)
(word) mode Default: $(maskarg_defaultvalue comet word1)
mode can be one of 'magick -list compose'
Recommended: Lighten, Darken.
Adjust only sigma, radius 0 is adjusted automatically.
--compass [=ARG] Compass edge detection.
ARG additionally takes argument:
(word) mode Default: $(maskarg_defaultvalue compass word1)
mode can be one of 'magick -list compose'
--diffstat [=ARG] Compares two local statistics of source images.
Uses ImageMagick option -statistic. Compare --statistic.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue diffstat radius1)
(word) mode1 Default: $(maskarg_defaultvalue diffstat word1)
(word) mode2 Default: $(maskarg_defaultvalue diffstat word2)
mode1 and mode2 can be two of 'magick -list evaluate'.
--dog [=ARG] Difference of Gaussian.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue cutblur radius1)
s sigma1 Default: $(maskarg_defaultvalue cutblur sigma1)
S sigma2 Default: 1.6 * sigma1
Adjust only sigma, radius 0 is adjusted automatically.
Compare https://www.imagemagick.org/Usage/convolve/#dog
Quite similar but faster is option --blur.
--fft [=ARG] Discrete fourier transformation.
This option is in development and might change in future.
The arguments configure a mask that is multiplied with
the fourier magnitude part.
ARG additionally takes arguments:
r radius1 Default: $(maskarg_defaultvalue fft radius1)
R radius2
s sigma1
p percent Default: $(maskarg_defaultvalue fft percent1)
N number2 Default: $(maskarg_defaultvalue fft number2)
(word1) shape conversion Default: $(maskarg_defaultvalue fft word1)
(word2) shape form Default: $(maskarg_defaultvalue fft word2)
radius1 sets the size of the mask shape. Low values
increase contrast, high values increase detail.
The radius is a percent value relative to image size.
radius2 creates a second result compared with the first.
word1 defines the shape conversion. Possible:
gradient gaussian logVALUE powVALUE solid
For VALUE replace VALUE with a value like
2 10 100 1000, e.g. log2, log100, log1000, pow2, pow0.5
word2 is the mask shape. Possible word2:
circle square rhombus cross crossx
sigma applies -sigmoidal-contrast to the shape.
Negative values apply a +sigmoidal-contrast.
For 'solid' shape it defines a blur sigma instead.
percent1 sets the mid-point for -sigmoidal-contrast.
No effect for 'solid' shapes.
number2 gives the choice between low and high pass filter:
0 low pass filter
1 high pass filter
--freichen [=ARG] Frei-Chen edge detection.
ARG additionally takes arguments:
n mode number Default: $(maskarg_defaultvalue freichen number1)
(word1) compose mode Default: $(maskarg_defaultvalue freichen word1)
Possible mode numbers at:
http://www.imagemagick.org/Usage/convolve/#freichen
Composen mode can be one of 'magick -list compose'.
No useful settings known other than default.
--kirsch [=ARG] Kirsch edge detection.
--laplacian [=ARG] Laplacian edge detection.
ARG additionally takes argument:
n mode number Default: $(maskarg_defaultvalue laplacian number1)
Possible mode numbers at:
http://www.imagemagick.org/Usage/convolve/#laplacian
--log [=ARG] Laplacian of Gaussian. Needs rework, not useful yet.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue log radius1)
s sigma1 Default: $(maskarg_defaultvalue log sigma1)
S sigma2
Adjust only sigma, radius 0 is adjusted automatically.
If sigma2 is set, -log gives the difference of
two log results with sigma1 and sigma2.
--morphology [=ARG] Morphology edge detection.
ARG additionally takes arguments:
r radius Default: $(maskarg_defaultvalue morphology radius1)
R kernel iteration Default: $(maskarg_defaultvalue morphology radius2)
(word1) mode Default: $(maskarg_defaultvalue morphology word1)
(word2) kernel Default: $(maskarg_defaultvalue morphology word2)
mode can be one of 'magick -list morphology'.
try: edge, edgein, edgeout, dilate, smooth.
kernel can be one of 'magick -list kernel'.
Compare: https://imagemagick.org/Usage/morphology
--prewitt [=ARG] Prewitt edge detection.
--resize [=ARG] Resize image to percent and back to normal size.
p percent Default: $(maskarg_defaultvalue resize percent1)%
(word) interpolation Default: $(maskarg_defaultvalue resize word1)
interpolation can be one of 'magick -list interpolate'.
--roberts [=ARG] Roberts edge detection.
--sobel [=ARG] Sobel edge detection.
--statistic [=ARG] ImageMagick option -statistic.
ARG additionally takes arguments:
r radius1 Default: $(maskarg_defaultvalue statistic radius1)
R radius2
(word) mode Default: $(maskarg_defaultvalue statistic word1)
mode can be one of 'magick -list statistic'.
If radius2 is set, --statistic gives the difference of
two statistic results with radius1 and radius2.
--wavelet [=ARG] Based on ImageMagick option -wavelet-denoise.
ARG additionally takes arguments:
p percent1 Default: $(maskarg_defaultvalue wavelet percent1)%
P percent2
For percent value compare option -wavelet-denoise.
If percent2 is set, --wavelet gives the difference of
two wavelet results with percent1 and percent2.
Colorspace channel masks:
All channel options are variations of --channel.
Adding some of them with a rather low weight between w5..w25
can help to close gaps where edges to detect focus are rare.
Most useful are --darkness and --saturation.
Changing argument level is worth a try.
--channel [=ARG] Use a colorspace channel as mask.
ARG takes the general mask arguments:
w weight
C= colorspace
c colorspace channel
neg negate
level leveling Default: $(maskarg_defaultvalue channel level)
mask --mask* Default: $(maskarg_defaultvalue channel mask)
Colorspace can be one of 'magick -list colorspace'
The color channel is a number from 0..31.
Compare 'magick -list colorspace'.
--chroma [=ARG] Chroma, color strength. Same as --channel=C=HCL,c1
ARG takes the general mask arguments:
w weight
level leveling Default: $(maskarg_defaultvalue chroma level)
mask --mask* Default: $(maskarg_defaultvalue channel mask)
--darkness [=ARG] Darkness. Same as --channel=C=HSL,c2,neg
ARG takes the general mask arguments:
w weight
level leveling Default: $(maskarg_defaultvalue darkness level)
mask --mask* Default: $(maskarg_defaultvalue channel mask)
--lightness [=ARG] Lightness. Same as --channel=C=HSL,c2
ARG takes the general mask arguments:
w weight
level leveling Default: $(maskarg_defaultvalue lightness level)
mask --mask* Default: $(maskarg_defaultvalue channel mask)
--saturation [=ARG] Color saturation. Same as --channel=C=HSL,c1
ARG takes the general mask arguments:
w weight
level leveling Default: $(maskarg_defaultvalue saturation level)
mask --mask* Default: $(maskarg_defaultvalue channel mask)
Image comparision masks:
Adding --compose with e.g. =Overlay with a rather low weight
between w5..w25 can help to close gaps, especially in large dark areas.
--compose [=ARG] Composes evaluated min,max of source images with MODE.
ARG takes the general mask arguments:
level leveling Default: $(maskarg_defaultvalue compose level)
mask --mask* Default: $(maskarg_defaultvalue compose mask)
ARG additionally takes argument:
(word) mode Default: $(maskarg_defaultvalue compose word1)
Adding a '2', e.g. '--compose=overlay2' swaps min,max
to max,min and can give a different result.
mode can be one of 'magick -list compose'
Some modes of interest:
overlay interpolate colordodge hardlight reflect
softburn softlight linearlight pegtoplight
--depthmap [=ARG] Use a previously created depth map to get focus areas.
ARG takes the general mask arguments:
level leveling Default: $(maskarg_defaultvalue depthmap level)
mask --mask* Default: $(maskarg_defaultvalue depthmap mask)
ARG additionally takes argument:
(word) filename
Yet works well only if no --cut* options except
--cutless has been used for the depth map.
Sample use: imfuse creates a depth map with -X, you can
change it in an image editor, and use this option to
create the focus image based on the adjusted depth map.
--enfuse [=ARG] Image comparision with a basic enfuse result.
ARG takes the general mask arguments:
level leveling Default: $(maskarg_defaultvalue enfuse level)
mask --mask* Default: $(maskarg_defaultvalue enfuse mask)
--evaluate [=ARG] Evaluates from all source images with mode MODE and
compares the result with each source image.
ARG takes the general mask arguments:
level leveling Default: $(maskarg_defaultvalue evaluate level)
mask --mask* Default: $(maskarg_defaultvalue evaluate mask)
ARG additionally takes arguments:
(word) mode Default: max
mode can be one of 'magick -list evaluate'
Compare --max, --min, --mean, --median.
--max [=ARG] Same as --evaluate=max. Brightest pixels of stack.
Result is similar to --lightness.
ARG takes the general mask arguments:
level leveling Default: $(maskarg_defaultvalue max level)
mask --mask* Default: $(maskarg_defaultvalue max mask)
--mean [=ARG] Same as --evaluate=mean. Average of pixels in stack.
level leveling Default: $(maskarg_defaultvalue mean level)
mask --mask* Default: $(maskarg_defaultvalue mean mask)
--median [=ARG] Same as --evaluate=median. Median pixel of stack.
level leveling Default: $(maskarg_defaultvalue median level)
mask --mask* Default: $(maskarg_defaultvalue median mask)
--min [=ARG] Same as --evaluate=min. Darkest pixels of stack.
Result is similar to --darkness.
level leveling Default: $(maskarg_defaultvalue min level)
mask --mask* Default: $(maskarg_defaultvalue min mask)
Step 3: Mask merging
The contrast masks generated in step 2 are merged into one per image.
The merged result can be adjusted with --mask* options.
Most recommended: --maskblur and --maskwave.
These options can be specified multiple times, the order matters:
--maskblur [=ARG] Blur masks to enhance contrast and close minor gaps.
ARG takes argument:
r radius Default: $(maskarg_defaultvalue cutblur radius1)
s sigma Default: $(maskarg_defaultvalue cutblur sigma1)
p percent1 Default: 100%
P percent2 Default: 100-percent1%
(word) gaussian
Adjust only sigma, radius 0 is adjusted automatically.
percent1 defines how strong the blur will be applied.
percent2 defines how strong the original we remain.
gaussian enables slower but more accurate gaussian blur.
Compare ImageMagick option -blur.
--maskcmd [=ARG] Custom ImageMagick option to apply on merged masks.
ARG takes argument:
(word) string
string can be e.g. '-blur 0x5'. Avoid comma , in string.
https://imagemagick.org/script/command-line-options.php
--maskenhance [=ARG] Remove noise from mask.
ARG takes argument:
n iterations Default: $(maskarg_defaultvalue maskenhance number1)
Compare ImageMagick option -enhance.
--maskdespeckle [=ARG] Remove noise from mask.
ARG takes argument:
n iterations Default: $(maskarg_defaultvalue maskdespeckle number1)
Compare ImageMagick option -despeckle.
--maskfft [=ARG] Discrete fourier transformation.
This option is in development and might change in future.
ARG additionally takes arguments:
r radius1 Default: $(maskarg_defaultvalue fft radius1)
s sigma1
p percent Default: $(maskarg_defaultvalue fft percent1)
N number2 Default: $(maskarg_defaultvalue fft number2)
(word1) shape conversion Default: $(maskarg_defaultvalue fft word1)
(word2) shape form Default: $(maskarg_defaultvalue fft word2)
See option --fft for arguments.
--maskkuwahara [=ARG] Noise removal with kuwahara method.
ARG takes argument:
r radius Default: $(maskarg_defaultvalue maskkuwahara radius1)
Compare ImageMagick option -kuwahara.
--maskmorph [=ARG] Change shape of mask.
ARG takes arguments:
r radius Default: $(maskarg_defaultvalue maskmorph radius1)
R kernel iteration Default: $(maskarg_defaultvalue maskmorph radius2)
(word1) mode Default: $(maskarg_defaultvalue maskmorph word1)
(word2) kernel Default: $(maskarg_defaultvalue maskmorph word2)
radius is the kernel radius.
kernel iteration multiplies the kernel radius.
mode can be one of 'magick -list morphology'.
try: open, close, erode, dilate, smooth.
kernel can be one of 'magick -list kernel'.
CPU expensive option. Iterating the kernel is cheaper
than using a greater radius.
Compare: https://imagemagick.org/Usage/morphology
--maskstat [=ARG] Apply a statistic method for each mask pixel
to adjust it according to its neighborhood.
Option is applied to contrast masks only.
ARG takes arguments:
r radius Default: $(maskarg_defaultvalue maskstat radius1)
(word) statistic mode Default: $(maskarg_defaultvalue maskstat word1)
word1 can be one of 'magick -list statistic'.
Modes of interest: mean, median, gradient.
Mode mean softens the result and closes gaps.
Mode median sharpens the result and opens gaps.
Mode gradient sharpens the result, closes gaps
and adds noise in weak areas.
Compare ImageMagick option -statistic.
--maskthreshold [=ARG] Remove low (or high) contrast area.
ARG takes arguments:
t threshold1 Default: $(maskarg_defaultvalue maskthreshold threshold1)%
T threshold2 Default: $(maskarg_defaultvalue maskthreshold threshold2)%
threshold1 removes low contrast areas below a percent.
threshold2 removes high contrast areas above a percent.
--maskwave [=ARG] Remove noise in masks. Can close gaps, but can also
create artefacts in very low contrast areas.
ARG takes arguments:
p percent Default: $(maskarg_defaultvalue maskwave percent1)%
Compare ImageMagick option -wavelet-denoise.
These options can be specified only once, order does not matter:
--maskmerge [=ARG] Compose mode to merge multiple masks.
ARG takes arguments:
(word) mode Default: $(maskarg_defaultvalue maskmerge word1)
mode can be one of 'magick -list compose'.
Of interest: Plus Interpolate Multiply Exclusion Blend
--masklevel [=ARG] Levels merged masks into visible spectrum from 0%..100%
to provide the following focus and postprocessing option
arguments a full range from 0%..100%. Enabled by default.
ARG takes argument:
t round threshold Default: $(maskarg_defaultvalue masklevel threshold1)%
p min
P max
(word) mode Default: $(maskarg_defaultvalue masklevel word1)
mode can be one of:
all: Level all masks.
substack: Level only merged masks in current substack.
off: Do not level masks.
The round threshold rounds up or down to the next matching
rounded percent value. This helps to get same results
for cropped image parts as well as for whole images.
mode 'substack' should only be used to speed up test runs
with different mask merging options within one single
substack. Only mode 'all' reliably provides valid values.
percent values min and max allow to set absolute values.
This option is always executed as the last one of --mask*.
Step 4: Stack fusion
The source images are processed with the merged masks,
the overall sharp image will be generated.
For each source image a cut mask is generated based on the merged masks
and the following --cut* options.
The order of --cut* options matters, use --cutless always first.
All --cut* options except --cutless can be specified multiple times.
Recommended combination: --cutwave --cutbg --cutalpha
To strengthen foreground objects use --cutless.
--cutalpha [=ARG] Make cut semitransparent according to mask.
Nice result if used after --cutwave. Use --cutbg before.
ARG takes arguments:
p percent1 Default: $(maskarg_defaultvalue cutalpha percent1)%
P percent2 Default: $(maskarg_defaultvalue cutalpha percent2)%
For percent arguments compare --finalalpha.
If the result is too soft, try 'p-25,P150'.
--cutbg [=ARG] Create a background from current cuts.
To be used before --cutthreshold or --cutalpha.
ARG takes argument:
(word) mode Default: $(maskarg_defaultvalue cutbg word1)%
Possible modes:
background Use as background, applied after --final*.
compose Add to result before --final* options.
off Store background, but do not show it.
--cutblur [=ARG] Blur cut masks in final focus montage.
ARG takes argument:
r radius Default: $(maskarg_defaultvalue cutblur radius1)
s sigma Default: $(maskarg_defaultvalue cutblur sigma1)
p percent1 Default: 100%
P percent2 Default: 100-percent1%
(word) gaussian
Adjust only sigma, radius 0 is adjusted automatically.
percent1 defines how strong the blur will be applied.
percent2 defines how strong the original we remain.
gaussian enables slower but more accurate gaussian blur.
--cutcmd [=ARG] Custom ImageMagick option to apply on cut mask.
ARG takes argument:
(word) string
string can be e.g. '-blur 0x5'. Avoid comma , in string.
https://imagemagick.org/script/command-line-options.php
--cutless [=ARG] Strenghtens less contrasted objects in front of strong
contrasted background objects.
ARG takes argument:
p percent Default: $(maskarg_defaultvalue cutless percent1)%
Percent is the minimal intermediate contrast
difference between background and foreground object.
If unsharp areas appear, use a greater percent value.
If the foreground object still has holes, use a lower
percent value.
Useful percent values heavily depend on previous options.
Can be specified only once, the order matters.
--cutmax [=ARG] Remove parts of current cut mask that are weaker than
current max in result mask.
ARG takes argument:
p percent Default: 0
The percent value preserves some weak cut mask parts.
--cutmorph [=ARG] Change shape of cut mask. Default mode 'erode' shrinks
the cut mask to eliminate undesired fringes.
ARG takes arguments:
r radius Default: $(maskarg_defaultvalue cutmorph radius1)
R kernel iteration Default: $(maskarg_defaultvalue cutmorph radius2)
(word1) mode Default: $(maskarg_defaultvalue cutmorph word1)
(word2) kernel Default: $(maskarg_defaultvalue cutmorph word2)
radius is the kernel radius.
kernel iteration multiplies the kernel radius.
mode can be one of 'magick -list morphology'.
try: open, close, erode, dilate, smooth.
kernel can be one of 'magick -list kernel'.
CPU expensive option. Iterating the kernel is cheaper
than using a greater radius.
Compare: https://imagemagick.org/Usage/morphology
--cutsoft [=ARG] Similar to --cutblur, but paint default sharp cut mask
over blurred cut mask.
ARG takes argument:
s sigma Default: $(maskarg_defaultvalue cutsoft sigma1)
--cutthreshold [=ARG] Cut of parts of cut mask where current source mask is
weaker than threshold. Removes undesired seams, but also
the background. Preserve background with --cutbg before.
ARG takes arguments:
t threshold Default: $(maskarg_defaultvalue cutthreshold threshold1)%
--cutwave [=ARG] Soften the cut mask. Result looses a bit of sharpness,
but looks more friendly overall, and covers small issues.
ARG takes arguments:
p percent Default: $(maskarg_defaultvalue cutwave percent1)%
For percent value compare option -wavelet-denoise.
--substacks [=ARG] Split stack into different stacks (called substacks)
and fuse them separately. Can be specified multiple times.
ARG takes arguments:
r radius Default: 5%
p percent1 Default: 14%
P percent2
n number1
N number2
(word) kurt
You can specify single substacks or generate a set of
substacks. Please specify either percents or numbers.
- Given only one of percent or number, imfuse will
generate a set of substacks accordingly.
- word 'kurt' starts all substacks with image 1.
- If you specify two of percents or numbers, one single
substack within the given range of images is generated.
- radius specifies how many images neigboured substacks
should share. A percent value for radius is allowed.
Use --threshold or --alpha for transparent substacks.
The substacks will be composed over each other onto
a background (to specify with --background).
See also --layered to store the substacks as layers.
That allows manual rework of the results in gimp.
Step 5: Post processing
The order of the options on cli matters. Noteably --alpha with percent values
different from =0%,100% affects the following percent arguments.nd to have
a good start for --finalalpha.
All options can be specified multiple times.
--finalalpha [=ARG] Generate transparent image using the contrast mask as
alpha channel (=transparency channel).
The semitransparent result is composed over --background.
Compare similar but often better option --cutalpa.
ARG takes arguments:
p percent1 Default: $(maskarg_defaultvalue finalalpha percent1)%
P percent2 Default: $(maskarg_defaultvalue finalalpha percent2)%
(word) off
percent1 affects the low contrast areas with
a useful range of -100%..+50%.
percent2 affects the high contrast areas with
a useful range of +100%..+200%.
Higher values make less transparent.
The percent values are applied as '+level p%,P%'
on the transparency alpha channel.
If word 'off' is set, the alpha channel is disabled
without further action.
--finalblur [=ARG] Soft blur of bokeh in low contrast areas.
ARG takes arguments:
t threshold Default: $(maskarg_defaultvalue finalblur threshold1)%
s sigma1 Default: $(maskarg_defaultvalue finalblur sigma1)
S sigma2 Default: $(maskarg_defaultvalue finalblur sigma2)
Blurs low contrast areas below threshold with sigma1.
If sigma2 is given, the cut border between bokeh and
foreground is blurred for a soft transition.
--finalblur2 [=ARG] Soft blur of bokeh in low contrast areas.
Other than --finalblur it blurs according to the contrast
mask strengh. Low contrast is blurred more than high.
ARG takes arguments:
r radius1 Default: $(maskarg_defaultvalue finalblur2 radius1)
t threshold Default: $(maskarg_defaultvalue finalblur2 threshold1)%
A greater radius1 blures more.
threshold restricts blur to contrast mask strength.
--finalcmd [=ARG] Custom ImageMagick option to apply on result image.
ARG takes argument:
(word) string Default: $(maskarg_defaultvalue finalcmd word1)
string can be e.g. '-auto-level'. Avoid comma , in string.
https://imagemagick.org/script/command-line-options.php
--finalgamma Set gamma of image. Can improve contrast.
(word) value/auto Default: $(maskarg_defaultvalue finalgamma word1)
Gamma less than 1.0 darkens the image,
and gamma greater than 1.0 lightens the image.
'auto' sets gamma automatically.
Compare ImageMagick options -gamma and -auto-gamma.
--finalsharpen [=ARG] Sharpen result image.
ARG takes arguments:
r radius Default: $(maskarg_defaultvalue finalsharpen radius1)
s sigma Default: $(maskarg_defaultvalue finalsharpen sigma1)
Adjust only sigma, radius 0 is adjusted automatically.
Compare ImageMagick option -adaptive-sharpen.
--finalthreshold [=ARG] Make low (or high) contrast area transparent.
ARG takes arguments:
t threshold1 Default: $(maskarg_defaultvalue finalthreshold threshold1)%
T threshold2 Default: $(maskarg_defaultvalue finalthreshold threshold2)%
s sigma Default: $(maskarg_defaultvalue finalthreshold sigma1)
threshold1 removes low contrast areas below a percent.
threshold2 removes high contrast areas above a percent.
sigma blurs the cut border.
Step 6: Background
--bg, --background [=ARG] Specify a background to paint on.
Useful for (semi-)transparent results or as a result
on its own.
ARG takes argument:
(word) background Default: $(maskarg_defaultvalue bg word1)
background can be one out of:
wave wavedark enfuse
transparent
magick -list color
magick -list evaluate (compare --evaluate)
magick -list compose (compare --compose)
Try: enfuse mean max min
colorize colorize2 pinlight pinlight2
interpolate overlay pegtoplight reflect
Can be specified multiple times for option --layered.
Much thanks to the developers and supporters of ImageMagick!
Without them this project would not have been possible.
A first start to get an idea of the stackshot image fusing workflow was
inspired by Alan Gibson's focStack.bat at https://im.snibgo.com/focstack.htm .
imfuse also contains code from Fred Weinhaus with his friendly permisson.
Version: imfuse v$Version
Author: Martin Viereck, Germany
License: GPLv2 https://www.gnu.org/licenses/old-licenses/gpl-2.0.html#SEC1
Website: https://github.com/mviereck/microscopy-tools
"
}
license() {
echo "
imfuse - Combines focus stackshot images to one overall sharp image.
Copyright (C) 2022 Martin Viereck
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
imfuse website and contact: https://github.com/mviereck/microscopy-tools
"
}
### Messages
error() {
echo "
imfuse$Subprocess ERROR: $*
" >&2
kill -s SIGINT "$Imfusepid"
}
note() {
echo "imfuse$Subprocess: $*" >&2
return 0
}
verbose() {
[ "$Verbose" = "yes" ] && echo "imfuse$Subprocess: $*" >&2
return 0
}
showimage() {
local Frame
[ "$Showimageprocessing" = "yes" ] && geeqie -t -r --File:"${1:-}" 2>/dev/null &
disown $!
[ "$Video" ] && {
# Videoframecount="$((Videoframecount+1))"
Videoframecount="$(ls "${Cachedir}"/videoframe????.* | sort -V | tail -n1)"
Videoframecount="$(basename "$Videoframecount")"
Videoframecount="${Videoframecount//[^0-9]/}"
Videoframecount="$(sed "s/^0*//" <<< "$Videoframecount")"
Videoframecount="$(calc "$Videoframecount+1")"
Frame="${Cachedir}/videoframe$(printnum "$Videoframecount").tif"
#ln -s "${1:-}" "$Frame"
cp "${1:-}" "$Frame" ### FIXME ln -s where possible
}
return 0
}
showviewnior() {
local W H
[ "$Viewnior" ] && {
#viewnior "$@" >/dev/null 2>&1 &
W=900
H=900
compare "$Imagewidth" -lt "$W" && W="$Imagewidth"
compare "$Imageheight" -lt "$H" && H="$Imageheight"
nohup feh --draw-tinted --zoom="max" --keep-zoom-vp \
--geometry="${W}x${H}+37+0" \
--title="$Sourcemd5: $Parsedoptions" \
--info="exiftool -imagedescription %F | cut -d: -f2- | fold -s -w $((W/7))" \
"$@" </dev/null >/dev/null 2>&1 &
disown $!
}
return 0
}
showresult() {
showimage "$Resultimage"
#command -v xclip >/dev/null && echo -n "$Resultimage" | xclip -i -selection clipboard
notify-send "imfuse is ready" 2>/dev/null ||:
case "$Extendedsave" in
yes)
showviewnior "$Resultimage" "$Resultmask" "$Resultdepthmap"
;;
no)
showviewnior "$Resultimage"
;;
esac
echo "$Resultimage"
}
traperror() {
error "traperror($Subprocess): Command at Line ${2:-} returned with error code ${1:-}:
${4:-}
${3:-} - ${5:-}"
}
### Misc
calc() {
# float calculation of $* with awk
# first awk calculates, second awk removes trailing zeros.
LC_ALL=C awk "BEGIN { OFMT=\"%f\"; print $**1 ; }" | awk '{ if ($0 ~ /\./){ sub("0*$","",$0); sub ("\\.$","",$0);} print }'
}
checkmagicklist() {
local Check
[ "${3:-}" = "print" ] && Check="" || Check="-q"
$Magickbin -list "${1:-}" | grep -w -i "^${2:-XXX}" | head -n1 | awk '{print $1}' | grep $Check -w -i "^${2:-XXX}"
}
compare() {
# compare floating number with < > =
local Arg1 Arg2 Operator
Arg1="${1:-}"
Operator="${2}"
Arg2="${3:-}"
case "$Operator" in
"<"|"lt"|"-lt") Operator="<" ;;
">"|"gt"|"-gt") Operator=">" ;;
"="|"eq"|"-eq"|"==") Operator="==" ;;
esac
[ -n "$(LC_ALL=C awk "BEGIN{if ($Arg1 $Operator $Arg2) print \"yes\"}")" ]
}
digitonly() {
#sed "s/[^0-9.]//g" <<< "${1:-}"
echo "${1//[^0-9.]/}"
}
digitrm() {
#sed "s/[0-9.]//g" <<< "${1:-}"
echo "${1//[0-9.-]/}"
}
generate_key() {
# generate a unique key value from current time and nanoseconds
date +%s.%N
}
lowercase() {
# Usage: lowercase "string"
printf '%s\n' "${1,,}"
}
md5cut() {
# print last 6 digits
grep -v "#" <<< "$*" | tr "\n" " " | md5sum | cut -c27-32
}
numberofpercent() {
Number="${1:-}"
Number="${Number//%}"
Number="$((Number*Sourceimagenumber/100))"
[ "$Number" -lt "1" ] && Number=1
[ "$Number" -gt "$Sourceimagenumber" ] && Number="$Sourceimagenumber"
echo "$Number"
}
percentrm() {
# remove % from string
#sed s/%//g <<< "${1:-}"
echo "${1//%/}"
}
printnum(){
# print number $1 with leading zeroes.
# $1 number
# $2 digits. Default: 4
[ "${1:-}" = "NUMBER" ] && echo "NUMBER" && return 0
printf "%0${2:-4}d" "${1:-0}"
}
printsameline() {
# print $1 without newline at begin of current line
echo -ne "${1:-}\033[0K\r" >&2
}
printtotalmemory() {
# print total memory including zram
local Memory Line Zram
Memory="$(LC_ALL=C free | grep "Mem:" | LC_ALL=C awk '{print $2}')"
while read Line; do
Zram="$(LC_ALL=C awk 'BEGIN {OFMT = "%.0f"} {print $3}' <<< "$Line")"
Zram="$(LC_ALL=C awk 'BEGIN {OFMT = "%.0f"} {print $1 / 1000}' <<< "$Zram")"
Memory="$((Memory + Zram))"
done < <(/sbin/swapon --bytes | grep zram ||:)
echo $Memory
}
unspecialstring() {
# Replace all characters except those described in tr string with a '-'.
printf %s "${1:-}" | LC_ALL=C tr -c "a-zA-Z0-9.,=-_" "-"
}
### Debugging helpers
forcemask() {
note "Forcing mask generation"
rm -f "${Cachedir}"/${Sourcemd5}.mask.*
}
forcelevel() {
note "Forcing leveling"
rm -f "${Cachedir}"/level*
}
forcemerge() {
note "Forcing merge"
rm -f "${Cachedir}"/${Sourcemd5}.mask.merge*
}
forcefocus() {
note "Forcing focus"
rm -f "${Cachedir}"/${Sourcemd5}.focus*
rm -f "${Cachedir}"/${Sourcemd5}.substack*
}
forcepost() {
note "Forcing focus postprocessing"
rm -f "${Cachedir}"/${Sourcemd5}.substack*.final.*
}
#### run commands on all CPUs
multicore() {
# Run multiple processes in parallel, but not more than $Multicore_maxprocesses
# $1 Command
# $2 Image to show if $1 is finished
# $3 Memory needed by the process
# Run multicore_wait afterwards to wait for the last processes to finish.
local Process Command
local Mem_needed Zram
[ "${1:-}" = "-t1" ] && {
shift
[ "$Multicore_processcount" -gt 0 ] && {
multicore_wait || return 1
}
}
[ "$Multicore_processcount" = "$Multicore_maxprocesses" ] && {
multicore_wait || return 1
}
[ "$Multicore_processcount" = "0" ] && {
Multicore_memorymax="$(printfreememory)"
Multicore_memorymax="$((Multicore_memorymax*8/10))"
}
Mem_needed=0
for Process in $(seq $Multicore_maxprocesses); do
Mem_needed="$(awk "BEGIN {print $Mem_needed + ${Multicore_memory[$Process]:-0} }" )"
done
Mem_needed="$((Mem_needed+${3:-0}))"
[ "$Mem_needed" -gt "$Multicore_memorymax" ] && {
note "multicore: Low memory. Waiting for $Multicore_processcount running processes to finish. Need: $(( ${3:-0}/1000 )) (overall $((Mem_needed/1000))) MB, Available: $((Multicore_memorymax/1000)) MB"
[ "$Mem_needed" -gt "$Multicore_memorymax" ] && [ "$Multicore_processcount" = "0" ] && note "multicore: Likely hard disk cache will be used and slow down the calculation."
multicore_wait || return 1
}
ifcmdbreak && return 1
Multicore_processcount=$((Multicore_processcount +1))
Command="$(cut -d ' ' -f1 <<< "${1:-}")"
case $(type -t "$Command") in
file) Command="nice ${1:-}" ;;
*) Command="${1:-}" ;;
esac
#verbose "multicore: ${1:-}"
eval "$Command &"
Multicore_process[Multicore_processcount]=$!
Multicore_image[Multicore_processcount]="${2:-}"
Multicore_memory[Multicore_processcount]="${3:-0}"
return 0
}
multicore_wait() {
local Process Error=
for Process in $(seq ${Multicore_maxprocesses:-0}); do
[ "${Multicore_process[$Process]}" ] && {
multicore_waitprocess "${Multicore_process[$Process]}" || {
multicore_break
Error=1
}
[ "$Error" ] && break
[ "${Multicore_image[$Process]}" ] && showimage "${Multicore_image[$Process]}"
}
Multicore_process[$Process]=""
Multicore_image[$Process]=""
Multicore_memory[$Process]="0"
done
[ "$Error" ] && return 1
Multicore_processcount=0
return 0
}
multicore_waitprocess() {
local Error=
while sleep 0.2 ; do
ps -p "${1:-}" >/dev/null || break
ifcmdbreak && Error=1
[ "$Error" ] && break
done
[ "$Error" ] && return 1
wait "${1:-}"
return $?
}
multicore_break() {
local Process
for Process in $(seq ${Multicore_maxprocesses:-0}); do
[ "${Multicore_process[$Process]}" ] && {
verbose "multicore_break: Sending SIGINT to $(ps -p ${Multicore_process[$Process]})"
kill "${Multicore_process[$Process]}"
wait "${Multicore_process[$Process]}"
Multicore_process[$Process]=""
Multicore_image[$Process]=""
Multicore_memory[$Process]="0"