Skip to content

Instantly share code, notes, and snippets.

@certik
Created July 2, 2016 02:38
Show Gist options
  • Save certik/4336299de10f400ee49943bd9f8a8ba6 to your computer and use it in GitHub Desktop.
Save certik/4336299de10f400ee49943bd9f8a8ba6 to your computer and use it in GitHub Desktop.
#!/usr/bin/perl
no warnings 'utf8';
use strict;
use Image::Magick;
use Term::Size;
use Getopt::Long;
use Time::HiRes qw(gettimeofday tv_interval);
our ($cachehit,$cachemiss,$nocache,$filter);
our $goodmatchdistance = 30;
our $blur = 1;
my ($columns, $rows) = Term::Size::chars *STDOUT{IO};
$rows--; # subtract a row for the line the user will be on.
my ($saturation,$debug);
my $interval;
my @timerstats;
GetOptions ( "saturation=s", \$saturation,
"debug", \$debug,
"nocache", \$nocache,
"filter=s", \$filter,
"blur=s", \$blur,
"width=s", \$columns,
"height=s", $rows );
our %colormap;
our %cache;
my $filename = shift();
# Let's start timing for output.
my $t0 = [gettimeofday];
my $img = new Image::Magick;
unless ( -f $filename ) { die "You must specify a valid filename.\n"; }
$img->Read($filename);
# Details on time loading image
my $t1 = [gettimeofday]; $interval = tv_interval($t0,$t1); push @timerstats,"Loading image: $interval"; $t0 = [gettimeofday];
setup_colormap();
if ($filter) {
$img->Resize( geometry => $columns."x".($rows * 2), filter=>$filter, blur=>$blur);
}
else {
$img->Resize( geometry => $columns."x".($rows * 2));
}
# Details on time resizing image
$t1 = [gettimeofday]; $interval = tv_interval($t0,$t1); push @timerstats,"Resizing image: $interval"; $t0 = [gettimeofday];
if (length($saturation)) { $img->Modulate( saturation => $saturation ); }
my $out;
my $width = $img->Get("width");
my $height = $img->Get("height");
my @map = $img->GetPixels('width'=> $width, 'height' => $height, 'x' => 0, 'y' => 0, 'map' => 'RGB', 'normalize' => 'true');
for (my $y=0;$y<($height -1 );$y+=2) {
for my $x ( 0 .. ($width -1 )) {
my $bc = find_closest_match($map[($x*3)+($y*$width*3)]*255,$map[($x*3)+($y*$width*3)+1]*255,$map[($x*3)+($y*$width*3)+2]*255);
my $fc = find_closest_match($map[($x*3)+(($y+1)*$width*3)]*255,$map[($x*3)+(($y+1)*$width*3)+1]*255,$map[($x*3)+(($y+1)*$width*3)+2]*255);
$out .= "[38;5;".$fc."m[48;5;".$bc."m".chr(0x2584);
}
$out .= "\n";
}
# Details on time colormatching image
$t1 = [gettimeofday]; $interval = tv_interval($t0,$t1); push @timerstats,"Colormatching image: $interval"; $t0 = [gettimeofday];
print $out;
# Details on time colormatching image
$t1 = [gettimeofday]; $interval = tv_interval($t0,$t1); push @timerstats,"Displaying image: $interval"; $t0 = [gettimeofday];
if ($debug) {
unless ($nocache) { warn "Hit: $cachehit Miss: $cachemiss\n";}
warn "Timer stats:\n ".join("\n ",@timerstats)."\n";
}
sub find_closest_match {
my ($r,$g,$b) = @_;
$r = int($r);
$g = int($g);
$b = int($b);
if ((! $nocache) && ($cache{join(":",$r,$g,$b)})) {
$cachehit++;
return($cache{join(":",$r,$g,$b)});
}
my $dist = 999999999;
my $index = 0;
my $boxsize = 50;
foreach my $key (keys %colormap) {
my ($r2,$g2,$b2) = @{$colormap{$key}};
if ((abs($r2-$r) > $boxsize) ||
(abs($g2-$g) > $boxsize) ||
(abs($b2-$b) > $boxsize)) { next; }
my $nd = sqrt(($r2-$r)**2 + ($g2-$g)**2 + ($b2-$b)**2);
if ($nd < $dist) {
$dist = $nd;
$index = $key;
if ($dist < $goodmatchdistance) {
unless ($nocache) {
$cachemiss++;
$cache{join(":",$r,$g,$b)} = $index;
}
return($index);
}
}
}
unless ($nocache) {
$cachemiss++;
$cache{join(":",$r,$g,$b)} = $index;
}
#warn "returning (".join(",",@{$colormap{$index}}).") for $dist for ($r,$g,$b)\n";
return($index);
}
sub setup_colormap {
%colormap = (
16 => [ 0, 0, 0 ],
17 => [ 0, 0, 95 ],
18 => [ 0, 0, 135 ],
19 => [ 0, 0, 175 ],
20 => [ 0, 0, 215 ],
21 => [ 0, 0, 255 ],
22 => [ 0, 95, 0 ],
23 => [ 0, 95, 95 ],
24 => [ 0, 95, 135 ],
25 => [ 0, 95, 175 ],
26 => [ 0, 95, 215 ],
27 => [ 0, 95, 255 ],
28 => [ 0, 135, 0 ],
29 => [ 0, 135, 95 ],
30 => [ 0, 135, 135 ],
31 => [ 0, 135, 175 ],
32 => [ 0, 135, 215 ],
33 => [ 0, 135, 255 ],
34 => [ 0, 175, 0 ],
35 => [ 0, 175, 95 ],
36 => [ 0, 175, 135 ],
37 => [ 0, 175, 175 ],
38 => [ 0, 175, 215 ],
39 => [ 0, 175, 255 ],
40 => [ 0, 215, 0 ],
41 => [ 0, 215, 95 ],
42 => [ 0, 215, 135 ],
43 => [ 0, 215, 175 ],
44 => [ 0, 215, 215 ],
45 => [ 0, 215, 255 ],
46 => [ 0, 255, 0 ],
47 => [ 0, 255, 95 ],
48 => [ 0, 255, 135 ],
49 => [ 0, 255, 175 ],
50 => [ 0, 255, 215 ],
51 => [ 0, 255, 255 ],
52 => [ 95, 0, 0 ],
53 => [ 95, 0, 95 ],
54 => [ 95, 0, 135 ],
55 => [ 95, 0, 175 ],
56 => [ 95, 0, 215 ],
57 => [ 95, 0, 255 ],
58 => [ 95, 95, 0 ],
59 => [ 95, 95, 95 ],
60 => [ 95, 95, 135 ],
61 => [ 95, 95, 175 ],
62 => [ 95, 95, 215 ],
63 => [ 95, 95, 255 ],
64 => [ 95, 135, 0 ],
65 => [ 95, 135, 95 ],
66 => [ 95, 135, 135 ],
67 => [ 95, 135, 175 ],
68 => [ 95, 135, 215 ],
69 => [ 95, 135, 255 ],
70 => [ 95, 175, 0 ],
71 => [ 95, 175, 95 ],
72 => [ 95, 175, 135 ],
73 => [ 95, 175, 175 ],
74 => [ 95, 175, 215 ],
75 => [ 95, 175, 255 ],
76 => [ 95, 215, 0 ],
77 => [ 95, 215, 95 ],
78 => [ 95, 215, 135 ],
79 => [ 95, 215, 175 ],
80 => [ 95, 215, 215 ],
81 => [ 95, 215, 255 ],
82 => [ 95, 255, 0 ],
83 => [ 95, 255, 95 ],
84 => [ 95, 255, 135 ],
85 => [ 95, 255, 175 ],
86 => [ 95, 255, 215 ],
87 => [ 95, 255, 255 ],
88 => [ 135, 0, 0 ],
89 => [ 135, 0, 95 ],
90 => [ 135, 0, 135 ],
91 => [ 135, 0, 175 ],
92 => [ 135, 0, 215 ],
93 => [ 135, 0, 255 ],
94 => [ 135, 95, 0 ],
95 => [ 135, 95, 95 ],
96 => [ 135, 95, 135 ],
97 => [ 135, 95, 175 ],
98 => [ 135, 95, 215 ],
99 => [ 135, 95, 255 ],
100 => [ 135, 135, 0 ],
101 => [ 135, 135, 95 ],
102 => [ 135, 135, 135 ],
103 => [ 135, 135, 175 ],
104 => [ 135, 135, 215 ],
105 => [ 135, 135, 255 ],
106 => [ 135, 175, 0 ],
107 => [ 135, 175, 95 ],
108 => [ 135, 175, 135 ],
109 => [ 135, 175, 175 ],
110 => [ 135, 175, 215 ],
111 => [ 135, 175, 255 ],
112 => [ 135, 215, 0 ],
113 => [ 135, 215, 95 ],
114 => [ 135, 215, 135 ],
115 => [ 135, 215, 175 ],
116 => [ 135, 215, 215 ],
117 => [ 135, 215, 255 ],
118 => [ 135, 255, 0 ],
119 => [ 135, 255, 95 ],
120 => [ 135, 255, 135 ],
121 => [ 135, 255, 175 ],
122 => [ 135, 255, 215 ],
123 => [ 135, 255, 255 ],
124 => [ 175, 0, 0 ],
125 => [ 175, 0, 95 ],
126 => [ 175, 0, 135 ],
127 => [ 175, 0, 175 ],
128 => [ 175, 0, 215 ],
129 => [ 175, 0, 255 ],
130 => [ 175, 95, 0 ],
131 => [ 175, 95, 95 ],
132 => [ 175, 95, 135 ],
133 => [ 175, 95, 175 ],
134 => [ 175, 95, 215 ],
135 => [ 175, 95, 255 ],
136 => [ 175, 135, 0 ],
137 => [ 175, 135, 95 ],
138 => [ 175, 135, 135 ],
139 => [ 175, 135, 175 ],
140 => [ 175, 135, 215 ],
141 => [ 175, 135, 255 ],
142 => [ 175, 175, 0 ],
143 => [ 175, 175, 95 ],
144 => [ 175, 175, 135 ],
145 => [ 175, 175, 175 ],
146 => [ 175, 175, 215 ],
147 => [ 175, 175, 255 ],
148 => [ 175, 215, 0 ],
149 => [ 175, 215, 95 ],
150 => [ 175, 215, 135 ],
151 => [ 175, 215, 175 ],
152 => [ 175, 215, 215 ],
153 => [ 175, 215, 255 ],
154 => [ 175, 255, 0 ],
155 => [ 175, 255, 95 ],
156 => [ 175, 255, 135 ],
157 => [ 175, 255, 175 ],
158 => [ 175, 255, 215 ],
159 => [ 175, 255, 255 ],
160 => [ 215, 0, 0 ],
161 => [ 215, 0, 95 ],
162 => [ 215, 0, 135 ],
163 => [ 215, 0, 175 ],
164 => [ 215, 0, 215 ],
165 => [ 215, 0, 255 ],
166 => [ 215, 95, 0 ],
167 => [ 215, 95, 95 ],
168 => [ 215, 95, 135 ],
169 => [ 215, 95, 175 ],
170 => [ 215, 95, 215 ],
171 => [ 215, 95, 255 ],
172 => [ 215, 135, 0 ],
173 => [ 215, 135, 95 ],
174 => [ 215, 135, 135 ],
175 => [ 215, 135, 175 ],
176 => [ 215, 135, 215 ],
177 => [ 215, 135, 255 ],
178 => [ 215, 175, 0 ],
179 => [ 215, 175, 95 ],
180 => [ 215, 175, 135 ],
181 => [ 215, 175, 175 ],
182 => [ 215, 175, 215 ],
183 => [ 215, 175, 255 ],
184 => [ 215, 215, 0 ],
185 => [ 215, 215, 95 ],
186 => [ 215, 215, 135 ],
187 => [ 215, 215, 175 ],
188 => [ 215, 215, 215 ],
189 => [ 215, 215, 255 ],
190 => [ 215, 255, 0 ],
191 => [ 215, 255, 95 ],
192 => [ 215, 255, 135 ],
193 => [ 215, 255, 175 ],
194 => [ 215, 255, 215 ],
195 => [ 215, 255, 255 ],
196 => [ 255, 0, 0 ],
197 => [ 255, 0, 95 ],
198 => [ 255, 0, 135 ],
199 => [ 255, 0, 175 ],
200 => [ 255, 0, 215 ],
201 => [ 255, 0, 255 ],
202 => [ 255, 95, 0 ],
203 => [ 255, 95, 95 ],
204 => [ 255, 95, 135 ],
205 => [ 255, 95, 175 ],
206 => [ 255, 95, 215 ],
207 => [ 255, 95, 255 ],
208 => [ 255, 135, 0 ],
209 => [ 255, 135, 95 ],
210 => [ 255, 135, 135 ],
211 => [ 255, 135, 175 ],
212 => [ 255, 135, 215 ],
213 => [ 255, 135, 255 ],
214 => [ 255, 175, 0 ],
215 => [ 255, 175, 95 ],
216 => [ 255, 175, 135 ],
217 => [ 255, 175, 175 ],
218 => [ 255, 175, 215 ],
219 => [ 255, 175, 255 ],
220 => [ 255, 215, 0 ],
221 => [ 255, 215, 95 ],
222 => [ 255, 215, 135 ],
223 => [ 255, 215, 175 ],
224 => [ 255, 215, 215 ],
225 => [ 255, 215, 255 ],
226 => [ 255, 255, 0 ],
227 => [ 255, 255, 95 ],
228 => [ 255, 255, 135 ],
229 => [ 255, 255, 175 ],
230 => [ 255, 255, 215 ],
231 => [ 255, 255, 255 ],
232 => [ 8, 8, 8 ],
233 => [ 18, 18, 18 ],
234 => [ 28, 28, 28 ],
235 => [ 38, 38, 38 ],
236 => [ 48, 48, 48 ],
237 => [ 58, 58, 58 ],
238 => [ 68, 68, 68 ],
239 => [ 78, 78, 78 ],
240 => [ 88, 88, 88 ],
241 => [ 98, 98, 98 ],
242 => [ 108, 108, 108 ],
243 => [ 118, 118, 118 ],
244 => [ 128, 128, 128 ],
245 => [ 138, 138, 138 ],
246 => [ 148, 148, 148 ],
247 => [ 158, 158, 158 ],
248 => [ 168, 168, 168 ],
249 => [ 178, 178, 178 ],
250 => [ 188, 188, 188 ],
251 => [ 198, 198, 198 ],
252 => [ 208, 208, 208 ],
253 => [ 218, 218, 218 ],
254 => [ 228, 228, 228 ],
255 => [ 238, 238, 238 ],
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment