I have a huge set of black and white images with spots and want to replace every spot into pixel (or small circle) that is located in the geometric center of the spot.
Because the spots has different sizes, I could not consequently use dilate operator because it completely deletes small sports. Is there any way to do this automatical with Imagemagick?
Kudos (and upvotes) to Fred (@fmw42) for the original technique. As much for my own curiosity as anything (and to document the approach), I wanted to make a variant that is hopefully more CPU-friendly, I/O friendly and maybe more portable. This should also have advantages given that OP has large numbers of images to process.
I worked on the following aspects:
rather than repeatedly load the image, draw a circle and re-save for every circle, I wanted to use a script that loads it once, draws all the circles and saves it
reduce dependency on other tools, and their process creation times - so all the cut
, grep
, tr
, convert
, echo
and so on are encapsulated inside a single, awk
invocation leveraging its built-in ability to split fields, process text, and do math. Hopefully this makes it easier to port to Windows too as fewer binaries are needed.
So, it looks like this:
#!/bin/bash
magick black_spots.png \
-threshold 50% -type bilevel \
-define connected-components:mean-color=true \
-define connected-components:area-threshold=0-300 \
-define connected-components:verbose=true \
-connected-components 8 null: | awk -F'[ x+]*' '
BEGIN { print "black_spots.png -fill white -colorize 100 -fill black" }
/gray\(0\)/ {
w=$3; h=$4; x=$5; y=$6; cx=x+w/2; cy=y+h/2
printf("-draw \"translate %f,%f circle 0,0 0,5\"\n", cx, cy)
}
END { print "-write result.png"}
' | magick -script -
The awk
part in the middle actually generates a script that looks like this:
black_spots.png -fill white -colorize 100 -fill black
-draw "translate 1429.000000,368.000000 circle 0,0 0,5"
-draw "translate 6.000000,1026.500000 circle 0,0 0,5"
-draw "translate 739.500000,378.000000 circle 0,0 0,5"
...
...
-write result.png
That is then piped into magick -script
at the end. Hopefully it is clear that the input file is only read once, all the circles are drawn, then the output file is written - just once.
Some notes on the awk
parts:
-F'[ x+]*'
means that multiple spaces, the letter x
and +
signs should all be treated as field separators
BEGIN
and END
blocks are executed once at the start and end of the awk
script
the /gray\(0\)/
block is executed only on lines containing gray(0)
As regards processing large numbers of files, I would use GNU Parallel, but you have not indicated your operating system. Basically, modify the above script to accept a filename as a parameter, save it as ProcessOne
, make it executable with chmod +x ProcessOne
, then run:
parallel ./ProcessOne ::: *.png
and it will keep all your CPU cores busy processing all your files in parallel till they are all done. You can get progress bars and ETAs with various switches:
parallel --eta ... # show ETA
parallel --progress ... # report progress
parallel --bar ... # add progress bar
parallel -j 4 ... # just run 4 jobs in parallel