root/tags/FuzzyOcr-3.5.1/FuzzyOcr/Hashing.pm

Revision 125, 14.1 kB (checked in by decoder, 2 years ago)

FuzzyOcr? 3.5.1 tag dir and tarball

Line 
1 use strict;
2 package FuzzyOcr::Hashing;
3
4 use base 'Exporter';
5 our @EXPORT_OK = qw(check_image_hash_db
6     add_image_hash_db
7     calc_image_hash);
8
9 use lib qw(..);
10 use FuzzyOcr::Config qw(get_thresholds get_config set_config get_tmpdir get_mysql_ddb);
11 use FuzzyOcr::Misc qw(save_execute);
12 use FuzzyOcr::Logging qw(debuglog errorlog warnlog infolog);
13 use Fcntl;
14 use Fcntl ':flock';
15
16 #Implements all functions related to Image Hashing
17
18 sub within_threshold {
19     my $thresref = get_thresholds();
20     my %Threshold = %$thresref;
21
22     my $digest = shift;
23     my $record = shift;
24
25     my ($dimg,$dkey) = split('::',$digest);
26     my ($rimg,$rkey) = split('::',$record);
27     my ($ds, $dh, $dw, $dcn) = split(':',$dimg);
28     my ($rs, $rh, $rw, $rcn) = split(':',$rimg);
29     return(0) unless $rs;
30     return(0) unless $rh;
31     return(0) unless $rw;
32     return(0) unless $rcn;
33     return(0) unless $rkey;
34     return(0) if ((abs($ds  - $rs ) / $rs ) > $Threshold{s});
35     return(0) if ((abs($dh  - $rh ) / $rh ) > $Threshold{h});
36     return(0) if ((abs($dw  - $rw ) / $rw ) > $Threshold{w});
37     return(0) if ((abs($dcn - $rcn) / $rcn) > $Threshold{cn});
38            
39     my @rcf = split('::',$rkey);
40     my @dcf = split('::',$dkey);
41
42     my (@dcfs, @rcfs);
43     foreach (@dcf) { push @dcfs,split(':',$_); }
44     foreach (@rcf) { push @rcfs,split(':',$_); }
45
46     my $total = scalar(@rcfs);
47     if ($total == scalar(@dcfs)) {
48         my $match = 0;
49         foreach (0 .. ($total-1)) {
50             $match++ if (abs($dcfs[$_] - $rcfs[$_]) <= $Threshold{c});
51         }
52         infolog("image matched <$match> of <$total> colors");
53         return(1) if ($match == $total);
54     }
55     return(0);
56 }
57
58 sub check_image_hash_db {
59     my $conf = get_config();
60     if ($conf->{focr_enable_image_hashing} == 0) {
61         warnlog("Image Hashing is disabled");
62         return (0,'');
63     }
64     my $digest = $_[0];
65     my $dbfile = $_[1] || $conf->{focr_db_hash};
66     my $fname  = $_[2];
67     my $ctype  = $_[3];
68     my $ftype  = $_[4] || 0;
69     my ($img, $key) = split('::', $digest,2);
70     return (0,'') unless defined $key;
71     my $now = time;
72     my $hash = $digest;
73     my $ret = 0; my $txt = 'Exact';
74     my $dinfo;
75
76     if ($conf->{focr_enable_image_hashing} == 3) {
77         unless (defined $conf->{focr_mysql_ddb}) {
78             warnlog("Connection to MySQL server unavailable");
79             return (0,'');
80         }
81         my $ddb   = $conf->{focr_mysql_ddb};
82         my $db    = $conf->{focr_mysql_db};
83         my $sql   = qq(select * from $db.$dbfile where $dbfile.key='$key');
84         my @data  = $ddb->selectrow_array($sql);
85         my $next  = 0;
86         my $when  = 0;
87         my $match = 0;
88         if ((scalar(@data)>0) and ($img eq $data[1])) {
89             $match++;
90             infolog("Found[$dbfile]: Score='$data[8]' Info: '$data[9]'");
91             $next  = $data[5]; $next++;
92             $when  = $data[7]; $data[8] += 0;
93             $ret   = $data[8] == 0 ? 0.001 : $data[8];
94             $dinfo = $data[9] || '';
95             if ($data[2] eq '') {
96                 infolog("Updating $txt info File-Name:'$fname'");
97                 $ddb->do(qq(update $db.$dbfile set $dbfile.fname=? where $dbfile.key='$key'),undef,$fname);
98             }
99             if ($data[3] eq '') {
100                 infolog("Updating $txt info Content-Type:'$ctype'");
101                 $ddb->do(qq(update $db.$dbfile set $dbfile.ctype=? where $dbfile.key='$key'),undef,$ctype);
102             }
103             if ($data[4] != $ftype) {
104                 infolog("Updating $txt info File-Type:'$ftype'");
105                 $ddb->do(qq(update $db.$dbfile set $dbfile.ftype=? where $dbfile.key='$key'),undef,$ftype);
106             }
107         }
108         unless ($match) {
109             my $then = time - ($conf->{focr_db_max_days}*86400);
110             $sql = qq(select * from $db.$dbfile order by $dbfile.check);
111             my $sth  = $ddb->prepare($sql); $sth->execute;
112             while (my @row = $sth->fetchrow_array) {
113                 my $hash2 = $row[1] || "0:0:0:0";
114                 $hash2 .= "::$row[0]";
115                 if (within_threshold($digest,$hash2)) {
116                     $txt   = 'Approx';
117                     $key   = $row[0];
118                     $next  = $row[5] + 1;
119                     $when  = $row[7] || $now;
120                     $ret   = $dbfile eq $conf->{focr_mysql_hash} ? $row[8] : $row[5];
121                     $dinfo = $row[9] || '';
122                     infolog("Found[$dbfile]: Score='$row[8]' Info: '$row[9]'");
123                     last;
124                 }
125             }
126             # Expire old records...
127             $sql = qq(delete from $db.$dbfile where $dbfile.check < $then);
128             debuglog($sql,2);
129             $ddb->do($sql);
130         }
131         if ($ret > 0) {
132             if ($dbfile eq $conf->{focr_mysql_hash}) {
133                 infolog("Found Score <$ret> for $txt Image Hash");
134             }
135             infolog("Matched [$next] time(s). Prev match: ".fmt_time($now - $when));
136             $sql = qq(update $db.$dbfile set $dbfile.match='$next',$dbfile.check='$now' where $dbfile.key='$key');
137             $ddb->do($sql);
138             debuglog($sql);
139         }
140         return ($ret,$dinfo);
141     }
142     elsif ($conf->{focr_enable_image_hashing} == 2) {
143         import MLDBM qw(DB_File Storable);
144         my %DB = (); my $dbm; my $sdbm;
145         $sdbm = tie %DB, 'MLDBM::Sync', $dbfile, O_CREAT|O_RDWR or $ret++;
146         if ($ret>0) {
147             warnlog("No Image Hash database found at \"$dbfile\", or permissions wrong.");
148             return (0,'');
149         }
150         $sdbm->Lock;
151         if (defined $DB{$key}) {
152             $dbm = $DB{$key};
153             if ($img eq $dbm->{basic}) {
154                 $ret = $dbm->{score} || 0.001;
155                 $dinfo = $dbm->{dinfo} || '';
156                 $dbm->{fname} = $fname;
157                 $dbm->{ctype} = $ctype;
158                 infolog("Updating $txt info File:'$fname' Type:'$ctype'");
159                 $DB{$key} = $dbm;
160             }
161         }
162         if ($ret == 0) {
163             my $then = time - ($conf->{focr_db_max_days}*86400);
164             foreach my $k (keys %DB) {
165                 $dbm  = $DB{$k};
166                 $hash = $dbm->{basic} ? $dbm->{basic} : "0:0:0:0::$k";
167                 if (within_threshold($digest,$hash)) {
168                     $ret  = $dbfile eq $conf->{focr_db_hash} ? $dbm->{score} : $dbm->{match};
169                     $txt  = 'Approx'; $dinfo = $dbm->{dinfo} || '';
170                     infolog("Found in: <$dbfile>");
171                     last;
172                 }
173                 # Has the record expired??
174                 $dbm->{check} = $now - 1 unless defined $dbm->{check};
175                 if ($dbm->{check} < $then) {
176                     infolog("Expiring <$k> older than $conf->{focr_db_max_days} days");
177                     delete $DB{$k};
178                 }
179             }
180         }
181         if ($ret>0) {
182             $dbm->{match}++;
183             if ($dbfile eq $conf->{focr_db_hash}) {
184                 $ret = sprintf("%0.3f",$dbm->{score});
185                 infolog("Found Score <$ret> for $txt Image Hash");
186             }
187             infolog("Matched [$dbm->{match}] time(s). Prev match: ".fmt_time(time - $dbm->{check}));
188             $dbm->{check} = time;
189             $DB{$key} = $dbm;
190         }
191         $sdbm->UnLock;
192         undef $sdbm;
193         untie %DB;
194         return ($ret,$dinfo);
195     }
196     elsif ($conf->{focr_enable_image_hashing} == 1) {
197         $ret = open HASH, $conf->{focr_digest_db};
198         unless($ret) {
199             warnlog("No Image Hash database found at \"$conf->{focr_digest_db}\", or permissions wrong.");
200             return (0,'');
201         }
202         while (<HASH>) {
203             chomp;
204             ($ret,$hash) = split('::',$_,2);
205             if (within_threshold($digest,$hash)) {
206                 infolog("Found Score <$ret> for Hash <$hash>");
207                 return ($ret,'');
208             }
209         }
210         close HASH;
211         return (0,'');
212     }
213 }
214
215 sub add_image_hash_db {
216     my $conf = get_config();
217     return if ($conf->{focr_enable_image_hashing} == 0);
218     my $digest = $_[0];
219     my $score  = $_[1];
220     my $ret = 0;
221
222     if ($conf->{focr_enable_image_hashing} == 3) {
223         unless (defined $conf->{focr_mysql_ddb}) {
224             warnlog("Connection to MySQL server unavailable");
225             return;
226         }
227         my $db    = $conf->{focr_mysql_db};
228         my $ddb   = $conf->{focr_mysql_ddb};
229         my $table = $_[2] || $conf->{focr_mysql_hash};
230         my $fname = $_[3] || '';
231         my $ctype = $_[4] || 'image';
232         my $ftype = $_[5] || 0;
233         my $dinfo = $_[6] || '';
234         infolog("Adding Hash to table: \"$db.$table\" with score \"$score\"");
235         my $sql;
236         my ($img,$key) = split('::',$digest,2);
237         if (defined $key) {
238             $sql = "select basic from $db.$table where $table.key='$key'";
239             my @data = $ddb->selectrow_array($sql);
240             if (scalar(@data)>0) {
241                 if ($conf->{focr_mysql_update_hash}) {
242                     infolog("Hash already in $db.$table updating...");
243                     $sql  = "update $db.$table set ";
244                     my @params;
245                     unless ($data[1] eq $img) {
246                         $sql .= "basic=?,"; push @params,$img;
247                     }
248                     unless ($data[2] eq $fname) {
249                         $sql .= "fname=?,"; push @params,$fname;
250                     }
251                     unless ($data[3] eq $ctype) {
252                         $sql .= "ctype=?,"; push @params,$ctype;
253                     }
254                     unless ($data[4] == $ftype) {
255                         $sql .= "ftype=?,"; push @params,$ftype;
256                     }
257                     unless ($data[8] == $score) {
258                         $sql .= "score=?,"; push @params,$score;
259                     }
260                     unless ($data[9] == $dinfo) {
261                         $sql .= "dinfo=?,"; push @params,$dinfo;
262                     }
263                     $sql  =~ s/,$//;
264                     $sql .= " where $table.key='$key'";
265                     $ddb->do($sql,undef,@params);
266                     foreach my $p (@params) { $sql =~ s/\?/$p/; }
267                     debuglog($sql);
268                 } else {
269                     infolog("Hash already in $db.$table skipping...");
270                 }
271             } else {
272                 my @params = (
273                     $key, $img, $fname, $ctype, $ftype,
274                     ($table eq $conf->{focr_mysql_hash} ? 0 : 1),
275                     time, time, $score, $dinfo);
276                 $sql = "insert into $db.$table values (?,?,?,?,?,?,?,?,?,?)";
277                 $ddb->do($sql,undef,@params);
278                 foreach my $p (@params) { $sql =~ s/\?/$p/; }
279                 debuglog($sql);
280             }
281         }
282     }
283     elsif ($conf->{focr_enable_image_hashing} == 2) {
284         import MLDBM qw(DB_File Storable);
285         my $dbfile = $_[2] || $conf->{focr_db_hash};
286         my %DB = (); my $sdbm;
287         $sdbm = tie %DB, 'MLDBM::Sync', $dbfile, O_CREAT|O_RDWR or $ret++;
288         if ($ret>0) {
289             warnlog("Unable to open/create Image Hash database at \"$dbfile\", check permissions.");
290             return;
291         }
292         $sdbm->Lock;
293         infolog("Adding Hash to \"$dbfile\" with score \"$score\"");
294         my ($img,$key) = split('::',$digest,2);
295         if (defined $key) {
296             my $dbm = $DB{$key};
297             $dbm->{fname} = $_[3];
298             $dbm->{ctype} = $_[4];
299             $dbm->{ftype} = $_[5];
300             $dbm->{dinfo} = $_[6];
301             $dbm->{basic} = $img;
302             $dbm->{score} = $score;
303             $dbm->{input} =
304             $dbm->{check} = time;
305             $dbm->{match} = $dbfile eq $conf->{focr_db_hash} ? 0 : 1;
306             $DB{$key} = $dbm;
307         }
308         $sdbm->UnLock;
309         undef $sdbm;
310         untie %DB;
311     }
312     elsif ($conf->{focr_enable_image_hashing} == 1) {
313         if (-e $conf->{focr_digest_db}) {
314             $ret = open DB, ">>$conf->{focr_digest_db}";
315         } else {
316             $ret = open DB,  ">$conf->{focr_digest_db}";
317         }
318         unless ($ret) {
319             warnlog("Unable to open/create Image Hash database at \"$conf->{focr_digest_db}\", check permissions.");
320             return;
321         }
322         infolog("Adding Hash to \"$conf->{focr_digest_db}\"");
323         flock( DB, LOCK_EX );
324         seek( DB, 0, 2 );
325         print DB "${score}::${digest}\n";
326         close(DB);
327     }
328     debuglog("Digest: $digest");
329 }
330
331 sub calc_image_hash {
332     my $conf = get_config();
333     my $imgdir = get_tmpdir();
334     my $thresref = get_thresholds();
335     my %Threshold = %$thresref;
336     my $pfile = $_[0];
337     my $pic   = $_[1];
338     my $hash;
339
340     foreach my $a (qw/ppmhist/) { #pamfile
341         unless (defined $conf->{"focr_bin_$a"}) {
342             errorlog("calc_image_hash cannot exec $a");
343             return (1, '');
344         }
345     }
346    
347     unless (-r $pfile) {
348         errorlog("Cannot read $pfile");
349         return(1, '');
350     }
351
352     my ($r, @stdout_data) = save_execute(
353         "$conf->{focr_bin_ppmhist} -noheader $pfile", undef,
354         ">$imgdir/ppmhist.info",
355         ">/dev/null", 1);
356
357     if ($r) {
358         chomp $r;
359         errorlog("$conf->{focr_bin_ppmhist}: ".
360             ($r<0) ? 'Timed out' : 'Error'
361             ." [$r], skipping...");
362         return (1, '');
363     }
364
365     my $cnt = 0;
366     my $c = scalar(@stdout_data);
367     my $s = (stat($pfile))[7] || 0;
368     $hash = sprintf "%d:%d:%d:%d",$s,
369         defined $pic->{height} ? $pic->{height} : 0,
370         defined $pic->{width}  ? $pic->{width}  : 0,
371         $c;
372     if ($Threshold{max_hash}) {
373         foreach (@stdout_data) {
374             $_ =~ s/ +/ /g;
375             my(@d) = split(' ', $_);
376             $hash .= sprintf("::%d:%d:%d:%d:%d",@d);
377             if ($cnt++ ge $Threshold{max_hash}) {
378                 last;
379             }
380         }
381     }
382     debuglog("Got: <$hash>");
383     return(0, $hash);
384 }
385
386 sub fmt_time {
387     my $when = $_[0];
388     my $ret;
389
390     if ($when>86400) {
391         my $d = int($when/86400);
392         $when -= $d*86400;
393         $ret = "$d days,";
394     }
395     if ($when>3600) {
396         my $h = int($when/3600);
397         $when -= $h*3600;
398         $ret .= " $h hrs.";
399     }
400     if ($when>60) {
401         my $m = int($when/60);
402         $when -= $m*60;
403         $ret .= " $m min.";
404     }
405     if ($when>0) {
406         $ret .= " $when sec.";
407     }
408     $ret .= " ago";
409     return $ret;
410 }
411
412 1;
Note: See TracBrowser for help on using the browser.