:octocat: allow contrast adjustment and grayscaling in the reader

This commit is contained in:
codemasher
2021-12-07 01:05:03 +01:00
parent e5b1fbaf7c
commit 6c34213aca
9 changed files with 108 additions and 54 deletions
+20 -10
View File
@@ -13,9 +13,10 @@
namespace chillerlan\QRCode\Decoder;
use chillerlan\Settings\SettingsContainerInterface;
use function file_get_contents, get_resource_type, imagecolorat, imagecolorsforindex,
imagecreatefromstring, imagesx, imagesy, is_resource;
use const PHP_MAJOR_VERSION;
imagecreatefromstring, imagefilter, imagesx, imagesy, is_resource;
use const IMG_FILTER_BRIGHTNESS, IMG_FILTER_CONTRAST, IMG_FILTER_GRAYSCALE, PHP_MAJOR_VERSION;
/**
* This class is used to help decode images from files which arrive as GD Resource
@@ -35,7 +36,7 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
*
* @throws \chillerlan\QRCode\Decoder\QRCodeDecoderException
*/
public function __construct($gdImage){
public function __construct($gdImage, SettingsContainerInterface $options = null){
/** @noinspection PhpFullyQualifiedNameUsageInspection */
if(
@@ -45,7 +46,7 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
throw new QRCodeDecoderException('Invalid GD image source.');
}
parent::__construct(imagesx($gdImage), imagesy($gdImage));
parent::__construct(imagesx($gdImage), imagesy($gdImage), $options);
$this->gdImage = $gdImage;
@@ -56,6 +57,16 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
*
*/
private function setLuminancePixels():void{
if($this->options->readerGrayscale){
imagefilter($this->gdImage, IMG_FILTER_GRAYSCALE);
}
if($this->options->readerIncreaseContrast){
imagefilter($this->gdImage, IMG_FILTER_BRIGHTNESS, -100);
imagefilter($this->gdImage, IMG_FILTER_CONTRAST, -100);
}
for($j = 0; $j < $this->height; $j++){
for($i = 0; $i < $this->width; $i++){
$argb = imagecolorat($this->gdImage, $i, $j);
@@ -64,18 +75,17 @@ final class GDLuminanceSource extends LuminanceSourceAbstract{
$this->setLuminancePixel($pixel['red'], $pixel['green'], $pixel['blue']);
}
}
}
/** @inheritDoc */
public static function fromFile(string $path):self{
$path = self::checkFile($path);
return new self(imagecreatefromstring(file_get_contents($path)));
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring(file_get_contents(self::checkFile($path))), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob):self{
return new self(imagecreatefromstring($blob));
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
return new self(imagecreatefromstring($blob), $options);
}
}
+20 -12
View File
@@ -13,6 +13,7 @@
namespace chillerlan\QRCode\Decoder;
use chillerlan\Settings\SettingsContainerInterface;
use Imagick;
use function count;
@@ -31,8 +32,8 @@ final class IMagickLuminanceSource extends LuminanceSourceAbstract{
*
* @throws \InvalidArgumentException
*/
public function __construct(Imagick $imagick){
parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight());
public function __construct(Imagick $imagick, SettingsContainerInterface $options = null){
parent::__construct($imagick->getImageWidth(), $imagick->getImageHeight(), $options);
$this->imagick = $imagick;
@@ -43,29 +44,36 @@ final class IMagickLuminanceSource extends LuminanceSourceAbstract{
*
*/
private function setLuminancePixels():void{
$this->imagick->setImageColorspace(Imagick::COLORSPACE_GRAY);
if($this->options->readerGrayscale){
$this->imagick->setImageColorspace(Imagick::COLORSPACE_GRAY);
}
if($this->options->readerIncreaseContrast){
for($i = 0; $i < 10; $i++){
$this->imagick->contrastImage(0);
}
}
$pixels = $this->imagick->exportImagePixels(1, 1, $this->width, $this->height, 'RGB', Imagick::PIXEL_CHAR);
$count = count($pixels);
$countPixels = count($pixels);
for($i = 0; $i < $countPixels; $i += 3){
for($i = 0; $i < $count; $i += 3){
$this->setLuminancePixel($pixels[$i] & 0xff, $pixels[$i + 1] & 0xff, $pixels[$i + 2] & 0xff);
}
}
/** @inheritDoc */
public static function fromFile(string $path):self{
$path = self::checkFile($path);
return new self(new Imagick($path));
public static function fromFile(string $path, SettingsContainerInterface $options = null):self{
return new self(new Imagick(self::checkFile($path)), $options);
}
/** @inheritDoc */
public static function fromBlob(string $blob):self{
public static function fromBlob(string $blob, SettingsContainerInterface $options = null):self{
$im = new Imagick;
$im->readImageBlob($blob);
return new self($im);
return new self($im, $options);
}
}
+9 -5
View File
@@ -11,6 +11,8 @@
namespace chillerlan\QRCode\Decoder;
use chillerlan\QRCode\QROptions;
use chillerlan\Settings\SettingsContainerInterface;
use function array_slice, array_splice, file_exists, is_file, is_readable, realpath;
/**
@@ -24,6 +26,8 @@ use function array_slice, array_splice, file_exists, is_file, is_readable, realp
*/
abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
/** @var \chillerlan\QRCode\QROptions|\chillerlan\Settings\SettingsContainerInterface */
protected SettingsContainerInterface $options;
protected array $luminances;
protected int $width;
protected int $height;
@@ -31,11 +35,11 @@ abstract class LuminanceSourceAbstract implements LuminanceSourceInterface{
/**
*
*/
public function __construct(int $width, int $height){
$this->width = $width;
$this->height = $height;
// In order to measure pure decoding speed, we convert the entire image to a greyscale array
// up front, which is the same as the Y channel of the YUVLuminanceSource in the real app.
public function __construct(int $width, int $height, SettingsContainerInterface $options = null){
$this->width = $width;
$this->height = $height;
$this->options = $options ?? new QROptions;
$this->luminances = [];
}
+7 -1
View File
@@ -48,8 +48,14 @@ interface LuminanceSourceInterface{
*/
public function getRow(int $y):array;
/**
* Creates a LuminanceSource instance from the given file
*/
public static function fromFile(string $path):self;
/**
* Creates a LuminanceSource instance from the given data blob
*/
public static function fromBlob(string $blob):self;
}