Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update mktrans to pass shellcheck and reformat it using shfmt #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 23 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ Note: You will need to have [**imagemagick**](https://imagemagick.org/index.php)
Usage: mktrans [-f <fuzz>] [-s|-S] [-v] <files ... >

-f <fuzz>: How loosely to match the background color (default 20%)
-s: Use speedy antialiasing (much faster, slightly less acurate)
-S: Do not antialias transparency (useful for repeated runs)
-p <x>,<y>: Floodfills from pixel at x,y instead of 0,0
-v: Verbose
-s: Use speedy antialiasing (much faster, slightly less acurate)
-S: Do not antialias transparency (useful for repeated runs)
-p <x>,<y>: Floodfills from pixel at x,y instead of 0,0
-v: Verbose

Output filenames will be the same as input, except suffixed with
`-transparent.png`. E.g., `mktrans foo.gif bar.jpg` creates
`foo-transparent.png` and `bar-transparent.png`.

### About -f *fuzz*
### About -f _fuzz_

*Fuzz* is how far off the background color can be (in percent). You
_Fuzz_ is how far off the background color can be (in percent). You
usually won't have to change this. If fuzz is too high, parts of the
foreground image will be missing. If fuzz is too low, parts of the
background will not be removed. On certain images it may help to tweak
Expand All @@ -79,9 +79,9 @@ eroding. Since this can be slow on large images, consider using the
Similar to -s, but does not antialias at all. Useful with with -p, below.
See an example of why you'd use -S in [astrolabe.md](astrolabe.md).

### About -p *x*,*y*
### About -p _x_,_y_

*X*,*y* specifies which pixel to start floodfilling from, instead of 0,0.
_X_,_y_ specifies which pixel to start floodfilling from, instead of 0,0.
You probably should just _ignore_ this option and use The GIMP for
more fiddly, complex images.

Expand All @@ -101,7 +101,7 @@ a first floodfill. Note the letters 'a' and 'g' in the example below.
mv foo-transparent.png foo.png
mktrans foo.png
mv foo-transparent.png logo-transparent.png

[![Using -p to fill lagoons](https://i.imgur.com/Hxl1a1A.png)](https://i.imgur.com/CmbUnHk.png)

You can find the correct coordinates for a pixel by using
Expand All @@ -112,45 +112,43 @@ the start points of the image.

## Bugs

* The -p option is ugly and probably nobody wants to use it. Maybe I
- The -p option is ugly and probably nobody wants to use it. Maybe I
should remove it just to make the documentation shorter and clearer.
Also, nearly every single bug listed here is due to -p existing.

* Running this script on an image that already has transparency will
- Running this script on an image that already has transparency will
erode the image due to the antialiasing. Using -S is a workaround,
but is not very satisfactory. Perhaps this script should remove any
existing transparency before manipulating the image and then add it
back in at the end. But then again, how often are people going to
want to do that? The only use I can think of is when using -p.

* Because of the previous bug, if you do use -p to fill lots of
- Because of the previous bug, if you do use -p to fill lots of
lagoons, you'll probably want to use -S at the same time.
(Should this be done automatically if the image already has
transparency? Yuck, more complexity.)

See the [astrolabe](astrolabe.md) example where running mktrans a
second time on a specific pixel requires the use of -S so as to
not cause the image to be eroded.
mktrans a.png
mktrans -p 258,54 a-transparent.png
mv a-transparent-transparent.png a-transparent-transparent' without -S).png
mktrans -S -p 258,54 a-transparent.png
mv a-transparent-transparent.png a-transparent-transparent' with -S).png

* Finding the coordinates for -p is a pain. (Tip: middle click on

mktrans a.png
mktrans -p 258,54 a-transparent.png
mv a-transparent-transparent.png a-transparent-transparent' without -S).png
mktrans -S -p 258,54 a-transparent.png
mv a-transparent-transparent.png a-transparent-transparent' with -S).png

- Finding the coordinates for -p is a pain. (Tip: middle click on
image when viewing with `display`). It'd be nice if there was a -P
option which let the user click on a point (or multiple points) in
the image to start the floodfill.

* Instead of choosing coordinates, we could use ImageMagick's
- Instead of choosing coordinates, we could use ImageMagick's
connected components labeling (blob extraction) to give people a
choice of which lagoons to fill.

choice of which lagoons to fill.

## See Also

This is similar to ImageMagick's
["bg_removal"](https://www.imagemagick.org/Usage/scripts/bg_removal)
script, but much higher quality. (It's also faster and simpler to use.)

script, but much higher quality. (It's also faster and simpler to use.)
Binary file removed a-transparent-transparent (with -S).png
Binary file not shown.
Binary file removed a-transparent-transparent (without -S).png
Binary file not shown.
Binary file removed a-transparent.png
Binary file not shown.
Binary file removed a.png
Binary file not shown.
15 changes: 7 additions & 8 deletions astrolabe.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ option to specify which pixel to start floodfilling at.

## Lagoon problem

Here's an example image which has already been run through `mktrans` once. As you can see, it left a black lagoon in the middle of the loop on this astrolabe.
Here's an example image which has already been run through `mktrans` once. As you can see, it left a black lagoon in the middle of the loop on this astrolabe.

<img src="README.md.d/a-transparent.png" align="center" width="50%" alt="After first run of mktrans">

Expand All @@ -25,34 +25,33 @@ If we did the most obvious thing and just ran
mktrans -p 256,64 a-transparent.png
```

then the output file (a-transparent-transparent.png) would lose some pixels due to rerunning the antialiasing step on the old transparency.
then the output file (a-transparent-transparent.png) would lose some pixels due to rerunning the antialiasing step on the old transparency.

<img src="README.md.d/a-transparent-transparent (without -S).png" align="center" width="50%" alt="Second run, but without -S">

## Kludge: Using -S on subsequent runs

Running `mktrans -S` supresses antialiasing completely.
Running `mktrans -S` supresses antialiasing completely.

```bash
mktrans -S -p 256,64 a-transparent.png
```

As you can see this looks much better:

<img src="README.md.d/a-transparent-transparent (with -S).png" align="center" width="50%" alt="Second run, with -S">

## But wait... isn't there a better way?

Yes. There are at least two ways this could be better, but there are no plans to implement them.
Yes. There are at least two ways this could be better, but there are no plans to implement them.

1. mktrans could automatically use `-S` when it detects a file already contains transparency.

2. More importantly, `-S` isn't even the right solution. The proper thing to do is to extract and remove the alpha channel from the original image, run mktrans, and
then composite the original alpha channel with the new one.
2. More importantly, `-S` isn't even the right solution. The proper thing to do is to extract and remove the alpha channel from the original image, run mktrans, and then composite the original alpha channel with the new one.

But, both of those improvements are only necessary if people are going to be running `mktrans` iteratively and the author (hackerb9) is not convinced that
people want to do that. The only use case is when people are using the `-p` option, which is already a pain in the butt to use as it requires firing up a graphical
program, like ImageMagick's `display`, to find the coordinates of lagoons. Wouldn't people just want to use a program like the [GNU Image Manipulation Program](https://gimp.org/)
instead of using mktrans?
instead of using mktrans?

If you think these or other improvements should be made, please file a bug with an explanation of your reasoning.

152 changes: 79 additions & 73 deletions mktrans
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
#!/bin/bash
#!/bin/bash
# B9 June 2017

# mktrans
# mktrans
# This is similar to ImageMagick's bg_removal script, but much higher
# quality. (It's also faster and simpler to use.)
# quality. (It's also faster and simpler to use.)
#
# For a sample, run these commands:
# convert logo: logo.png
# mktrans logo.png
# display logo-transparent.png


# Fuzz is how far off the background color can be (in percent).
# This is important for getting good antialiasing.
defaultfuzz=20
Expand All @@ -19,136 +18,143 @@ usage() {
cat <<EOF

mktrans: Convert images into shaped transparent pngs by floodfilling
the background with transparency (antialiased alpha channel).
Unless a different starting pixel is specified, the top left
pixel is used as the "background" color to remove and
floodfill starts from all four image edges,
the background with transparency (antialiased alpha channel).
Unless a different starting pixel is specified, the top left
pixel is used as the "background" color to remove and
floodfill starts from all four image edges,

Typical usage:
Typical usage:

mktrans foo.jpg (creates foo-transparent.png)
mktrans foo.jpg (creates foo-transparent.png)

Usage: mktrans [-f <fuzz>] [-s|-S] [-p <x>,<y>] [-v] <files ... >
Usage: mktrans [-f <fuzz>] [-s|-S] [-p <x>,<y>] [-v] <files ... >

-f <fuzz>: How loosely to match the background color (default $defaultfuzz%)
-f <fuzz>: How loosely to match the background color (default ${defaultfuzz}%)

Advanced options:
-s: Use speedy antialiasing (much faster, slightly less acurate)
-S: Supress antialiasing completely. (Useful for repeated runs)
p <x>,<y>: Floodfill from pixel at x,y instead of 0,0
-v: Verbose
Advanced options:
-s: Use speedy antialiasing (much faster, slightly less acurate)
-S: Supress antialiasing completely. (Useful for repeated runs)
-p <x>,<y>: Floodfill from pixel at x,y instead of 0,0
-v: Verbose
EOF

# * Side note: This creates an antialiased (blurred) alpha channel
# that is also eroded by half a pixel to avoid halos. ImageMagick's
# morphological operations don't (yet?) work at the subpixel level,
# so I'm blowing up the alpha channel to 200% before eroding. Since
# this can be slow on large images, consider using the '-s' option
# which uses a faster, lower quality antialiasing.
# * Side note: This creates an antialiased (blurred) alpha channel
# that is also eroded by half a pixel to avoid halos. ImageMagick's
# morphological operations don't (yet?) work at the subpixel level,
# so I'm blowing up the alpha channel to 200% before eroding. Since
# this can be slow on large images, consider using the '-s' option
# which uses a faster, lower quality antialiasing.

# * Running this script on an image that already has transparency will
# erode the image due to the antialiasing. Using -S is a workaround,
# but is not very satisfactory. Perhaps this script should remove any
# existing transparency before manipulating the image and then add it
# back in at the end. But then again, how often are people going to
# want to do that? The only use I can think of is when using -p.
# * Running this script on an image that already has transparency will
# erode the image due to the antialiasing. Using -S is a workaround,
# but is not very satisfactory. Perhaps this script should remove any
# existing transparency before manipulating the image and then add it
# back in at the end. But then again, how often are people going to
# want to do that? The only use I can think of is when using -p.

# * Because of the previous bug, if you do use -p to fill lots of
# lagoons, you'll probably want to use -A at the same time.
# * Because of the previous bug, if you do use -p to fill lots of
# lagoons, you'll probably want to use -A at the same time.

# * Finding the coordinates for -p is a pain. It'd be nice if there was
# a -P option which let the user click on a point (or multiple points)
# in the image to start the floodfill.
# * Finding the coordinates for -p is a pain. It'd be nice if there was
# a -P option which let the user click on a point (or multiple points)
# in the image to start the floodfill.

exit 0
}

# Work around MS Windows having a program called "convert".
if [[ "$COMSPEC" ]]; then
shopt -s expand_aliases # Allow aliases to workaround quirks.
shopt -s expand_aliases # Allow aliases to workaround quirks.
alias convert="magick convert" # Shun MS Windows' "convert" command.
fi


fuzz=$defaultfuzz
pixelcomma="0,0"
pixelplus="+0+0"

while getopts f:sAShp:v name; do
case $name in
f) fuzz=$OPTARG
;;
s) sflag=True
;;
S|A) noantialias=True
;;
v) vflag=True
;;
h) usage
;;
p) pixelcomma=$OPTARG
pixelplus=+${OPTARG%,*}+${OPTARG#*,}
pflag=True
;;
*) usage
;;
f)
fuzz=$OPTARG
;;
s)
sflag=True
;;
S | A)
noantialias=True
;;
v)
vflag=True
;;
h)
usage
;;
p)
pixelcomma=$OPTARG
pixelplus=+${OPTARG%,*}+${OPTARG#*,}
pflag=True
;;
*)
usage
;;
esac
done

shift $((OPTIND-1))
shift $((OPTIND - 1))
[[ "$#" != 0 ]] || usage


for filename; do
# Get color of 0,0 (top left) pixel
color=$(convert "$filename" -format "%[pixel:p{$pixelcomma}]" info:-)
if [[ "$color" == *rgba*",0)" ]]; then
color="${color%,0)},1)" # Floodfill only works with opaque colors.
color="${color%,0)},1)" # Floodfill only works with opaque colors.
fi
if [[ "$color" == "none" ]]; then
echo "Error: $filename: pixel at $pixelcomma is completely transparent. Cannot floodfill." >&2
continue
echo "Error: $filename: pixel at $pixelcomma is completely transparent. Cannot floodfill." >&2
continue
fi

options=""
if [ -z "$pflag" ]; then
# Add a 1 pixel border so we'll fill from the bottom and sides as well.
options+=" -bordercolor $color -border 1 "
# Add a 1 pixel border so we'll fill from the bottom and sides as well.
options+=" -bordercolor $color -border 1 "
fi
# In a new stack, make a copy of the image
options+=" ( +clone "
# [copy] floodfill with transparency ("none") starting at top-left
options+=" -fuzz $fuzz% -fill none -floodfill $pixelplus $color"
options+=" -fuzz $fuzz% -fill none -floodfill $pixelplus $color"
# [copy] extract just the transparency (alpha channel)
options+=" -alpha extract"
options+=" -alpha extract"

if [ -z "$noantialias" ]; then
if [ -z "$sflag" ]; then
if [ -z "$sflag" ]; then
# [copy] blow up the alpha channel so we can do sub-pixel morphology
options+=" -geometry 200%"
options+=" -geometry 200%"
# [copy] blur the alpha channel to make it antialiased
options+=" -blur 0x0.5"
options+=" -blur 0x0.5"
# [copy] shrink the region that is opaque by half a pixel.
options+=" -morphology erode square:1"
options+=" -morphology erode square:1"
# [copy] scale the alpha channel back to normal size.
options+=" -geometry 50%"
else # sflag: speedy antialias
options+=" -geometry 50%"
else # sflag: speedy antialias
# [copy] blur the alpha channel to make it antialiased
options+=" -blur 0x1"
options+=" -blur 0x1"
# [copy] only antialias inside the figure (<50% opacity becomes 0%)
options+=" -level 50%,100%"
fi
options+=" -level 50%,100%"
fi
fi
# [copy] end the stack.
options+=" ) "
# Compose the original image and the copy's alpha channel.
options+=" -compose CopyOpacity -composite"
if [ -z "$pflag" ]; then
# Remove the 1 pixel border we added
options+=" -shave 1"
# Remove the 1 pixel border we added
options+=" -shave 1"
fi


# shellcheck disable=SC2086
[ "$vflag" ] && echo convert "$filename" $options "${filename%.*}-transparent.png"

# shellcheck disable=SC2086
convert "$filename" $options "${filename%.*}-transparent.png"
done