-
-
Save marcan/d537c81efc6d0ff57d54273edd6765fd to your computer and use it in GitHub Desktop.
#!/bin/sh | |
# PNG Gamma trick (by @marcan42 / [email protected]) | |
# | |
# This script implements an improved version of the gamma trick used to make | |
# thumbnail images on reddit/4chan look different from the full-size image. | |
# | |
# Sample output (SFW; images by @Miluda): | |
# https://mrcn.st/t/homura_gamma_trick.png | |
# https://www.reddit.com/r/test/comments/6edthw/ (click for fullsize) | |
# https://twitter.com/marcan42/status/869855956842143744 | |
# | |
# Some backstory, explanation and example (slightly NSFW): | |
# https://www.reddit.com/r/touhou/comments/6e6lga/a/di83t02/ | |
# | |
# No idea who came up with the concept; this is an old trick, but past | |
# implementations I've seen were less correct than this one :-) | |
# | |
# This trick works on at least Reddit, 4chan and similar imageboards, Google | |
# Drive, Slack, and probably many others. It does not work on Twitter, as | |
# Twitter always preprocesses PNG images and strips the gAMA chunk. It does, | |
# however, work with e.g. imgur embed previews on Twitter. | |
# | |
# *Different* one-liner trick that works on Twitter (for grayscale images): | |
# https://twitter.com/marcan42/status/869858577116086272 | |
# | |
# License: public domain | |
high="$1" # High image (full-size original view) | |
low="$2" # Low image (thumbnail) (should be the same size) | |
output="output.png" | |
if [ ! -z "$3" ]; then | |
output="$3" # Output image | |
fi | |
size=$(convert "$high" -format "%wx%h" info:) | |
# Give a slight brightness boost to the high source, then apply the gamma. | |
# This ensures that the pixels look mostly white. | |
convert "$high" -alpha off +level 3.5%,100% -gamma 20 high_gamma.png | |
# Since the low image will be washed out, use gamma to darken it a bit, then | |
# reduce its brightness to ensure that its pixels become black after PNG gamma. | |
# 77% brightness gets crushed down to 0x00 or 0x01, enough for our purposes. | |
low_gamma="-alpha off -gamma 0.8 +level 0%,77%" | |
# To get rid of the slight "halo" of the high image, we're going to cancel it | |
# out from the low image. The equation that we need is: | |
# output = ¾low + ¼ (what we want, for high = white) | |
# output = ¾output_low + ¼high (what we get) | |
# Solve for output_low: | |
# ¾output_low + ¼high = ¾low + ¼ | |
# ¾output_low = ¾low + ¼ - ¼high | |
# output_low = low + ⅓ - ⅓high | |
# This assumes "dumb" resizing (not gamma-correct). For gamma-correct resizing, | |
# or for viewing at 1:1 (which is equivalent to gamma-correct resizing, because | |
# physics, assuming your monitor isn't mangling things), this operation would | |
# have to be done in a linear colorspace. In practice, the vast majority of | |
# resizing implementations are not gamma-correct, so this works. | |
convert \( "$low" $low_gamma \) high_gamma.png \ | |
-compose Mathematics -define compose:args='0,-0.33,1,0.33' \ | |
-composite low_adjusted.png | |
# Now compose both images together using the mask, then set the gamma metadata. | |
# Note that the typical display gamma is 2.2 and image gamma is the reciprocal | |
# 1/2.2. Since we're adding a gamma of 20, we need 1/2.2/20 = 0.022727. | |
# We also force the PNG encoder to include the gAMA chunk (and no other | |
# spurious metadata). | |
convert low_adjusted.png high_gamma.png -size $size pattern:gray25 -composite \ | |
-set gamma 0.022727 -define png:include-chunk=none,gAMA "$output" |
Could not get this to work with ImageMagick 7.1.1 (after converting convert commands to magick), the full-screen image as displayed by the browser looked mostly like the thumbnail image and the gAMA chunk didn't seem to be getting added to the header. Using identify -verbose output.png did not show any png:gAMA chunk, and the file gamma was listed as 0.454545. I downgraded to ImageMagick 6.9.13-21 (and also grabbed the 8-bit version instead of the 16-bit version, not sure which fixed the issue) and now it's closer to working. - the gamma is now correctly listed as 0.02273 in identify, and the png:gAMA header is present (with a matching gamma).
When used in Slack, the original full-screen image is never seen - the thumbnail generated when attaching images, the in-chat embed and full screen, and even the "View in Browser" all show the thumbnail image (mixed with the nearly-white full screen image). You can very briefly see the full-screen image as a thumbnail (when it's uploading the attachment), but once uploaded it seems to lose the gAMA header and the trick no longer works.
When used in Discord, I get mixed results. On Desktop, I see the "thumbnail" image both inline in chat, "fullscreen" in chat, and only once I click "Open in Browser" to show it in Chrome do I see the original full-screen image, instead of a version of the thumbnail. The same image when viewed on Discord mobile app (Android) shows the full-screen image (with a moire pattern) all the time, and never the thumbnail version.
Here is a version of the bash file converted to windows .bat (via ChatGPT with some fixes);
@echo off
rem PNG Gamma trick (by @marcan42 / [email protected])
rem This script implements an improved version of the gamma trick used to make
rem thumbnail images on reddit/4chan look different from the full-size image.
rem Replaced "convert" with "magick.exe" for Image Magick 7+.
rem High image (full-size original view)
set high=%1
rem Low image (thumbnail) (should be the same size)
set low=%2
rem Output image
set output=output.png
if not "%3"=="" set output=%3
rem Get the image size (Width x Height)
for /f "delims=" %%a in ('magick identify -format "%%wx%%h" "%high%"') do set size=%%a
rem Give a slight brightness boost to the high source, then apply the gamma.
rem This ensures that the pixels look mostly white.
magick "%high%" -alpha off +level 3.5%,100% -gamma 20 high_gamma.png
rem Since the low image will be washed out, use gamma to darken it a bit, then
rem reduce its brightness to ensure that its pixels become black after PNG gamma.
rem 77% brightness gets crushed down to 0x00 or 0x01, enough for our purposes.
set low_gamma=-alpha off -gamma 0.8 +level 0%%,77%%
rem To get rid of the slight "halo" of the high image, we're going to cancel it
rem out from the low image. The equation that we need is:
rem output = ¾low + ¼ (what we want, for high = white)
rem output = ¾output_low + ¼high (what we get)
rem Solve for output_low:
rem ¾output_low + ¼high = ¾low + ¼
rem ¾output_low = ¾low + ¼ - ¼high
rem output_low = low + ⅓ - ⅓high
rem This assumes "dumb" resizing (not gamma-correct). For gamma-correct resizing,
rem or for viewing at 1:1 (which is equivalent to gamma-correct resizing, because
rem physics, assuming your monitor isn't mangling things), this operation would
rem have to be done in a linear colorspace. In practice, the vast majority of
rem resizing implementations are not gamma-correct, so this works.
magick "%low%" %low_gamma% high_gamma.png ^
-compose Mathematics -define compose:args="0,-0.33,1,0.33" ^
-composite low_adjusted.png
rem Now compose both images together using the mask, then set the gamma metadata.
rem Note that the typical display gamma is 2.2 and image gamma is the reciprocal
rem 1/2.2. Since we're adding a gamma of 20, we need 1/2.2/20 = 0.022727.
rem We also force the PNG encoder to include the gAMA chunk (and no other
rem spurious metadata).
magick low_adjusted.png high_gamma.png -size %size% pattern:gray25 -composite ^
-set gamma 0.022727 -define png:include-chunk=none,gAMA "%output%"
echo Done! Output saved as %output%.
pause
Usage is still the same - thumbnail_encode.bat <full_image.png> <thumb_image.png> <output.png>
Having any transparency on the thumbnail image seems to produce very incorrect thumbnails. Both images should be the same dimensions.
is there a way i can do it in a regular image editing program without this tool?
Replace
convert
withmagick
everywhere in the script and it should work