|
| 1 | +#!/usr/bin/env php |
| 2 | +<?php |
| 3 | + |
| 4 | +require __DIR__ . '/../vendor/autoload.php'; |
| 5 | +use Jcupitt\Vips; |
| 6 | + |
| 7 | +# sigmoidal contrast adjustment in php-vips |
| 8 | + |
| 9 | +# This is a standard contrast adjustment technique: grey values are put through |
| 10 | +# an S-shaped curve which boosts the slope in the mid-tones and drops it for |
| 11 | +# white and black. |
| 12 | + |
| 13 | +function sigmoid(float $alpha, float $beta, bool $ushort = FALSE): Vips\Image |
| 14 | +{ |
| 15 | + # make a identity LUT, that is, a lut where each pixel has the value of |
| 16 | + # its index ... if you map an image through the identity, you get the |
| 17 | + # same image back again |
| 18 | + # |
| 19 | + # LUTs in libvips are just images with either the width or height set |
| 20 | + # to 1, and the "interpretation" tag set to HISTOGRAM |
| 21 | + # |
| 22 | + # if "ushort" is TRUE, we make a 16-bit LUT, ie. 0 - 65535 values; |
| 23 | + # otherwise it's 8-bit (0 - 255) |
| 24 | + $lut = Vips\Image::identity(["ushort" => $ushort]); |
| 25 | + |
| 26 | + # rescale so each element is in [0, 1] |
| 27 | + $max = $lut->max(); |
| 28 | + $lut = $lut->divide($max); |
| 29 | + |
| 30 | + # the sigmoidal equation, see |
| 31 | + # http://www.imagemagick.org/Usage/color_mods/#sigmoidal |
| 32 | + $x = $lut->multiply(-1)->add($alpha)->multiply($beta)->exp(); |
| 33 | + $y = exp($beta + 1); |
| 34 | + $t1 = $x->divide($y)->add(1); |
| 35 | + $t2 = $x->add(1)->pow(-1)->subtract(1 / $y); |
| 36 | + $result = $t1->multiply($t2); |
| 37 | + |
| 38 | + # rescale back to 0 - 255 or 0 - 65535 |
| 39 | + $result = $result->multiply($max); |
| 40 | + |
| 41 | + # and get the format right ... $result will be a float image after all |
| 42 | + # that maths, but we want uchar or ushort |
| 43 | + $result = $result->cast($ushort ? |
| 44 | + Vips\BandFormat::USHORT : Vips\BandFormat::UCHAR); |
| 45 | + |
| 46 | + return $result; |
| 47 | +} |
| 48 | + |
| 49 | +# Apply to RGB. This takes no account of image gamma, and applies the |
| 50 | +# contrast boost to R, G and B bands, thereby also boosting colourfulness. |
| 51 | +function sigRGB(Vips\Image $image, float $alpha, float $beta): Vips\Image |
| 52 | +{ |
| 53 | + $lut = sigmoid($alpha, $beta, $image->format == Vips\BandFormat::USHORT); |
| 54 | + |
| 55 | + return $image->maplut($lut); |
| 56 | +} |
| 57 | + |
| 58 | +# Fancier: apply to L of CIELAB. This will change luminance equally, and will |
| 59 | +# not change colourfulness. |
| 60 | +function sigLAB(Vips\Image $image, float $alpha, float $beta): Vips\Image |
| 61 | +{ |
| 62 | + $old_interpretation = $image->interpretation; |
| 63 | + |
| 64 | + # labs is CIELAB with colour values expressed as short (signed 16-bit ints) |
| 65 | + # L is in 0 - 32767 |
| 66 | + $image = $image->colourspace(Vips\Interpretation::LABS); |
| 67 | + |
| 68 | + # make a 16-bit LUT, then shrink by x2 to make it fit the range of L in labs |
| 69 | + $lut = sigmoid($alpha, $beta, TRUE); |
| 70 | + $lut = $lut->shrinkh(2)->multiply(0.5); |
| 71 | + $lut = $lut->cast(Vips\BandFormat::SHORT); |
| 72 | + |
| 73 | + # get the L band from our labs image, map though the LUT, then reattach the |
| 74 | + # ab bands from the labs image |
| 75 | + $L = $image->extract_band(0); |
| 76 | + $AB = $image->extract_band(1, ["n" => 2]); |
| 77 | + $L = $L->maplut($lut); |
| 78 | + $image = $L->bandjoin($AB); |
| 79 | + |
| 80 | + # and back to our original colourspace again (probably rgb) |
| 81 | + # |
| 82 | + # after the manipulation above, $image will just be tagged as a generic |
| 83 | + # multiband image, vips will no longer know that it's a labs, so we need to |
| 84 | + # tell colourspace what the source space is |
| 85 | + $image = $image->colourspace($old_interpretation, |
| 86 | + ["source_space" => Vips\Interpretation::LABS]); |
| 87 | + |
| 88 | + return $image; |
| 89 | +} |
| 90 | + |
| 91 | +$im = Vips\Image::newFromFile($argv[1], ["access" => Vips\Access::SEQUENTIAL]); |
| 92 | + |
| 93 | +# $beta == 10 is a large contrast boost, values below about 4 drop the contrast |
| 94 | +# |
| 95 | +# sigLAB is the fancy one, and is much slower than sigRGB |
| 96 | +$im = sigLAB($im, 0.5, 10); |
| 97 | + |
| 98 | +$im->writeToFile($argv[2]); |
| 99 | + |
| 100 | +/* |
| 101 | + * Local variables: |
| 102 | + * tab-width: 4 |
| 103 | + * c-basic-offset: 4 |
| 104 | + * End: |
| 105 | + * vim600: expandtab sw=4 ts=4 fdm=marker |
| 106 | + * vim<600: expandtab sw=4 ts=4 |
| 107 | + */ |
0 commit comments