Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #/bin/perl
- # Conversion video en images TO7 avec contraintes:
- # 2 couleurs max par bloc de 8 pixels horizontaux.
- #
- # Samuel DEVULDER, Avril-Mai 2016.
- #
- # parametres
- ($W, $H) = (320, 200); # HR
- $fps = 10;
- $dither = "tst2";
- $dither = "vac8";
- ($RED,$GRN,$BLU) = (2,4,1);
- @dither = (
- [35, 57, 19, 55, 7, 51, 4, 21],
- [29, 6, 41, 27, 37, 17, 59, 45],
- [61, 15, 53, 12, 62, 25, 33, 9],
- [23, 39, 31, 49, 2, 47, 13, 43],
- [ 3, 52, 8, 22, 36, 58, 20, 56],
- [38, 18, 60, 46, 30, 5, 42, 28],
- [63, 26, 34, 11, 64, 16, 54, 10],
- [14, 48, 1, 44, 24, 40, 32, 50]
- );
- # essayer 17*15 -> 256 vals par composantes
- @dither = (
- [ 224 , 159 , 49 , 175 , 18 , 227 , 97 , 127 , 187 , 23 , 102 , 169 , 12 , 137 , 31 ],
- [ 89 , 2 , 235 , 69 , 131 , 201 , 28 , 168 , 79 , 229 , 130 , 212 , 86 , 239 , 122 ],
- [ 217 , 118 , 200 , 100 , 186 , 56 , 114 , 234 , 11 , 157 , 62 , 30 , 196 , 50 , 178 ],
- [ 64 , 149 , 34 , 162 , 14 , 253 , 147 , 66 , 193 , 96 , 255 , 144 , 104 , 163 , 21 ],
- [ 191 , 93 , 241 , 60 , 195 , 107 , 33 , 211 , 136 , 45 , 185 , 3 , 220 , 75 , 247 ],
- [ 135 , 4 , 205 , 139 , 87 , 221 , 158 , 101 , 24 , 223 , 113 , 59 , 121 , 152 , 46 ],
- [ 81 , 173 , 119 , 29 , 176 , 1 , 70 , 242 , 182 , 76 , 143 , 243 , 180 , 22 , 198 ],
- [ 245 , 55 , 216 , 74 , 251 , 125 , 203 , 40 , 129 , 13 , 199 , 36 , 90 , 226 , 111 ],
- [ 15 , 148 , 106 , 188 , 47 , 94 , 165 , 78 , 172 , 228 , 103 , 167 , 138 , 52 , 160 ],
- [ 218 , 44 , 238 , 8 , 153 , 232 , 16 , 207 , 109 , 32 , 68 , 240 , 6 , 210 , 71 ],
- [ 181 , 128 , 85 , 174 , 115 , 67 , 142 , 48 , 237 , 154 , 192 , 123 , 91 , 170 , 112 ],
- [ 58 , 230 , 27 , 206 , 39 , 183 , 222 , 82 , 134 , 10 , 57 , 213 , 38 , 246 , 20 ],
- [ 197 , 151 , 105 , 141 , 254 , 99 , 19 , 202 , 98 , 249 , 179 , 140 , 84 , 156 , 120 ],
- [ 77 , 41 , 236 , 7 , 72 , 133 , 171 , 42 , 161 , 65 , 108 , 17 , 231 , 53 , 219 ],
- [ 146 , 194 , 92 , 164 , 214 , 51 , 233 , 110 , 215 , 26 , 225 , 189 , 126 , 177 , 5 ],
- [ 117 , 25 , 209 , 37 , 116 , 190 , 9 , 145 , 88 , 166 , 124 , 73 , 35 , 95 , 244 ],
- [ 184 , 80 , 132 , 248 , 83 , 155 , 63 , 252 , 43 , 208 , 54 , 250 , 150 , 204 , 61 ]
- ) if 0;
- @dither = (
- [ 12 , 14 , 7 ],
- [ 9 , 2 , 4 ],
- [ 6 , 11 , 15 ],
- [ 13 , 8 , 3 ],
- [ 1 , 5 , 10 ]
- ) if 0; # ouais
- @dither = (
- [ 1,7 ,11],
- [12, 5,8 ],
- [3 ,13, 2],
- [14, 6, 9],
- [10,15, 4],
- ) if 0;
- @dither = (
- [1,4,7],
- [5,8,2],
- [9,3,6]
- ) if 1;
- @dither = (
- [ 1, 8, 4],
- [ 7, 6, 3],
- [ 5, 2, 9]
- ) if 0; #cluster 3x3
- @dither = (
- [ 1 , 193 , 49 , 241 , 13 , 205 , 61 , 253 , 4 , 196 , 52 , 244 , 16 , 208 , 64 , 256 ],
- [ 129 , 65 , 177 , 113 , 141 , 77 , 189 , 125 , 132 , 68 , 180 , 116 , 144 , 80 , 192 , 128 ],
- [ 33 , 225 , 17 , 209 , 45 , 237 , 29 , 221 , 36 , 228 , 20 , 212 , 48 , 240 , 32 , 224 ],
- [ 161 , 97 , 145 , 81 , 173 , 109 , 157 , 93 , 164 , 100 , 148 , 84 , 176 , 112 , 160 , 96 ],
- [ 9 , 201 , 57 , 249 , 5 , 197 , 53 , 245 , 12 , 204 , 60 , 252 , 8 , 200 , 56 , 248 ],
- [ 137 , 73 , 185 , 121 , 133 , 69 , 181 , 117 , 140 , 76 , 188 , 124 , 136 , 72 , 184 , 120 ],
- [ 41 , 233 , 25 , 217 , 37 , 229 , 21 , 213 , 44 , 236 , 28 , 220 , 40 , 232 , 24 , 216 ],
- [ 169 , 105 , 153 , 89 , 165 , 101 , 149 , 85 , 172 , 108 , 156 , 92 , 168 , 104 , 152 , 88 ],
- [ 3 , 195 , 51 , 243 , 15 , 207 , 63 , 255 , 2 , 194 , 50 , 242 , 14 , 206 , 62 , 254 ],
- [ 131 , 67 , 179 , 115 , 143 , 79 , 191 , 127 , 130 , 66 , 178 , 114 , 142 , 78 , 190 , 126 ],
- [ 35 , 227 , 19 , 211 , 47 , 239 , 31 , 223 , 34 , 226 , 18 , 210 , 46 , 238 , 30 , 222 ],
- [ 163 , 99 , 147 , 83 , 175 , 111 , 159 , 95 , 162 , 98 , 146 , 82 , 174 , 110 , 158 , 94 ],
- [ 11 , 203 , 59 , 251 , 7 , 199 , 55 , 247 , 10 , 202 , 58 , 250 , 6 , 198 , 54 , 246 ],
- [ 139 , 75 , 187 , 123 , 135 , 71 , 183 , 119 , 138 , 74 , 186 , 122 , 134 , 70 , 182 , 118 ],
- [ 43 , 235 , 27 , 219 , 39 , 231 , 23 , 215 , 42 , 234 , 26 , 218 , 38 , 230 , 22 , 214 ],
- [ 171 , 107 , 155 , 91 , 167 , 103 , 151 , 87 , 170 , 106 , 154 , 90 , 166 , 102 , 150 , 86 ]
- ) if 0; # bayer 8x8
- @dither = (
- [ 1, 7,13,19,25,31],
- [29,35, 5,11,17,23],
- [14,20,26,32, 2, 8],
- [ 3, 9,15,21,27,33],
- [30,36, 6,12,18,24],
- [16,22,28,34, 4,10]
- ) if 0;
- @dither = (
- [ 1, 7,13,19,25,31],
- [26,32, 2, 8,14,20],
- [15,21,27,33, 3, 9],
- [ 4,10,16,22,28,34],
- [29,35, 5,11,17,23],
- [18,24,30,36, 6,12],
- ) if 0;
- @dither = (
- [ 7, 13, 11, 4],
- [12, 16, 14, 8],
- [10, 15, 6, 2],
- [ 5, 9, 3, 1]
- ) if 1; # TO7
- @dither = &bayer(@dither);
- # &bayer(&bayer(&bayer(&bayer([1]))));
- # fichier entree
- $file = $ARGV[0];
- $out = $file;
- $out =~ s/\.[^\.]*$/.gif/;
- exit if -e $out;
- ($x,$y, $aspect_ratio) = (160,100,"16:9");
- open(IN, "./ffmpeg -i \"$file\" 2>&1 |");
- while(<IN>) {
- if(/, (\d+)x(\d+)/) {
- ($x,$y) = ($1, $2);
- # 4:3
- if(abs($x - 4/3*$y) < abs($x - 16/9*$y)) {
- ($w,$h,$aspect_ratio) = (133,100,"4:3");
- }
- }
- }
- close(IN);
- $h = int(($w=$W)*$y/$x);
- $w = int(($h=$H)*$x/$y) if $h>$H;
- print $file," : ${x}x${y} ($aspect_ratio) -> ${w}x${h}\n";
- # dossier temporaire
- mkdir "tmp";
- open(OUT,"| ./ffmpeg -i - -v 0 -r $fps -s ${w}x${h} -an tmp/img%05d.bmp");
- open(IN, "<$file");
- &init_magick;
- $gif = Image::Magick->new(size=>"${w}x${h}");
- # setup dither
- $dy=1+$#dither;
- $dx=1+$#{$dither[0]};
- for my $y (0..$dy-1) {
- for my $x (0..$dx-1) {
- $dither[$y][$x] /= 1+$dx*$dy;
- }
- }
- $cpt = 1; my @c = (0)x8;
- binmode(IN);
- binmode(OUT);
- while(1) {
- $name = sprintf("tmp/img%05d.bmp", $cpt);
- $expected_size = $h*(($w*3 + 3)&~3) + 54 if !$expected_size;
- if($expected_size != -s $name) {
- # image pas complete: on continue de nourrir ffmpeg
- my $buf;
- my $read = read(IN,$buf,4096);
- last unless $read;
- syswrite OUT, $buf, $read;
- } else {
- # image complete!
- print STDERR int($cpt++/$fps),"s\r";
- sleep(5) if ($cpt%1200)==0; # on fait une pause régulière pour ne pas surchauffer le processeur
- # lecture de l'image
- my $img = Image::Magick->new();
- $img->Read($name);
- unlink $name;
- $img->Set(colorspace=>$LINEAR_SPACE);
- # next if $cpt<293;
- # on force la saturation (140%) pour avoir des couleurs plus franches
- #$img->Modulate(saturation=>120);
- #$img->Evaluate(operator=>'Multiply', value=>255/245);
- $tmp = Image::Magick->new(size=>"${W}x${H}");
- $tmp->Read("xc:black");
- $tmp->Composite(image=>$img, Operator=>"Over", x=>($W-$w)>>1, y=>($H-$h)>>1);
- undef $img; $img = $tmp;
- my $img2 = $img->Clone();
- $img2->Set(colorspace=>$LINEAR_SPACE eq "RGB" ? "sRGB" : "RGB");
- my @px = $img->GetPixels(height=>200, normalize=>"True");
- my @qx = &dither(0,1,@px);
- @px = &dither(1,1,@px);
- $img->OrderedDither("sam,2");
- open(PX2IMG,">/tmp/.toto2.pnm");
- print PX2IMG "P6\n320 200\n255\n",pack('C*', @qx),"\n";
- close(PX2IMG);
- $org = Image::Magick->new();
- $org->ReadImage("/tmp/.toto2.pnm");
- open(PX2IMG,">/tmp/.toto2.pnm");
- print PX2IMG "P6\n320 200\n255\n",pack('C*', @px),"\n";
- close(PX2IMG);
- $dth = Image::Magick->new();
- $dth->ReadImage("/tmp/.toto2.pnm");
- unlink "/tmp/.toto2.pnm";
- $tmp = Image::Magick->new(size=>"641x401");
- $tmp->Read("xc:white");
- $tmp->Composite(image=>$org, Operator=>"Over", x=>0, y=>0);
- $tmp->Composite(image=>$dth, Operator=>"Over", x=>321, y=>0);
- $tmp->Composite(image=>$img, Operator=>"Over", x=>0, y=>201);
- $tmp->Composite(image=>$img2, Operator=>"Over", x=>321, y=>201);
- # $dth->Blur(sigma=>1);
- #$img2->Blur(sigma=>1);
- #$dif = $dth->Compare(image=>$img2, metric=>"rmse");
- # $tmp->Composite(image=>$dth, Operator=>"Over", x=>321, y=>0);
- $img = $tmp;
- $img->OrderedDither("o8x8,8");
- $img->Write("toto.png");
- # ajout de l'image au gif animé
- $img->Set(dispose=>"None");
- $img->Set(delay=>int(100/$fps));
- push(@$gif, $img);
- # pas plus de 3600 imgs (6mins)
- last if $cpt==3600;
- }
- }
- close(OUT);
- unlink(<$ENV{'HOME'}/img*.bmp>);
- # ecriture du fichier gif
- $gif->Set(dispose=>"None");
- $gif->Set(Layers=>"optimize-trans");
- $gif->Set(delay=>int(100/$fps));
- $gif->Write($out);
- sub dither {
- my($withCorrection, $withConstraint, @px) = @_;
- my $i = 0;
- for my $y (0..199) {
- for my $x (0..39) {
- my (@s,@q,%h) = ($i..$i+7); $i+= 8;
- for my $j (@s) {$j*=3;} my $k = $x<<3;
- for my $j (@s) {
- my $d = $dither[$y%$dy][$k++ %$dx];
- my $p = ($px[$j]>=$d?2:0) + ($px[$j+1]>=$d?4:0) + ($px[$j+2]>=$d?1:0);
- push(@q, $p);
- ++$h{$p};
- }
- my ($c1,$c2);
- my @t = keys %h;
- if($#t==0) {
- $c1 = $c2 = $t[0];
- } elsif($#t==1) {
- ($c1, $c2) = (@t);
- } else {
- @t = keys %cache;%cache = () if $#t>=10000;
- my $k = join(',', $h{0}, $h{1}, $h{2}, $h{3}, $h{4}, $h{5}, $h{6}, $h{7});
- my $v = $cache{$k};
- if(!$v) {
- ($c1,$c2) = &best_couple(\%h);
- $cache{$k} = join(',',$c1,$c2);
- } else {
- ($c1,$c2) = split(/,/,$v);
- }
- }
- #print "$k $c1,$c2 $dm : $v\n";
- #for my $i (0..7) {print "$i : $col[$i] = $h[$col[$i]]\n";}
- for my $j (@s) {
- my $q = shift(@q);
- my $p = $withConstraint ? (($q^$c1)<($q^$c2)?$c1:$c2) : $q;
- @px[$j..$j+2] = ($p&2?255:0, $p&4?255:0, $p&1?255:0);
- &diffuse($p, $q, $j, \@px) if $withCorrection && $y<199 && ($p ^= $q);
- }
- }
- }
- return @px;
- }
- sub diffuse {
- # Wolfram-alpha: {x=1/3, a=arcsin(x)+x*sqrt(1-x^2)-2*x^2, b=pi/4-a-x^2, c=a/(a+2*b), d=b/(a+2*b)}
- # --> c = 0.47, b = 0.26
- my($p, $q, $j, $px) = @_;
- # diffusion
- my(@e) = (0,0,0);
- my $e = 0.08; my $f = (1-2*$e)/$e;
- $e[0] = $q&2 ? $e: -$e if $p&2;
- $e[1] = $q&4 ? $e: -$e if $p&4;
- $e[2] = $q&1 ? $e: -$e if $p&1;
- my $r = $j+319*3;
- my $z = $j%960;
- $px->[$r+0] += $e[0] if $z>0;
- $px->[$r+1] += $e[1] if $z>0;
- $px->[$r+2] += $e[2] if $z>0;
- $px->[$r+3] += $e[0]*$f;
- $px->[$r+4] += $e[1]*$f;
- $px->[$r+5] += $e[2]*$f;
- $px->[$r+6] += $e[0] if $z<959;
- $px->[$r+7] += $e[1] if $z<959;
- $px->[$r+8] += $e[2] if $z<959;
- }
- sub best_couple {
- my($h) = @_;
- my $dm = 1000;
- for my $i (0..6) {
- for my $j ($i+1..7) {
- #next if ($i&$j);
- my $d = 0;
- for my $p (keys %$h) {
- my ($a,$b) = (&dist($p^$i),&dist($p^$j));
- $d += $h->{$p}*(($a<$b?$a:$b));
- last if $d>$dm;
- }
- ($dm,$c1,$c2) = ($d,$i,$j) if $d<$dm;
- }
- }
- return ($c1, $c2);
- }
- sub dist {
- my($p) = @_;
- my($r,$g,$b) = (0.299, 0.587, 0.114);
- return ($p&4?$g:0)+($p&2?$r:0)+($p&1?$b:0);
- }
- sub bayer {
- my(@matrix) = @_;
- my $m=$#matrix;
- my $n=$#{$matrix[0]};
- # 0 3
- # 2 1
- my(@m);
- for (0..$m) {
- my $t = [];
- for (0..$n) {push(@$t, 0);}
- push(@m, $t);
- }
- for my $j (0..$m) {
- for my $i (0..$n) {
- $m[(1+$m)*0+$j][(1+$n)*0+$i] = 4*$matrix[$j][$i]-3;
- $m[(1+$m)*1+$j][(1+$n)*1+$i] = 4*$matrix[$j][$i]-2;
- $m[(1+$m)*1+$j][(1+$n)*0+$i] = 4*$matrix[$j][$i]-1;
- $m[(1+$m)*0+$j][(1+$n)*1+$i] = 4*$matrix[$j][$i]-0;
- }
- }
- return @m;
- }
- sub init_magick {
- # chargement image-magick la 1ere fois
- my($home) = "tmp";
- $ENV{'HOME'} = $home;
- mkdir($home);
- mkdir("$home/.magick");
- open(THR, ">$home/.magick/thresholds.xml");
- my $mx=1+$#dither;
- my $my=1+$#{$dither[0]};
- my $md=$mx*$my+1;
- my $mtx = "<threshold map=\"sam\">\n<description>none</description>\n";
- $mtx .= "<levels width=\"$mx\" height=\"$my\" divisor=\"$md\">\n";
- for my $l (@dither) {for my $x (@$l) {$mtx.=" $x";} $mtx.="\n";}
- $mtx .= " </levels>\n</threshold>\n";
- print THR <<EOF;
- <thresholds>
- $mtx
- <threshold map="2x2">
- <description>2x2 dither matrix</description>
- <levels width="2" height="2" divisor="5">
- 1 2
- 3 4
- </levels>
- </threshold>
- <threshold map="3x3">
- <description>3x3 dither matrix</description>
- <levels width="3" height="3" divisor="10">
- 7 8 2
- 6 9 4
- 3 5 1
- </levels>
- </threshold>
- <threshold map="5x3">
- <description>5x3 dither matrix</description>
- <levels width="3" height="5" divisor="16">
- 3 9 4
- 8 14 10
- 13 15 11
- 7 12 5
- 2 6 1
- </levels>
- </threshold>
- <threshold map="to7">
- <description>4x4 dither matrix</description>
- <levels width="4" height="4" divisor="17">
- 7 13 11 4
- 12 16 14 8
- 10 15 6 2
- 5 9 3 1
- </levels>
- </threshold>
- <threshold map="tst">
- <description>4x4 dither matrix</description>
- <levels width="4" height="5" divisor="21">
- 1 20 13 4
- 5 14 16 12
- 6 17 19 11
- 7 15 18 10
- 2 8 9 3
- </levels>
- </threshold>
- <threshold map="tst2">
- <description>4x4 dither matrix</description>
- <levels width="8" height="8" divisor="65">
- 1 45 41 13 4 48 44 16
- 17 49 61 37 20 52 64 40
- 21 53 57 33 24 56 60 36
- 5 25 29 9 8 28 32 12
- 3 47 43 15 2 46 42 14
- 19 51 63 39 18 50 62 38
- 23 55 59 35 22 54 58 34
- 7 27 31 11 6 26 30 10
- </levels>
- </threshold>
- <threshold map="vac8">
- <description>void and cluster 65 niveaux</description>
- <levels width="8" height="8" divisor="65">
- 35 57 19 55 7 51 4 21
- 29 6 41 27 37 17 59 45
- 61 15 53 12 62 25 33 9
- 23 39 31 49 2 47 13 43
- 3 52 8 22 36 58 20 56
- 38 18 60 46 30 5 42 28
- 63 26 34 11 64 16 54 10
- 14 48 1 44 24 40 32 50
- </levels>
- </threshold>
- </thresholds>
- EOF
- close(THR);
- eval 'use Image::Magick;';
- # determination de l'espace RGB lineaire
- my $img = Image::Magick->new(size=>"256x1", depth=>16);
- $img->Read('gradient:black-white');
- $img->Set(colorspace=>'RGB');
- #$img->Set(colorspace=>"Gray") unless $coul;
- my @px1 = $img->GetPixel(x=>128, y=>0);
- $img->Read('gradient:black-white');
- $img->Set(colorspace=>'sRGB');
- #$img->Set(colorspace=>"Gray") unless $coul;
- my @px2 = $img->GetPixel(x=>128, y=>0);
- my $d1 = $px1[0]-0.5; $d1=-$d1 if $d1<0;
- my $d2 = $px2[0]-0.5; $d2=-$d2 if $d2<0;
- $LINEAR_SPACE = $d1>=$d2 ? "RGB" : "sRGB";
- #print $px1[0], " ",$px2[0]," $LINEAR_SPACE\n";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement