Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
Kernel
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
182
0.00% covered (danger)
0.00%
0 / 1
 convolve
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
182
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Image
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Image;
16
17use phpOMS\Utils\NumericUtils;
18
19/**
20 * Kernel - image sharpening/blurring
21 *
22 * @package phpOMS\Image
23 * @license OMS License 2.0
24 * @link    https://jingga.app
25 * @since   1.0.0
26 */
27final class Kernel
28{
29    public const KERNEL_RIDGE_1 = [
30        [0, -1, 0],
31        [-1, 4, -1],
32        [0, -1, 0],
33    ];
34
35    public const KERNEL_RIDGE_2 = [
36        [-1, -1, -1],
37        [-1, 8, -1],
38        [-1, -1, -1],
39    ];
40
41    public const KERNEL_SHARPEN = [
42        [0, -1, 0],
43        [-1, 5, -1],
44        [0, -1, 0],
45    ];
46
47    public const KERNEL_BOX_BLUR = [
48        [1 / 9, 1 / 9, 1 / 9],
49        [1 / 9, 1 / 9, 1 / 9],
50        [1 / 9, 1 / 9, 1 / 9],
51    ];
52
53    public const KERNEL_GAUSSUAN_BLUR_3 = [
54        [1 / 16, 2 / 16, 1 / 16],
55        [2 / 16, 4 / 16, 2 / 16],
56        [1 / 16, 2 / 16, 1 / 16],
57    ];
58
59    public const KERNEL_EMBOSS = [
60        [-2, -1, 0],
61        [-1, 1, 1],
62        [0, 1, 2],
63    ];
64
65    public const KERNEL_UNSHARP_MASKING = [
66        [-1 / 256,  -4 / 256, -6 / 256, -4 / 256, -1 / 256],
67        [-4 / 256,  -16 / 256, -24 / 256, -16 / 256, -4 / 256],
68        [-6 / 256,  -24 / 256, 476 / 256, -24 / 256, -6 / 256],
69        [-4 / 256,  -16 / 256, -24 / 256, -16 / 256, -4 / 256],
70        [-1 / 256,  -4 / 256, -6 / 256, -4 / 256, -1 / 256],
71    ];
72
73    /**
74     * @see https://en.wikipedia.org/wiki/Kernel_(image_processing)
75     * @see https://towardsdatascience.com/image-processing-with-python-blurring-and-sharpening-for-beginners-3bcebec0583a
76     * @see https://web.eecs.umich.edu/~jjcorso/t/598F14/files/lecture_0924_filtering.pdf
77     */
78    public static function convolve(string $inPath, string $outPath, array $kernel) : void
79    {
80        $im = null;
81        if (\strripos($inPath, 'png') !== false) {
82            $im = \imagecreatefrompng($inPath);
83        } elseif (\strripos($inPath, 'jpg') !== false || \strripos($inPath, 'jpeg') !== false) {
84            $im = \imagecreatefromjpeg($inPath);
85        } else {
86            $im = \imagecreatefromgif($inPath);
87        }
88
89        if ($im === false) {
90            return;
91        }
92
93        if (\count($kernel) === 3) {
94            \imageconvolution($im, $kernel, 1, 0);
95        } else {
96            $dim  = [\imagesx($im), \imagesy($im)];
97            $kDim = [\count($kernel[1]), \count($kernel)];
98
99            $kWidthRadius  = NumericUtils::uRightShift($kDim[0], 1);
100            $kHeightRadius = NumericUtils::uRightShift($kDim[1], 1);
101
102            for ($y = 0; $y < $dim[1]; ++$y) {
103                for ($x = 0; $x < $dim[0]; ++$x) {
104                    $newR = 0;
105                    $newG = 0;
106                    $newB = 0;
107
108                    for ($ky = 0; $ky < $kDim[0]; ++$ky) {
109                        for ($kx = 0; $kx < $kDim[1]; ++$kx) {
110                            $pixel =  \imagecolorat($im,
111                                \min(\max($x + $kx - $kWidthRadius, 0), $dim[0] - 1),
112                                \min(\max($y + $ky - $kHeightRadius, 0), $dim[1] - 1)
113                            );
114
115                            // old
116                            $r = ($pixel >> 16) & 0xFF;
117                            $g = ($pixel >> 8) & 0xFF;
118                            $b = $pixel & 0xFF;
119
120                            // new
121                            $newR += $r * $kernel[$ky][$kx];
122                            $newG += $g * $kernel[$ky][$kx];
123                            $newB += $b * $kernel[$ky][$kx];
124                        }
125                    }
126
127                    $newR = (int) \max(0, \min(255, $newR));
128                    $newG = (int) \max(0, \min(255, $newG));
129                    $newB = (int) \max(0, \min(255, $newB));
130
131                    \imagesetpixel($im, $x, $y, (int) (($newR << 16) | ($newG << 8) | $newB));
132                }
133            }
134        }
135
136        if (\strripos($outPath, 'png') !== false) {
137            \imagepng($im, $outPath);
138        } elseif (\strripos($outPath, 'jpg') !== false || \strripos($outPath, 'jpeg') !== false) {
139            \imagejpeg($im, $outPath);
140        } else {
141            \imagegif($im, $outPath);
142        }
143
144        \imagedestroy($im);
145    }
146}