Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@
"psr/log": "^1.0.1"
},
"require-dev": {
"phpunit/phpunit": "^6.0",
"phpunit/phpunit": "^6.3",
"phpdocumentor/phpdocumentor" : "^2.9",
"jakub-onderka/php-parallel-lint": "^0.9.2"
"jakub-onderka/php-parallel-lint": "^0.9.2",
"squizlabs/php_codesniffer": "3.*"
},
"autoload": {
"psr-4": {
Expand All @@ -44,7 +45,8 @@
"scripts": {
"test": [
"parallel-lint . --exclude vendor",
"phpunit"
"phpunit",
"phpcs -np --standard=PSR2 --extensions=php --ignore=/vendor/ ."
]
}
}
2 changes: 1 addition & 1 deletion examples/class.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

$image = Vips\Image::newFromFile($argv[1]);

echo "width = " . $image->width . "\n";
echo 'width = ' . $image->width . "\n";

$image = $image->invert();

Expand Down
192 changes: 119 additions & 73 deletions examples/sig.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,107 +2,153 @@
<?php

require __DIR__ . '/../vendor/autoload.php';
use Jcupitt\Vips;

# sigmoidal contrast adjustment in php-vips

# This is a standard contrast adjustment technique: grey values are put through
# an S-shaped curve which boosts the slope in the mid-tones and drops it for
# white and black.
use Jcupitt\Vips;

function sigmoid(float $alpha, float $beta, bool $ushort = false): Vips\Image
/**
* sigmoidal contrast adjustment in php-vips
*
* This is a standard contrast adjustment technique: grey values are put through
* an S-shaped curve which boosts the slope in the mid-tones and drops it for
* white and black.
*
* @param bool $sharpen If true increase the contrast, if false decrease the contrast.
* @param float $midpoint Midpoint of the contrast (typically 0.5).
* @param float $contrast Strength of the contrast (typically 3-20).
* @param bool $ushort Indicating if we have a 16-bit LUT.
*
* @return Vips\Image 16-bit or 8-bit LUT
*/
function sigmoid(bool $sharpen, float $midpoint, float $contrast, bool $ushort = false): Vips\Image
{
# make a identity LUT, that is, a lut where each pixel has the value of
# its index ... if you map an image through the identity, you get the
# same image back again
#
# LUTs in libvips are just images with either the width or height set
# to 1, and the 'interpretation' tag set to HISTOGRAM
#
# if 'ushort' is TRUE, we make a 16-bit LUT, ie. 0 - 65535 values;
# otherwise it's 8-bit (0 - 255)
/**
* Make a identity LUT, that is, a lut where each pixel has the value of
* its index ... if you map an image through the identity, you get the
* same image back again.
*
* LUTs in libvips are just images with either the width or height set
* to 1, and the 'interpretation' tag set to HISTOGRAM.
*
* If 'ushort' is TRUE, we make a 16-bit LUT, ie. 0 - 65535 values;
* otherwise it's 8-bit (0 - 255)
*/
$lut = Vips\Image::identity(['ushort' => $ushort]);

# rescale so each element is in [0, 1]
$max = $lut->max();
$lut = $lut->divide($max);

# the sigmoidal equation, see
#
# http://www.imagemagick.org/Usage/color_mods/#sigmoidal
#
# though that's missing a term -- it should be
#
# (1/(1+exp(β*(α-u))) - 1/(1+exp(β*α))) /
# (1/(1+exp(β*(α-1))) - 1/(1+exp(β*α)))
#
# ie. there should be an extra α in the second term
$x = 1.0 / (1.0 + exp($beta * $alpha));
$y = 1.0 / (1.0 + exp($beta * ($alpha - 1.0))) - $x;
$z = $lut->multiply(-1)->add($alpha)->multiply($beta)->exp()->add(1);
$result = $z->pow(-1)->subtract($x)->divide($y);

# rescale back to 0 - 255 or 0 - 65535
$result = $result->multiply($max);

# and get the format right ... $result will be a float image after all
# that maths, but we want uchar or ushort
$result = $result->cast($ushort ?
Vips\BandFormat::USHORT : Vips\BandFormat::UCHAR);

// Rescale so each element is in [0, 1]
$range = $lut->max();
$lut = $lut->divide($range);

/**
* The sigmoidal equation, see
*
* http://www.imagemagick.org/Usage/color_mods/#sigmoidal
*
* and
*
* http://osdir.com/ml/video.image-magick.devel/2005-04/msg00006.html
*
* Though that's missing a term -- it should be
*
* (1/(1+exp(β*(α-u))) - 1/(1+exp(β*α))) /
* (1/(1+exp(β*(α-1))) - 1/(1+exp(β*α)))
*
* ie. there should be an extra α in the second term
*/
if ($sharpen) {
$x = $lut->multiply(-1)->add($midpoint)->multiply($contrast)->exp()->add(1)->pow(-1);
$min = $x->min();
$max = $x->max();
$result = $x->subtract($min)->divide($max - $min);
} else {
$min = 1 / (1 + exp($contrast * $midpoint));
$max = 1 / (1 + exp($contrast * ($midpoint - 1)));
$x = $lut->multiply($max - $min)->add($min);
$result = $x->multiply(-1)->add(1)->divide($x)->log()->divide($contrast)->multiply(-1)->add($midpoint);
}

// Rescale back to 0 - 255 or 0 - 65535
$result = $result->multiply($range);

/**
* And get the format right ... $result will be a float image after all
* that maths, but we want uchar or ushort
*/
$result = $result->cast($ushort ? Vips\BandFormat::USHORT : Vips\BandFormat::UCHAR);
return $result;
}

# Apply to RGB. This takes no account of image gamma, and applies the
# contrast boost to R, G and B bands, thereby also boosting colourfulness.
function sigRGB(Vips\Image $image, float $alpha, float $beta): Vips\Image
/**
* Apply to RGB. This takes no account of image gamma, and applies the
* contrast boost to R, G and B bands, thereby also boosting colourfulness.
*
* @param Vips\Image $image The source image.
* @param bool $sharpen If true increase the contrast, if false decrease the contrast.
* @param float $midpoint Midpoint of the contrast (typically 0.5).
* @param float $contrast Strength of the contrast (typically 3-20).
*
* @return Vips\Image The manipulated image.
*/
function sigRGB(Vips\Image $image, bool $sharpen, float $midpoint, float $contrast): Vips\Image
{
$lut = sigmoid($alpha, $beta, $image->format == Vips\BandFormat::USHORT);

$lut = sigmoid($sharpen, $midpoint, $contrast, $image->format === Vips\BandFormat::USHORT);
return $image->maplut($lut);
}

# Fancier: apply to L of CIELAB. This will change luminance equally, and will
# not change colourfulness.
function sigLAB(Vips\Image $image, float $alpha, float $beta): Vips\Image
/**
* Fancier: apply to L of CIELAB. This will change luminance equally, and will
* not change colourfulness.
*
* @param Vips\Image $image The source image.
* @param bool $sharpen If true increase the contrast, if false decrease the contrast.
* @param float $midpoint Midpoint of the contrast (typically 0.5).
* @param float $contrast Strength of the contrast (typically 3-20).
*
* @return Vips\Image The manipulated image.
*/
function sigLAB(Vips\Image $image, bool $sharpen, float $midpoint, float $contrast): Vips\Image
{
$old_interpretation = $image->interpretation;
$oldInterpretation = $image->interpretation;

# labs is CIELAB with colour values expressed as short (signed 16-bit ints)
# L is in 0 - 32767
/**
* Labs is CIELAB with colour values expressed as short (signed 16-bit ints)
* L is in 0 - 32767
*/
$image = $image->colourspace(Vips\Interpretation::LABS);

# make a 16-bit LUT, then shrink by x2 to make it fit the range of L in labs
$lut = sigmoid($alpha, $beta, true);
// Make a 16-bit LUT, then shrink by x2 to make it fit the range of L in labs
$lut = sigmoid($sharpen, $midpoint, $contrast, true);
$lut = $lut->shrinkh(2)->multiply(0.5);
$lut = $lut->cast(Vips\BandFormat::SHORT);

# get the L band from our labs image, map though the LUT, then reattach the
# ab bands from the labs image
/**
* Get the L band from our labs image, map though the LUT, then reattach the
* ab bands from the labs image
*/
$L = $image->extract_band(0);
$AB = $image->extract_band(1, ['n' => 2]);
$L = $L->maplut($lut);
$image = $L->bandjoin($AB);

# and back to our original colourspace again (probably rgb)
#
# after the manipulation above, $image will just be tagged as a generic
# multiband image, vips will no longer know that it's a labs, so we need to
# tell colourspace what the source space is
$image = $image->colourspace(
$old_interpretation,
['source_space' => Vips\Interpretation::LABS]
);

return $image;
/**
* And back to our original colourspace again (probably rgb)
*
* After the manipulation above, $image will just be tagged as a generic
* multiband image, vips will no longer know that it's a labs, so we need to
* tell colourspace what the source space is
*/
return $image->colourspace($oldInterpretation, [
'source_space' => Vips\Interpretation::LABS
]);
}

$im = Vips\Image::newFromFile($argv[1], ['access' => Vips\Access::SEQUENTIAL]);

# $beta == 10 is a large contrast boost, values below about 4 drop the contrast
#
# sigLAB is the fancy one, and is much slower than sigRGB
$im = sigLAB($im, 0.5, 7);
/**
* $contrast == 10 is a large contrast boost, values below about 4 drop the contrast
*
* sigLAB is the fancy one, and is much slower than sigRGB
*/
$im = sigLAB($im, true, 0.5, 7);

$im->writeToFile($argv[2]);

Expand Down
12 changes: 10 additions & 2 deletions src/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -1663,7 +1663,11 @@ public function bandjoin($other, array $options = []): Image
{
/* Allow a single unarrayed value as well.
*/
$other = (array) $other;
if ($other instanceof Image) {
$other = [$other];
} else {
$other = (array) $other;
}

/* If $other is all numbers, we can use self::bandjoin_const().
*/
Expand Down Expand Up @@ -1727,7 +1731,11 @@ public function bandrank($other, array $options = []): Image

/* Allow a single unarrayed value as well.
*/
$other = (array) $other;
if ($other instanceof Image) {
$other = [$other];
} else {
$other = (array) $other;
}

return self::call('bandrank', $this, $other, $options);
}
Expand Down
8 changes: 5 additions & 3 deletions tests/call.php → tests/CallTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?php

namespace Jcupitt\Vips\Test;

use Jcupitt\Vips;
use PHPUnit\Framework\TestCase;

class VipsCallTest extends PHPUnit\Framework\TestCase
class CallTest extends TestCase
{
public function testVipsCall()
{
Expand All @@ -16,7 +19,7 @@ public function testVipsCall()

public function testVipsCallStatic()
{
$image = Vips\Image::black(1, 4, ["bands" => 3]);
$image = Vips\Image::black(1, 4, ['bands' => 3]);

$this->assertEquals($image->width, 1);
$this->assertEquals($image->height, 4);
Expand Down Expand Up @@ -45,7 +48,6 @@ public function testVipsDraw()
$this->assertEquals($image->getpoint(0, 0), [0]);
$this->assertEquals($image->getpoint(50, 50), [255]);
}

}

/*
Expand Down
6 changes: 4 additions & 2 deletions tests/Main.php → tests/ConfigTest.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?php

namespace Jcupitt\Vips\Test;

use Jcupitt\Vips;
use PHPUnit\Framework\TestCase;

class VipsConfigTest extends PHPUnit\Framework\TestCase
class ConfigTest extends TestCase
{
public function testVipsCacheSetMax()
{
Expand All @@ -25,7 +28,6 @@ public function testVipsConcurrencySet()
{
Vips\Config::concurrencySet(12);
}

}

/*
Expand Down
Loading