source: spip-zone/_plugins_/filtres_images_vectorise/trunk/lib/geometrize/src/Core.php @ 116199

Last change on this file since 116199 was 116199, checked in by cedric@…, 21 months ago

Un plugin pour vectoriser en SVG des images bitmap, qui propose 4 nouveaux filtres

  • extraire_palette_couleurs permet d'extraire une palette de couleur d'une image (par defaut les 3 couleurs les plus representees) en utilisant un calcul des couleurs dominantes par partitionnement en k-moyennes

`
<BOUCLE_palette(POUR){tableau #FICHIER|extraire_palette_couleurs{3}}>
<div style="display: inline-block;width: 30px;height: 15px;background-color: #VALEUR;"></div>
</BOUCLE_palette>
`

  • image_geometrize permet de creer une image SVG approchante de l'image d'origine en utilisant le lib GeometrizePHP https://github.com/Cerdic/geometrize-php/ (attention methode gourmande en temps de calcul)
  • image_potrace permet de generer un trace SVG depuis l'image d'origine, a l'aide de Potracio PHP qui est un portage PHP de PotRace? https://seenthis.net/messages/645575
  • image_geopotrize combine les 2 techniques : un background geometrize qui n'a pas besoin d'un grand nombre de shapes et un trace potrace superpose/mixe

Les 3 filtres ont tout un tas d'option pour qui veut jouer avec, mais les reglages par defaut permettent d'avoir immediatement un joli resultat exploitable
A noter que pour image_geometrize, le temps de calcul peut depasser les 30s pour generer le nombre de shapes demandees, selon les reglages utilises.
Dans ce cas le calcul est arrete au bout de 20s, on stocke et on renvoie l'image provisoire incomplete, et on stocke l'etat du calcul qui reprendra au prochain calcul de la page.

(J'evite les screenshots dans le message de commit mais le coeur y est)

File size: 7.3 KB
Line 
1<?php
2
3namespace Cerdic\Geometrize;
4
5use \Cerdic\Geometrize\Bitmap;
6use \Cerdic\Geometrize\State;
7use \Cerdic\Geometrize\Rasterizer\Rasterizer;
8use \Cerdic\Geometrize\Shape\ShapeFactory;
9
10class Core {
11
12        /**
13         * @param \Cerdic\Geometrize\Bitmap $target
14         * @param \Cerdic\Geometrize\Bitmap $current
15         * @param array $lines
16         * @param int $alpha
17         * @return int
18         * @throws \Exception
19         */
20        static function computeColor($target, $current, $lines, $alpha){
21                if (!($target!==null)){
22                        throw new \Exception("FAIL: target != null");
23                }
24                if (!($lines!==null)){
25                        throw new \Exception("FAIL: lines != null");
26                }
27                if (!($alpha>=0)){
28                        throw new \Exception("FAIL: alpha >= 0");
29                }
30                $totalRed = 0;
31                $totalGreen = 0;
32                $totalBlue = 0;
33                $count = 0;
34
35                if ($alpha < 255) {
36                        if (!($current!==null)){
37                                throw new \Exception("FAIL: current != null");
38                        }
39
40                        $f = 256 * 255 /$alpha;
41                        $a = intval($f);
42
43                        foreach($lines as $line){
44                                $y = $line['y'];
45                                for ($x=$line['x1']; $x<=$line['x2']; $x++) {
46                                        $t = $target->data[$y][$x];
47                                        $c = $current->data[$y][$x];
48                                        $totalRed += (($t >> 24 & 255)-($c >> 24 & 255))*$a+($c >> 24 & 255)*256;
49                                        $totalGreen += (($t >> 16 & 255)-($c >> 16 & 255))*$a+($c >> 16 & 255)*256;
50                                        $totalBlue += (($t >> 8 & 255)-($c >> 8 & 255))*$a+($c >> 8 & 255)*256;
51                                        $count++;
52                                }
53                        }
54                        $totalRed = $totalRed >> 8;
55                        $totalGreen = $totalGreen >> 8;
56                        $totalBlue = $totalBlue >> 8;
57                }
58                else {
59                        foreach($lines as $line){
60                                $y = $line['y'];
61                                for ($x=$line['x1']; $x<=$line['x2']; $x++) {
62                                        $t = $target->data[$y][$x];
63                                        $totalRed += ($t >> 24 & 255);
64                                        $totalGreen += ($t >> 16 & 255);
65                                        $totalBlue += ($t >> 8 & 255);
66                                        $count++;
67                                }
68                        }
69                }
70
71                if ($count===0){
72                        return 0;
73                }
74
75                $r = intval(round($totalRed/$count));
76                $r = min(255, $r);
77
78                $g = intval(round($totalGreen/$count));
79                $g = min(255, $g);
80
81                $b = intval(round($totalBlue/$count));
82                $b = min(255, $b);
83
84                return ($r << 24) + ($g << 16) + ($b << 8) + $alpha;
85        }
86
87        /**
88         * @param \Cerdic\Geometrize\Bitmap $target
89         * @param \Cerdic\Geometrize\Bitmap $current
90         * @return int
91         * @throws \Exception
92         */
93        static function differenceFull($target, $current){
94
95                $current->errorCache = [];
96                $total = 0;
97                $width = $target->width;
98                $height = $target->height;
99                for ($y = 0; $y<$height; $y++){
100                        for ($x = 0; $x<$width; $x++){
101                                $t = &$target->data[$y][$x];
102                                $c = &$current->data[$y][$x];
103                                $e = 0;
104                                foreach ([24,16,8,0] as $k){
105                                        $dk = ($t>>$k & 255)-($c>>$k & 255);
106                                        if ($dk<0){
107                                                $dk *= -1;
108                                        }
109                                        $e += $dk;
110                                }
111                                $total += ($current->errorCache[$y][$x] = $e);
112                        }
113                }
114                return $total;
115        }
116
117        /**
118         * @param \Cerdic\Geometrize\Bitmap $target
119         * @param \Cerdic\Geometrize\Bitmap $before
120         * @param \Cerdic\Geometrize\Bitmap $after
121         * @param int $score
122         * @param array $lines
123         * @param null|int $bestScore
124         * @return int
125         */
126        static function differencePartial($target, $before, $after, $score, $lines, $bestScore = null){
127
128                $total = $score;
129                foreach ($lines as &$line) {
130                        $y = $line['y'];
131                        $_xe = $line['x2']+1;
132                        for ($x = $line['x1']; $x<$_xe; $x++){
133                                if (!isset($before->errorCache[$y][$x])){
134                                        $e = 0;
135                                        $t = &$target->data[$y][$x];
136                                        $b = &$before->data[$y][$x];
137                                        foreach ([24,16,8,0] as $k){
138                                                $dk = ($t>>$k & 255)-($b>>$k & 255);
139                                                if ($dk<0){
140                                                        $dk *= -1;
141                                                }
142                                                $e += $dk;
143                                        }
144                                        $before->errorCache[$y][$x] = $e;
145                                }
146                                $total -= $before->errorCache[$y][$x];
147                        }
148                }
149                if (!is_null($bestScore) && $total>$bestScore){
150                        return $total;
151                }
152
153                foreach ($lines as &$line) {
154                        $y = $line['y'];
155                        $_xe = $line['x2']+1;
156                        for ($x = $line['x1']; $x<$_xe; $x++){
157                                $t = &$target->data[$y][$x];
158                                $a = &$after->data[$y][$x];
159                                foreach ([24,16,8,0] as $k){
160                                        $dk = ($t>>$k & 255)-($a>>$k & 255);
161                                        if ($dk<0){
162                                                $dk *= -1;
163                                        }
164                                        $total += $dk;
165                                }
166                        }
167                        if (!is_null($bestScore) && $total>$bestScore){
168                                return $total;
169                        }
170                }
171
172                return $total;
173        }
174
175        /**
176         * @param array $shapes
177         * @param int $alpha
178         * @param int $nRandom
179         * @param \Cerdic\Geometrize\Bitmap $target
180         * @param \Cerdic\Geometrize\Bitmap $current
181         * @param \Cerdic\Geometrize\Bitmap $buffer
182         * @param int $lastScore
183         * @return \Cerdic\Geometrize\State
184         * @throws \Exception
185         */
186        static function bestRandomState($shapes, $shapeSizeFactor, $alpha, $nRandom, $target, $current, $buffer, $lastScore){
187                $bestEnergy = null;
188                $bestState = null;
189
190                $nRandom = max($nRandom, 1);
191
192                for ($i = 0; $i<$nRandom; $i++){
193                        $state = new State(ShapeFactory::randomShapeOf($shapes, $current->width, $current->height, $shapeSizeFactor), $alpha, $target, $current, $buffer);
194                        $energy = $state->energy($lastScore, $bestEnergy);
195                        if (is_null($bestEnergy) || $energy<$bestEnergy){
196                                $bestEnergy = $energy;
197                                $bestState = $state;
198                        }
199                }
200
201                return $bestState;
202        }
203
204        /**
205         * @param array $shapes
206         * @param float $shapeSizeFactor
207         * @param int $alpha
208         * @param int $nRandom
209         * @param int $maxMutationAge
210         * @param \Cerdic\Geometrize\Bitmap $target
211         * @param \Cerdic\Geometrize\Bitmap $current
212         * @param \Cerdic\Geometrize\Bitmap $buffer
213         * @param int $lastScore
214         * @return \Cerdic\Geometrize\State
215         * @throws \Exception
216         */
217        static function bestHillClimbState($shapes, $shapeSizeFactor, $alpha, $nRandom, $maxMutationAge, $target, $current, $buffer, $lastScore){
218                $state = Core::bestRandomState($shapes, $shapeSizeFactor, $alpha, $nRandom, $target, $current, $buffer, $lastScore);
219                $state = Core::hillClimb($state, $maxMutationAge, $lastScore);
220                return $state;
221        }
222
223        /**
224         * @param \Cerdic\Geometrize\State $state
225         * @param int $maxAge
226         * @param int $lastScore
227         * @return \Cerdic\Geometrize\State
228         * @throws \Exception
229         */
230        static function hillClimb($state, $maxAge, $lastScore){
231                if (!($state!==null)){
232                        throw new \Exception("FAIL: state != null");
233                }
234                if (!($maxAge>=0)){
235                        throw new \Exception("FAIL: maxAge >= 0");
236                }
237
238                $bestEnergy = $state->energy($lastScore);
239                $bestState = clone $state;
240
241                $age = 0;
242                while ($age++<$maxAge){
243                        $state1 = clone $bestState;
244                        $state1->mutate();
245                        $energy = $state1->energy($lastScore, $bestEnergy);
246
247                        if ($energy<$bestEnergy){
248                                $bestEnergy = $energy;
249                                $bestState = $state1;
250                                $age = 0;
251                        }
252                }
253
254                return $bestState;
255        }
256
257        /**
258         * @param \Cerdic\Geometrize\Shape\Shape $shape
259         * @param int $alpha
260         * @param \Cerdic\Geometrize\Bitmap $target
261         * @param \Cerdic\Geometrize\Bitmap $current
262         * @param \Cerdic\Geometrize\Bitmap $buffer
263         * @param int $score
264         * @param null|int $bestScore
265         * @return int
266         * @throws \Exception
267         */
268        static function energy(&$shape, $alpha, $target, $current, $buffer, $score, $bestScore = null){
269                if (!($shape!==null)){
270                        throw new \Exception("FAIL: shape != null");
271                }
272                if (!($target!==null)){
273                        throw new \Exception("FAIL: target != null");
274                }
275                if (!($current!==null)){
276                        throw new \Exception("FAIL: current != null");
277                }
278                if (!($buffer!==null)){
279                        throw new \Exception("FAIL: buffer != null");
280                }
281                $lines = $shape->rasterize();
282                if (!isset($shape->color)){
283                        $shape->color = Core::computeColor($target, $current, $lines, $alpha);
284                }
285                // copyLines only if opacity!=1 (speed issue with no transparency in shapes)
286                if ($shape->color & 255!==255){
287                        Rasterizer::copyLines($buffer, $current, $lines);
288                }
289                Rasterizer::drawLines($buffer, $shape->color, $lines);
290                return Core::differencePartial($target, $current, $buffer, $score, $lines, $bestScore);
291        }
292
293}
Note: See TracBrowser for help on using the repository browser.