diff --git a/Ai/Ocr/Tesseract/TesseractOcr.php b/Ai/Ocr/Tesseract/TesseractOcr.php index 5b5c9f9ad..0b15e8cb1 100644 --- a/Ai/Ocr/Tesseract/TesseractOcr.php +++ b/Ai/Ocr/Tesseract/TesseractOcr.php @@ -15,6 +15,7 @@ declare(strict_types=1); namespace phpOMS\Ai\Ocr\Tesseract; use phpOMS\System\File\PathException; +use phpOMS\System\SystemUtils; /** * Tesseract api @@ -54,87 +55,6 @@ final class TesseractOcr self::$bin = \realpath($path); } - /** - * Run git command. - * - * @param string $cmd Command to run - * - * @return string[] - * - * @throws \Exception - * - * @since 1.0.0 - */ - private function run(string $cmd) : array - { - if (\strtolower((string) \substr(\PHP_OS, 0, 3)) === 'win') { - $cmd = 'cd ' . \escapeshellarg(\dirname(self::$bin)) - . ' && ' . \basename(self::$bin) - . ' ' - . $cmd; - } else { - $cmd = \escapeshellarg(self::$bin) - . ' ' - . $cmd; - } - - $pipes = []; - $desc = [ - 1 => ['pipe', 'w'], - 2 => ['pipe', 'w'], - ]; - - $resource = \proc_open($cmd, $desc, $pipes, null, null); - - if ($resource === false) { - throw new \Exception(); - } - - $stdout = \stream_get_contents($pipes[1]); - $stderr = \stream_get_contents($pipes[2]); - - foreach ($pipes as $pipe) { - \fclose($pipe); - } - - $status = \proc_close($resource); - - if ($status == -1) { - throw new \Exception((string) $stderr); - } - - return $this->parseLines(\trim($stdout === false ? '' : $stdout)); - } - - /** - * Parse lines. - * - * @param string $lines Result of git command - * - * @return string[] - * - * @since 1.0.0 - */ - private function parseLines(string $lines) : array - { - $lineArray = \preg_split('/\r\n|\n|\r/', $lines); - $lines = []; - - if ($lineArray === false) { - return $lines; - } - - foreach ($lineArray as $line) { - $temp = \preg_replace('/\s+/', ' ', \trim($line, ' ')); - - if (!empty($temp)) { - $lines[] = $temp; - } - } - - return $lines; - } - /** * Prase image * @@ -167,7 +87,8 @@ final class TesseractOcr */ public function parseImage(string $image, array $languages = ['eng'], int $psm = 3, int $oem = 3) : string { - $this->run( + SystemUtils::runProc( + self::$bin, $image . ' ' . ($temp = \tempnam(\sys_get_temp_dir(), 'ocr_')) . ' --psm ' . $psm diff --git a/Image/Skew.php b/Image/Skew.php index 7b9b33dd6..daec1fc31 100644 --- a/Image/Skew.php +++ b/Image/Skew.php @@ -65,7 +65,7 @@ final class Skew $avg /= $start[1] - $end[1]; - $dimImMatrix = [$end[0] - $start[0], $end[1] - $start[1]]; + $dimImMatrix = [\count($imMatrix), \count($imMatrix[0])]; $bestScore = 0; $bestDegree = 0; @@ -77,7 +77,7 @@ final class Skew $rotated = self::rotatePixelMatrix($imMatrix, $dimImMatrix, $i); $hist = []; - for ($j = 0; $j < $dimImMatrix[1]; ++$j) { + for ($j = 0; $j < $dimImMatrix[0]; ++$j) { $hist[$j] = \array_sum($rotated[$j]); // cleanup for score function @@ -95,8 +95,6 @@ final class Skew $im = \imagerotate($im, $bestDegree, 1); - // https://stackoverflow.com/questions/29981083/rotating-a-bit-matrix - if (\strripos($outPath, 'png') !== false) { \imagepng($im, $outPath); } elseif (\strripos($outPath, 'jpg') !== false || \strripos($outPath, 'jpeg') !== false) { @@ -118,13 +116,13 @@ final class Skew $rotated = [[]]; for ($i = 0; $i < $dim[0]; ++$i) { - $cY = $i - $dim[1] / 2.0; // center + $cY = $i - $dim[0] / 2.0; // center for ($j = 0; $j < $dim[1]; ++$j) { - $cX = $j - $dim[0] / 2.0; // center + $cX = $j - $dim[1] / 2.0; // center - $x = $cos * $cX + $sin * $cY + $dim[0] / 2.0; - $y = -$sin * $cX + $cos * $cY + $dim[1] / 2.0; + $x = $cos * $cX + $sin * $cY + $dim[1] / 2.0; + $y = -$sin * $cX + $cos * $cY + $dim[0] / 2.0; $rotated[$i][$j] = self::getNearestValue($pixel, $dim, $x, $y); } @@ -135,11 +133,11 @@ final class Skew private static function getNearestValue(array $pixel, array $dim, float $x, float $y) : int { - $xLow = \min((int) $x, $dim[0] - 1); - $xHigh = \min((int) \ceil($x), $dim[0] - 1); + $xLow = \min((int) $x, $dim[1] - 1); + $xHigh = \min((int) \ceil($x), $dim[1] - 1); - $yLow = \min((int) $y, $dim[1] - 1); - $yHigh = \min((int) \ceil($y), $dim[1] - 1); + $yLow = \min((int) $y, $dim[0] - 1); + $yHigh = \min((int) \ceil($y), $dim[0] - 1); $points = [ [$xLow, $yLow], diff --git a/Image/Thresholding.php b/Image/Thresholding.php index ce30c16e0..f54a63eeb 100644 --- a/Image/Thresholding.php +++ b/Image/Thresholding.php @@ -54,12 +54,8 @@ final class Thresholding $out = \imagecreate($dim[0], $dim[1]); $intImg = [[]]; - - $s = (int) $dim[0] / 96; // can be changed 8 - $t = 30; // can be changed 15 - for ($i = 0; $i < $dim[0]; ++$i) { - $sum = 0; + $sum = 0.0; for ($j = 0; $j < $dim[1]; ++$j) { $rgb = \imagecolorat($im, $i, $j); @@ -69,16 +65,19 @@ final class Thresholding } } + $s = (int) ($dim[0] / 96.0); // can be changed 8 + $t = 30; // can be changed 15 + $black = \imagecolorallocate($out, 0, 0, 0); $white = \imagecolorallocate($out, 255, 255, 255); for ($i = 0; $i < $dim[0]; ++$i) { for ($j = 0; $j < $dim[1]; ++$j) { - $x1 = \max(1, (int) ($i - $s / 2)); - $x2 = \min((int) ($i + $s / 2), $dim[0] - 1); + $x1 = \max(1, (int) ($i - $s / 2.0)); + $x2 = \min((int) ($i + $s / 2.0), $dim[0] - 1); - $y1 = \max(1, (int) ($j - $s / 2)); - $y2 = \min((int) ($j + $s / 2), $dim[1] - 1); + $y1 = \max(1, (int) ($j - $s / 2.0)); + $y2 = \min((int) ($j + $s / 2.0), $dim[1] - 1); $count = ($x2 - $x1) * ($y2 - $y1); $sum = $intImg[$x2][$y2] - $intImg[$x2][$y1 - 1] - $intImg[$x1 - 1][$y2] + $intImg[$x1 - 1][$y1 - 1]; @@ -86,7 +85,7 @@ final class Thresholding $rgb = \imagecolorat($im, $i, $j); $brightness = ImageUtils::lightness($rgb); - $color = $brightness * $count <= ($sum * (100 - $t) / 100) ? $black : $white; + $color = $brightness * $count <= ($sum * (100.0 - $t) / 100.0) ? $black : $white; \imagesetpixel($out, $i, $j, $color); } diff --git a/Message/Http/HttpRequest.php b/Message/Http/HttpRequest.php index 94187eb50..afe91cd85 100644 --- a/Message/Http/HttpRequest.php +++ b/Message/Http/HttpRequest.php @@ -76,12 +76,7 @@ final class HttpRequest extends RequestAbstract { $this->header = new HttpHeader(); $this->header->l11n = $l11n ?? new Localization(); - - if ($uri !== null) { - $this->uri = $uri; - } - - $this->init(); + $this->uri = $uri ?? new HttpUri(''); } /** @@ -93,13 +88,11 @@ final class HttpRequest extends RequestAbstract * * @since 1.0.0 */ - private function init() : void + public function initRequest() : void { - if (!isset($this->uri)) { - $this->initCurrentRequest(); - $this->lock(); - self::cleanupGlobals(); - } + $this->initCurrentRequest(); + $this->lock(); + self::cleanupGlobals(); $this->data = \array_change_key_case($this->data, \CASE_LOWER); } @@ -394,7 +387,10 @@ final class HttpRequest extends RequestAbstract */ public static function createFromSuperglobals() : self { - return new self(); + $request = new self(); + $request->initRequest(); + + return $request; } /** diff --git a/Message/RequestAbstract.php b/Message/RequestAbstract.php index 30e1d9256..d71b275df 100644 --- a/Message/RequestAbstract.php +++ b/Message/RequestAbstract.php @@ -289,4 +289,9 @@ abstract class RequestAbstract implements MessageInterface { return $this->files; } + + public function addFile(array $file) : void + { + $this->files[] = $file; + } } diff --git a/README.md b/README.md index fa230dd28..3528ad826 100755 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ With Karaka you have one partner who can provide all the tools and software solu - [Installation](#installation) - [Requirements](#requirements) - [Setup](#setup) + - [End-User](#end-user) + - [Developer](#developer) - [Philosophy](#philosophy) - [Development Status](#development-status) - [Features](#features) @@ -86,6 +88,7 @@ Features this framework provides are: * Utils (e.g. barcodes, comporession, unit converter, jobqueue, git, etc.) * Value validation * View management +* Image processing * Stdlib (e.g. graph, map, queue, enum, etc.) ## Unit Tests diff --git a/System/SystemUtils.php b/System/SystemUtils.php index a82f77a9e..be470ee6e 100644 --- a/System/SystemUtils.php +++ b/System/SystemUtils.php @@ -146,4 +146,62 @@ final class SystemUtils return 'localhost.localdomain'; } + + public static function runProc(string $executable, string $cmd) : array + { + if (\strtolower((string) \substr(\PHP_OS, 0, 3)) === 'win') { + $cmd = 'cd ' . \escapeshellarg(\dirname($executable)) + . ' && ' . \basename($executable) + . ' ' + . $cmd; + } else { + $cmd = \escapeshellarg($executable) + . ' ' + . $cmd; + } + + $pipes = []; + $desc = [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ]; + + $resource = \proc_open($cmd, $desc, $pipes, null, null); + + if ($resource === false) { + throw new \Exception(); + } + + $stdout = \stream_get_contents($pipes[1]); + $stderr = \stream_get_contents($pipes[2]); + + foreach ($pipes as $pipe) { + \fclose($pipe); + } + + $status = \proc_close($resource); + + if ($status == -1) { + throw new \Exception((string) $stderr); + } + + $lines = \trim($stdout === false ? '' : $stdout); + + $lineArray = \preg_split('/\r\n|\n|\r/', $lines); + $lines = []; + + if ($lineArray === false) { + return $lines; + } + + foreach ($lineArray as $line) { + $temp = \preg_replace('/\s+/', ' ', \trim($line, ' ')); + + if (!empty($temp)) { + $lines[] = $temp; + } + } + + return $lines; + } } diff --git a/Uri/UriFactory.php b/Uri/UriFactory.php index 3a668fad0..8f84bb256 100644 --- a/Uri/UriFactory.php +++ b/Uri/UriFactory.php @@ -198,11 +198,13 @@ final class UriFactory private static function unique(string $url) : string { // handle edge cases / normalization + /* $url = \str_replace( ['=%', '=#', '=?'], ['=%25', '=%23', '=%3F'], $url ); + */ if (\stripos($url, '?') === false && ($pos = \stripos($url, '&')) !== false) { $url = \substr_replace($url, '?', $pos, 1); diff --git a/Utils/ImageUtils.php b/Utils/ImageUtils.php index 57ac64910..32cecfe49 100644 --- a/Utils/ImageUtils.php +++ b/Utils/ImageUtils.php @@ -64,17 +64,17 @@ final class ImageUtils public static function lightnessFromRgb(int $r, int $g, int $b) : float { - $vR = $r / 255; - $vG = $g / 255; - $vB = $b / 255; + $vR = $r / 255.0; + $vG = $g / 255.0; + $vB = $b / 255.0; $lR = $vR <= 0.04045 ? $vR / 12.92 : \pow((($vR + 0.055) / 1.055), 2.4); $lG = $vG <= 0.04045 ? $vG / 12.92 : \pow((($vG + 0.055) / 1.055), 2.4); $lB = $vB <= 0.04045 ? $vB / 12.92 : \pow((($vB + 0.055) / 1.055), 2.4); $y = 0.2126 * $lR + 0.7152 * $lG + 0.0722 * $lB; - $lStar = $y <= 216 / 24389 ? $y * 24389 / 27 : \pow($y,(1 / 3)) * 116 - 16; + $lStar = $y <= 216.0 / 24389.0 ? $y * 24389.0 / 27.0 : \pow($y, (1 / 3)) * 116.0 - 16.0; - return $lStar / 100; + return $lStar / 100.0; } } diff --git a/Utils/TaskSchedule/Cron.php b/Utils/TaskSchedule/Cron.php index 3904e1ec9..b6b5e0fdb 100644 --- a/Utils/TaskSchedule/Cron.php +++ b/Utils/TaskSchedule/Cron.php @@ -30,10 +30,11 @@ class Cron extends SchedulerAbstract */ public function create(TaskAbstract $task) : void { - $this->run('-l > ' . __DIR__ . '/tmpcron.tmp'); - \file_put_contents(__DIR__ . '/tmpcron.tmp', $task->__toString() . "\n", \FILE_APPEND); - $this->run(__DIR__ . '/tmpcron.tmp'); - \unlink(__DIR__ . '/tmpcron.tmp'); + $path = \tempnam(\sys_get_temp_dir(), 'cron_'); + $this->run('-l > ' . $path); + \file_put_contents($path, $task->__toString() . "\n", \FILE_APPEND); + $this->run($path); + \unlink($path); } /** @@ -41,10 +42,11 @@ class Cron extends SchedulerAbstract */ public function update(TaskAbstract $task) : void { - $this->run('-l > ' . __DIR__ . '/tmpcron.tmp'); + $path = \tempnam(\sys_get_temp_dir(), 'cron_'); + $this->run('-l > ' . $path); $new = ''; - $fp = \fopen(__DIR__ . '/tmpcron.tmp', 'r+'); + $fp = \fopen($path, 'r+'); if ($fp) { $line = \fgets($fp); @@ -59,11 +61,11 @@ class Cron extends SchedulerAbstract } \fclose($fp); - \file_put_contents(__DIR__ . '/tmpcron.tmp', $new); + \file_put_contents($path, $new); } - $this->run(__DIR__ . '/tmpcron.tmp'); - \unlink(__DIR__ . '/tmpcron.tmp'); + $this->run($path); + \unlink($path); } /** @@ -79,10 +81,11 @@ class Cron extends SchedulerAbstract */ public function deleteByName(string $name) : void { - $this->run('-l > ' . __DIR__ . '/tmpcron.tmp'); + $path = \tempnam(\sys_get_temp_dir(), 'cron_'); + $this->run('-l > ' . $path); $new = ''; - $fp = \fopen(__DIR__ . '/tmpcron.tmp', 'r+'); + $fp = \fopen($path, 'r+'); if ($fp) { $line = \fgets($fp); @@ -97,11 +100,11 @@ class Cron extends SchedulerAbstract } \fclose($fp); - \file_put_contents(__DIR__ . '/tmpcron.tmp', $new); + \file_put_contents($path, $new); } - $this->run(__DIR__ . '/tmpcron.tmp'); - \unlink(__DIR__ . '/tmpcron.tmp'); + $this->run($path); + \unlink($path); } /** @@ -109,10 +112,11 @@ class Cron extends SchedulerAbstract */ public function getAll() : array { - $this->run('-l > ' . __DIR__ . '/tmpcron.tmp'); + $path = \tempnam(\sys_get_temp_dir(), 'cron_'); + $this->run('-l > ' . $path); $jobs = []; - $fp = \fopen(__DIR__ . '/tmpcron.tmp', 'r+'); + $fp = \fopen($path, 'r+'); if ($fp) { $line = \fgets($fp); @@ -136,8 +140,8 @@ class Cron extends SchedulerAbstract \fclose($fp); } - $this->run(__DIR__ . '/tmpcron.tmp'); - \unlink(__DIR__ . '/tmpcron.tmp'); + $this->run($path); + \unlink($path); return $jobs; } @@ -147,10 +151,11 @@ class Cron extends SchedulerAbstract */ public function getAllByName(string $name, bool $exact = true) : array { - $this->run('-l > ' . __DIR__ . '/tmpcron.tmp'); + $path = \tempnam(\sys_get_temp_dir(), 'cron_'); + $this->run('-l > ' . $path); $jobs = []; - $fp = \fopen(__DIR__ . '/tmpcron.tmp', 'r+'); + $fp = \fopen($path, 'r+'); if ($fp) { $line = \fgets($fp); @@ -168,8 +173,8 @@ class Cron extends SchedulerAbstract \fclose($fp); } - $this->run(__DIR__ . '/tmpcron.tmp'); - \unlink(__DIR__ . '/tmpcron.tmp'); + $this->run($path); + \unlink($path); return $jobs; } diff --git a/Views/View.php b/Views/View.php index d320de695..4808e1b10 100644 --- a/Views/View.php +++ b/Views/View.php @@ -355,4 +355,9 @@ class View extends ViewAbstract { return $this->l11nManager->getDateTime($this->l11n, $datetime, $format); } + + public function renderUserName(string $format, array $names) : string + { + return \trim(\preg_replace('/\s+/', ' ', \sprintf($format, ...$names)));; + } } diff --git a/tests/Image/SkewTest.php b/tests/Image/SkewTest.php index a651d2085..29c85f7f1 100644 --- a/tests/Image/SkewTest.php +++ b/tests/Image/SkewTest.php @@ -37,4 +37,4 @@ final class SkewTest extends \PHPUnit\Framework\TestCase [1700, 900] ); } -} \ No newline at end of file +} diff --git a/tests/Image/test_img1_integral_thresholding.png b/tests/Image/test_img1_integral_thresholding.png index 8b737025d..9884c39b5 100644 Binary files a/tests/Image/test_img1_integral_thresholding.png and b/tests/Image/test_img1_integral_thresholding.png differ diff --git a/tests/Image/test_img2_integral_thresholding.jpg b/tests/Image/test_img2_integral_thresholding.jpg index c7a4876b5..ae1fc787b 100644 Binary files a/tests/Image/test_img2_integral_thresholding.jpg and b/tests/Image/test_img2_integral_thresholding.jpg differ