|
tesseract 3.04.01
|
00001 /********************************************************************** 00002 * File: bmp_8.cpp 00003 * Description: Implementation of an 8-bit Bitmap class 00004 * Author: Ahmad Abdulkader 00005 * Created: 2007 00006 * 00007 * (C) Copyright 2008, Google Inc. 00008 ** Licensed under the Apache License, Version 2.0 (the "License"); 00009 ** you may not use this file except in compliance with the License. 00010 ** You may obtain a copy of the License at 00011 ** http://www.apache.org/licenses/LICENSE-2.0 00012 ** Unless required by applicable law or agreed to in writing, software 00013 ** distributed under the License is distributed on an "AS IS" BASIS, 00014 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00015 ** See the License for the specific language governing permissions and 00016 ** limitations under the License. 00017 * 00018 **********************************************************************/ 00019 00020 #include <stdlib.h> 00021 #include <math.h> 00022 #include <cstring> 00023 #include <algorithm> 00024 #include "bmp_8.h" 00025 #include "con_comp.h" 00026 #include "platform.h" 00027 #ifdef USE_STD_NAMESPACE 00028 using std::min; 00029 using std::max; 00030 #endif 00031 00032 namespace tesseract { 00033 00034 const int Bmp8::kDeslantAngleCount = (1 + static_cast<int>(0.5f + 00035 (kMaxDeslantAngle - kMinDeslantAngle) / kDeslantAngleDelta)); 00036 float *Bmp8::tan_table_ = NULL; 00037 00038 Bmp8::Bmp8(unsigned short wid, unsigned short hgt) 00039 : wid_(wid) 00040 , hgt_(hgt) { 00041 line_buff_ = CreateBmpBuffer(); 00042 } 00043 00044 Bmp8::~Bmp8() { 00045 FreeBmpBuffer(line_buff_); 00046 } 00047 00048 // free buffer 00049 void Bmp8::FreeBmpBuffer(unsigned char **buff) { 00050 if (buff != NULL) { 00051 if (buff[0] != NULL) { 00052 delete []buff[0]; 00053 } 00054 delete []buff; 00055 } 00056 } 00057 00058 void Bmp8::FreeBmpBuffer(unsigned int **buff) { 00059 if (buff != NULL) { 00060 if (buff[0] != NULL) { 00061 delete []buff[0]; 00062 } 00063 delete []buff; 00064 } 00065 } 00066 00067 // init bmp buffers 00068 unsigned char **Bmp8::CreateBmpBuffer(unsigned char init_val) { 00069 unsigned char **buff; 00070 00071 // Check valid sizes 00072 if (!hgt_ || !wid_) 00073 return NULL; 00074 00075 // compute stride (align on 4 byte boundries) 00076 stride_ = ((wid_ % 4) == 0) ? wid_ : (4 * (1 + (wid_ / 4))); 00077 00078 buff = (unsigned char **) new unsigned char *[hgt_ * sizeof(*buff)]; 00079 if (!buff) { 00080 delete []buff; 00081 return NULL; 00082 } 00083 00084 // alloc and init memory for buffer and line buffer 00085 buff[0] = (unsigned char *) 00086 new unsigned char[stride_ * hgt_ * sizeof(*buff[0])]; 00087 if (!buff[0]) { 00088 return NULL; 00089 } 00090 00091 memset(buff[0], init_val, stride_ * hgt_ * sizeof(*buff[0])); 00092 00093 for (int y = 1; y < hgt_; y++) { 00094 buff[y] = buff[y -1] + stride_; 00095 } 00096 00097 return buff; 00098 } 00099 00100 // init bmp buffers 00101 unsigned int ** Bmp8::CreateBmpBuffer(int wid, int hgt, 00102 unsigned char init_val) { 00103 unsigned int **buff; 00104 00105 // compute stride (align on 4 byte boundries) 00106 buff = (unsigned int **) new unsigned int *[hgt * sizeof(*buff)]; 00107 if (!buff) { 00108 delete []buff; 00109 return NULL; 00110 } 00111 00112 // alloc and init memory for buffer and line buffer 00113 buff[0] = (unsigned int *) new unsigned int[wid * hgt * sizeof(*buff[0])]; 00114 if (!buff[0]) { 00115 return NULL; 00116 } 00117 00118 memset(buff[0], init_val, wid * hgt * sizeof(*buff[0])); 00119 00120 for (int y = 1; y < hgt; y++) { 00121 buff[y] = buff[y -1] + wid; 00122 } 00123 00124 return buff; 00125 } 00126 00127 // clears the contents of the bmp 00128 bool Bmp8::Clear() { 00129 if (line_buff_ == NULL) { 00130 return false; 00131 } 00132 00133 memset(line_buff_[0], 0xff, stride_ * hgt_ * sizeof(*line_buff_[0])); 00134 return true; 00135 } 00136 00137 bool Bmp8::LoadFromCharDumpFile(CachedFile *fp) { 00138 unsigned short wid; 00139 unsigned short hgt; 00140 unsigned short x; 00141 unsigned short y; 00142 int buf_size; 00143 int pix; 00144 int pix_cnt; 00145 unsigned int val32; 00146 unsigned char *buff; 00147 00148 // read and check 32 bit marker 00149 if (fp->Read(&val32, sizeof(val32)) != sizeof(val32)) { 00150 return false; 00151 } 00152 00153 if (val32 != kMagicNumber) { 00154 return false; 00155 } 00156 00157 // read wid and hgt 00158 if (fp->Read(&wid, sizeof(wid)) != sizeof(wid)) { 00159 return false; 00160 } 00161 00162 if (fp->Read(&hgt, sizeof(hgt)) != sizeof(hgt)) { 00163 return false; 00164 } 00165 00166 // read buf size 00167 if (fp->Read(&buf_size, sizeof(buf_size)) != sizeof(buf_size)) { 00168 return false; 00169 } 00170 00171 // validate buf size: for now, only 3 channel (RBG) is supported 00172 pix_cnt = wid * hgt; 00173 if (buf_size != (3 * pix_cnt)) { 00174 return false; 00175 } 00176 00177 // alloc memory & read the 3 channel buffer 00178 buff = new unsigned char[buf_size]; 00179 if (buff == NULL) { 00180 return false; 00181 } 00182 00183 if (fp->Read(buff, buf_size) != buf_size) { 00184 delete []buff; 00185 return false; 00186 } 00187 00188 // create internal buffers 00189 wid_ = wid; 00190 hgt_ = hgt; 00191 00192 line_buff_ = CreateBmpBuffer(); 00193 if (line_buff_ == NULL) { 00194 delete []buff; 00195 return false; 00196 } 00197 00198 // copy the data 00199 for (y = 0, pix = 0; y < hgt_; y++) { 00200 for (x = 0; x < wid_; x++, pix += 3) { 00201 // for now we only support gray scale, 00202 // so we expect R = G = B, it this is not the case, bail out 00203 if (buff[pix] != buff[pix + 1] || buff[pix] != buff[pix + 2]) { 00204 delete []buff; 00205 return false; 00206 } 00207 line_buff_[y][x] = buff[pix]; 00208 } 00209 } 00210 00211 // delete temp buffer 00212 delete[]buff; 00213 00214 return true; 00215 } 00216 00217 Bmp8 * Bmp8::FromCharDumpFile(CachedFile *fp) { 00218 // create a Bmp8 object 00219 Bmp8 *bmp_obj = new Bmp8(0, 0); 00220 if (bmp_obj == NULL) { 00221 return NULL; 00222 } 00223 00224 if (bmp_obj->LoadFromCharDumpFile(fp) == false) { 00225 delete bmp_obj; 00226 return NULL; 00227 } 00228 00229 return bmp_obj; 00230 } 00231 00232 bool Bmp8::LoadFromCharDumpFile(FILE *fp) { 00233 unsigned short wid; 00234 unsigned short hgt; 00235 unsigned short x; 00236 unsigned short y; 00237 int buf_size; 00238 int pix; 00239 int pix_cnt; 00240 unsigned int val32; 00241 unsigned char *buff; 00242 00243 // read and check 32 bit marker 00244 if (fread(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { 00245 return false; 00246 } 00247 00248 if (val32 != kMagicNumber) { 00249 return false; 00250 } 00251 00252 // read wid and hgt 00253 if (fread(&wid, 1, sizeof(wid), fp) != sizeof(wid)) { 00254 return false; 00255 } 00256 00257 if (fread(&hgt, 1, sizeof(hgt), fp) != sizeof(hgt)) { 00258 return false; 00259 } 00260 00261 // read buf size 00262 if (fread(&buf_size, 1, sizeof(buf_size), fp) != sizeof(buf_size)) { 00263 return false; 00264 } 00265 00266 // validate buf size: for now, only 3 channel (RBG) is supported 00267 pix_cnt = wid * hgt; 00268 if (buf_size != (3 * pix_cnt)) { 00269 return false; 00270 } 00271 00272 // alloc memory & read the 3 channel buffer 00273 buff = new unsigned char[buf_size]; 00274 if (buff == NULL) { 00275 return false; 00276 } 00277 00278 if (fread(buff, 1, buf_size, fp) != buf_size) { 00279 delete []buff; 00280 return false; 00281 } 00282 00283 // create internal buffers 00284 wid_ = wid; 00285 hgt_ = hgt; 00286 00287 line_buff_ = CreateBmpBuffer(); 00288 if (line_buff_ == NULL) { 00289 delete []buff; 00290 return false; 00291 } 00292 00293 // copy the data 00294 for (y = 0, pix = 0; y < hgt_; y++) { 00295 for (x = 0; x < wid_; x++, pix += 3) { 00296 // for now we only support gray scale, 00297 // so we expect R = G = B, it this is not the case, bail out 00298 if (buff[pix] != buff[pix + 1] || buff[pix] != buff[pix + 2]) { 00299 delete []buff; 00300 return false; 00301 } 00302 line_buff_[y][x] = buff[pix]; 00303 } 00304 } 00305 00306 // delete temp buffer 00307 delete[]buff; 00308 00309 return true; 00310 } 00311 00312 Bmp8 * Bmp8::FromCharDumpFile(FILE *fp) { 00313 // create a Bmp8 object 00314 Bmp8 *bmp_obj = new Bmp8(0, 0); 00315 if (bmp_obj == NULL) { 00316 return NULL; 00317 } 00318 00319 if (bmp_obj->LoadFromCharDumpFile(fp) == false) { 00320 delete bmp_obj; 00321 return NULL; 00322 } 00323 00324 return bmp_obj; 00325 } 00326 00327 bool Bmp8::IsBlankColumn(int x) const { 00328 for (int y = 0; y < hgt_; y++) { 00329 if (line_buff_[y][x] != 0xff) { 00330 return false; 00331 } 00332 } 00333 00334 return true; 00335 } 00336 00337 bool Bmp8::IsBlankRow(int y) const { 00338 for (int x = 0; x < wid_; x++) { 00339 if (line_buff_[y][x] != 0xff) { 00340 return false; 00341 } 00342 } 00343 00344 return true; 00345 } 00346 00347 // crop the bitmap returning new dimensions 00348 void Bmp8::Crop(int *xst, int *yst, int *wid, int *hgt) { 00349 (*xst) = 0; 00350 (*yst) = 0; 00351 00352 int xend = wid_ - 1; 00353 int yend = hgt_ - 1; 00354 00355 while ((*xst) < (wid_ - 1) && (*xst) <= xend) { 00356 // column is not empty 00357 if (!IsBlankColumn((*xst))) { 00358 break; 00359 } 00360 (*xst)++; 00361 } 00362 00363 while (xend > 0 && xend >= (*xst)) { 00364 // column is not empty 00365 if (!IsBlankColumn(xend)) { 00366 break; 00367 } 00368 xend--; 00369 } 00370 00371 while ((*yst) < (hgt_ - 1) && (*yst) <= yend) { 00372 // column is not empty 00373 if (!IsBlankRow((*yst))) { 00374 break; 00375 } 00376 (*yst)++; 00377 } 00378 00379 while (yend > 0 && yend >= (*yst)) { 00380 // column is not empty 00381 if (!IsBlankRow(yend)) { 00382 break; 00383 } 00384 yend--; 00385 } 00386 00387 (*wid) = xend - (*xst) + 1; 00388 (*hgt) = yend - (*yst) + 1; 00389 } 00390 00391 // generates a scaled bitmap with dimensions the new bmp will have the 00392 // same aspect ratio and will be centered in the box 00393 bool Bmp8::ScaleFrom(Bmp8 *bmp, bool isotropic) { 00394 int x_num; 00395 int x_denom; 00396 int y_num; 00397 int y_denom; 00398 int xoff; 00399 int yoff; 00400 int xsrc; 00401 int ysrc; 00402 int xdest; 00403 int ydest; 00404 int xst_src = 0; 00405 int yst_src = 0; 00406 int xend_src = bmp->wid_ - 1; 00407 int yend_src = bmp->hgt_ - 1; 00408 int wid_src; 00409 int hgt_src; 00410 00411 // src dimensions 00412 wid_src = xend_src - xst_src + 1, 00413 hgt_src = yend_src - yst_src + 1; 00414 00415 // scale to maintain aspect ratio if required 00416 if (isotropic) { 00417 if ((wid_ * hgt_src) > (hgt_ * wid_src)) { 00418 x_num = y_num = hgt_; 00419 x_denom = y_denom = hgt_src; 00420 } else { 00421 x_num = y_num = wid_; 00422 x_denom = y_denom = wid_src; 00423 } 00424 } else { 00425 x_num = wid_; 00426 y_num = hgt_; 00427 x_denom = wid_src; 00428 y_denom = hgt_src; 00429 } 00430 00431 // compute offsets needed to center new bmp 00432 xoff = (wid_ - ((x_num * wid_src) / x_denom)) / 2; 00433 yoff = (hgt_ - ((y_num * hgt_src) / y_denom)) / 2; 00434 00435 // scale up 00436 if (y_num > y_denom) { 00437 for (ydest = yoff; ydest < (hgt_ - yoff); ydest++) { 00438 // compute un-scaled y 00439 ysrc = static_cast<int>(0.5 + (1.0 * (ydest - yoff) * 00440 y_denom / y_num)); 00441 if (ysrc < 0 || ysrc >= hgt_src) { 00442 continue; 00443 } 00444 00445 for (xdest = xoff; xdest < (wid_ - xoff); xdest++) { 00446 // compute un-scaled y 00447 xsrc = static_cast<int>(0.5 + (1.0 * (xdest - xoff) * 00448 x_denom / x_num)); 00449 if (xsrc < 0 || xsrc >= wid_src) { 00450 continue; 00451 } 00452 00453 line_buff_[ydest][xdest] = 00454 bmp->line_buff_[ysrc + yst_src][xsrc + xst_src]; 00455 } 00456 } 00457 } else { 00458 // or scale down 00459 // scaling down is a bit tricky: we'll accumulate pixels 00460 // and then compute the means 00461 unsigned int **dest_line_buff = CreateBmpBuffer(wid_, hgt_, 0), 00462 **dest_pix_cnt = CreateBmpBuffer(wid_, hgt_, 0); 00463 00464 for (ysrc = 0; ysrc < hgt_src; ysrc++) { 00465 // compute scaled y 00466 ydest = yoff + static_cast<int>(0.5 + (1.0 * ysrc * y_num / y_denom)); 00467 if (ydest < 0 || ydest >= hgt_) { 00468 continue; 00469 } 00470 00471 for (xsrc = 0; xsrc < wid_src; xsrc++) { 00472 // compute scaled y 00473 xdest = xoff + static_cast<int>(0.5 + (1.0 * xsrc * x_num / x_denom)); 00474 if (xdest < 0 || xdest >= wid_) { 00475 continue; 00476 } 00477 00478 dest_line_buff[ydest][xdest] += 00479 bmp->line_buff_[ysrc + yst_src][xsrc + xst_src]; 00480 dest_pix_cnt[ydest][xdest]++; 00481 } 00482 } 00483 00484 for (ydest = 0; ydest < hgt_; ydest++) { 00485 for (xdest = 0; xdest < wid_; xdest++) { 00486 if (dest_pix_cnt[ydest][xdest] > 0) { 00487 unsigned int pixval = 00488 dest_line_buff[ydest][xdest] / dest_pix_cnt[ydest][xdest]; 00489 00490 line_buff_[ydest][xdest] = 00491 (unsigned char) min((unsigned int)255, pixval); 00492 } 00493 } 00494 } 00495 00496 // we no longer need these temp buffers 00497 FreeBmpBuffer(dest_line_buff); 00498 FreeBmpBuffer(dest_pix_cnt); 00499 } 00500 00501 return true; 00502 } 00503 00504 bool Bmp8::LoadFromRawData(unsigned char *data) { 00505 unsigned char *pline_data = data; 00506 00507 // copy the data 00508 for (int y = 0; y < hgt_; y++, pline_data += wid_) { 00509 memcpy(line_buff_[y], pline_data, wid_ * sizeof(*pline_data)); 00510 } 00511 00512 return true; 00513 } 00514 00515 bool Bmp8::SaveBmp2CharDumpFile(FILE *fp) const { 00516 unsigned short wid; 00517 unsigned short hgt; 00518 unsigned short x; 00519 unsigned short y; 00520 int buf_size; 00521 int pix; 00522 int pix_cnt; 00523 unsigned int val32; 00524 unsigned char *buff; 00525 00526 // write and check 32 bit marker 00527 val32 = kMagicNumber; 00528 if (fwrite(&val32, 1, sizeof(val32), fp) != sizeof(val32)) { 00529 return false; 00530 } 00531 00532 // write wid and hgt 00533 wid = wid_; 00534 if (fwrite(&wid, 1, sizeof(wid), fp) != sizeof(wid)) { 00535 return false; 00536 } 00537 00538 hgt = hgt_; 00539 if (fwrite(&hgt, 1, sizeof(hgt), fp) != sizeof(hgt)) { 00540 return false; 00541 } 00542 00543 // write buf size 00544 pix_cnt = wid * hgt; 00545 buf_size = 3 * pix_cnt; 00546 if (fwrite(&buf_size, 1, sizeof(buf_size), fp) != sizeof(buf_size)) { 00547 return false; 00548 } 00549 00550 // alloc memory & write the 3 channel buffer 00551 buff = new unsigned char[buf_size]; 00552 if (buff == NULL) { 00553 return false; 00554 } 00555 00556 // copy the data 00557 for (y = 0, pix = 0; y < hgt_; y++) { 00558 for (x = 0; x < wid_; x++, pix += 3) { 00559 buff[pix] = 00560 buff[pix + 1] = 00561 buff[pix + 2] = line_buff_[y][x]; 00562 } 00563 } 00564 00565 if (fwrite(buff, 1, buf_size, fp) != buf_size) { 00566 delete []buff; 00567 return false; 00568 } 00569 00570 // delete temp buffer 00571 delete[]buff; 00572 00573 return true; 00574 } 00575 00576 // copy part of the specified bitmap to the top of the bitmap 00577 // does any necessary clipping 00578 void Bmp8::Copy(int x_st, int y_st, int wid, int hgt, Bmp8 *bmp_dest) const { 00579 int x_end = min(x_st + wid, static_cast<int>(wid_)), 00580 y_end = min(y_st + hgt, static_cast<int>(hgt_)); 00581 00582 for (int y = y_st; y < y_end; y++) { 00583 for (int x = x_st; x < x_end; x++) { 00584 bmp_dest->line_buff_[y - y_st][x - x_st] = 00585 line_buff_[y][x]; 00586 } 00587 } 00588 } 00589 00590 bool Bmp8::IsIdentical(Bmp8 *pBmp) const { 00591 if (wid_ != pBmp->wid_ || hgt_ != pBmp->hgt_) { 00592 return false; 00593 } 00594 00595 for (int y = 0; y < hgt_; y++) { 00596 if (memcmp(line_buff_[y], pBmp->line_buff_[y], wid_) != 0) { 00597 return false; 00598 } 00599 } 00600 00601 return true; 00602 } 00603 00604 // Detect connected components in the bitmap 00605 ConComp ** Bmp8::FindConComps(int *concomp_cnt, int min_size) const { 00606 (*concomp_cnt) = 0; 00607 00608 unsigned int **out_bmp_array = CreateBmpBuffer(wid_, hgt_, 0); 00609 if (out_bmp_array == NULL) { 00610 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not allocate " 00611 "bitmap array\n"); 00612 return NULL; 00613 } 00614 00615 // listed of connected components 00616 ConComp **concomp_array = NULL; 00617 00618 int x; 00619 int y; 00620 int x_nbr; 00621 int y_nbr; 00622 int concomp_id; 00623 int alloc_concomp_cnt = 0; 00624 00625 // neighbors to check 00626 const int nbr_cnt = 4; 00627 00628 // relative coordinates of nbrs 00629 int x_del[nbr_cnt] = {-1, 0, 1, -1}, 00630 y_del[nbr_cnt] = {-1, -1, -1, 0}; 00631 00632 00633 for (y = 0; y < hgt_; y++) { 00634 for (x = 0; x < wid_; x++) { 00635 // is this a foreground pix 00636 if (line_buff_[y][x] != 0xff) { 00637 int master_concomp_id = 0; 00638 ConComp *master_concomp = NULL; 00639 00640 // checkout the nbrs 00641 for (int nbr = 0; nbr < nbr_cnt; nbr++) { 00642 x_nbr = x + x_del[nbr]; 00643 y_nbr = y + y_del[nbr]; 00644 00645 if (x_nbr < 0 || y_nbr < 0 || x_nbr >= wid_ || y_nbr >= hgt_) { 00646 continue; 00647 } 00648 00649 // is this nbr a foreground pix 00650 if (line_buff_[y_nbr][x_nbr] != 0xff) { 00651 // get its concomp ID 00652 concomp_id = out_bmp_array[y_nbr][x_nbr]; 00653 00654 // this should not happen 00655 if (concomp_id < 1 || concomp_id > alloc_concomp_cnt) { 00656 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): illegal " 00657 "connected component id: %d\n", concomp_id); 00658 FreeBmpBuffer(out_bmp_array); 00659 delete []concomp_array; 00660 return NULL; 00661 } 00662 00663 // if we has previously found a component then merge the two 00664 // and delete the latest one 00665 if (master_concomp != NULL && concomp_id != master_concomp_id) { 00666 // relabel all the pts 00667 ConCompPt *pt_ptr = concomp_array[concomp_id - 1]->Head(); 00668 while (pt_ptr != NULL) { 00669 out_bmp_array[pt_ptr->y()][pt_ptr->x()] = master_concomp_id; 00670 pt_ptr = pt_ptr->Next(); 00671 } 00672 00673 // merge the two concomp 00674 if (!master_concomp->Merge(concomp_array[concomp_id - 1])) { 00675 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " 00676 "merge connected component: %d\n", concomp_id); 00677 FreeBmpBuffer(out_bmp_array); 00678 delete []concomp_array; 00679 return NULL; 00680 } 00681 00682 // delete the merged concomp 00683 delete concomp_array[concomp_id - 1]; 00684 concomp_array[concomp_id - 1] = NULL; 00685 } else { 00686 // this is the first concomp we encounter 00687 master_concomp_id = concomp_id; 00688 master_concomp = concomp_array[master_concomp_id - 1]; 00689 00690 out_bmp_array[y][x] = master_concomp_id; 00691 00692 if (!master_concomp->Add(x, y)) { 00693 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " 00694 "add connected component (%d,%d)\n", x, y); 00695 FreeBmpBuffer(out_bmp_array); 00696 delete []concomp_array; 00697 return NULL; 00698 } 00699 } 00700 } // foreground nbr 00701 } // nbrs 00702 00703 // if there was no foreground pix, then create a new concomp 00704 if (master_concomp == NULL) { 00705 master_concomp = new ConComp(); 00706 if (master_concomp == NULL || master_concomp->Add(x, y) == false) { 00707 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " 00708 "allocate or add a connected component\n"); 00709 FreeBmpBuffer(out_bmp_array); 00710 delete []concomp_array; 00711 return NULL; 00712 } 00713 00714 // extend the list of concomps if needed 00715 if ((alloc_concomp_cnt % kConCompAllocChunk) == 0) { 00716 ConComp **temp_con_comp = 00717 new ConComp *[alloc_concomp_cnt + kConCompAllocChunk]; 00718 if (temp_con_comp == NULL) { 00719 fprintf(stderr, "Cube ERROR (Bmp8::FindConComps): could not " 00720 "extend array of connected components\n"); 00721 FreeBmpBuffer(out_bmp_array); 00722 delete []concomp_array; 00723 return NULL; 00724 } 00725 00726 if (alloc_concomp_cnt > 0) { 00727 memcpy(temp_con_comp, concomp_array, 00728 alloc_concomp_cnt * sizeof(*concomp_array)); 00729 00730 delete []concomp_array; 00731 } 00732 00733 concomp_array = temp_con_comp; 00734 } 00735 00736 concomp_array[alloc_concomp_cnt++] = master_concomp; 00737 out_bmp_array[y][x] = alloc_concomp_cnt; 00738 } 00739 } // foreground pix 00740 } // x 00741 } // y 00742 00743 // free the concomp bmp 00744 FreeBmpBuffer(out_bmp_array); 00745 00746 if (alloc_concomp_cnt > 0 && concomp_array != NULL) { 00747 // scan the array of connected components and color 00748 // the o/p buffer with the corresponding concomps 00749 (*concomp_cnt) = 0; 00750 ConComp *concomp = NULL; 00751 00752 for (int concomp_idx = 0; concomp_idx < alloc_concomp_cnt; concomp_idx++) { 00753 concomp = concomp_array[concomp_idx]; 00754 00755 // found a concomp 00756 if (concomp != NULL) { 00757 // add the connected component if big enough 00758 if (concomp->PtCnt() > min_size) { 00759 concomp->SetLeftMost(true); 00760 concomp->SetRightMost(true); 00761 concomp->SetID((*concomp_cnt)); 00762 concomp_array[(*concomp_cnt)++] = concomp; 00763 } else { 00764 delete concomp; 00765 } 00766 } 00767 } 00768 } 00769 00770 return concomp_array; 00771 } 00772 00773 // precompute the tan table to speedup deslanting 00774 bool Bmp8::ComputeTanTable() { 00775 int ang_idx; 00776 float ang_val; 00777 00778 // alloc memory for tan table 00779 delete []tan_table_; 00780 tan_table_ = new float[kDeslantAngleCount]; 00781 if (tan_table_ == NULL) { 00782 return false; 00783 } 00784 00785 for (ang_idx = 0, ang_val = kMinDeslantAngle; 00786 ang_idx < kDeslantAngleCount; ang_idx++) { 00787 tan_table_[ang_idx] = tan(ang_val * M_PI / 180.0f); 00788 ang_val += kDeslantAngleDelta; 00789 } 00790 00791 return true; 00792 } 00793 00794 // generates a deslanted bitmap from the passed bitmap. 00795 bool Bmp8::Deslant() { 00796 int x; 00797 int y; 00798 int des_x; 00799 int des_y; 00800 int ang_idx; 00801 int best_ang; 00802 int min_des_x; 00803 int max_des_x; 00804 int des_wid; 00805 00806 // only do deslanting if bitmap is wide enough 00807 // otherwise it slant estimate might not be reliable 00808 if (wid_ < (hgt_ * 2)) { 00809 return true; 00810 } 00811 00812 // compute tan table if needed 00813 if (tan_table_ == NULL && !ComputeTanTable()) { 00814 return false; 00815 } 00816 00817 // compute min and max values for x after deslant 00818 min_des_x = static_cast<int>(0.5f + (hgt_ - 1) * tan_table_[0]); 00819 max_des_x = (wid_ - 1) + 00820 static_cast<int>(0.5f + (hgt_ - 1) * tan_table_[kDeslantAngleCount - 1]); 00821 00822 des_wid = max_des_x - min_des_x + 1; 00823 00824 // alloc memory for histograms 00825 int **angle_hist = new int*[kDeslantAngleCount]; 00826 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 00827 angle_hist[ang_idx] = new int[des_wid]; 00828 if (angle_hist[ang_idx] == NULL) { 00829 delete[] angle_hist; 00830 return false; 00831 } 00832 memset(angle_hist[ang_idx], 0, des_wid * sizeof(*angle_hist[ang_idx])); 00833 } 00834 00835 // compute histograms 00836 for (y = 0; y < hgt_; y++) { 00837 for (x = 0; x < wid_; x++) { 00838 // find a non-bkgrnd pixel 00839 if (line_buff_[y][x] != 0xff) { 00840 des_y = hgt_ - y - 1; 00841 // stamp all histograms 00842 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 00843 des_x = x + static_cast<int>(0.5f + (des_y * tan_table_[ang_idx])); 00844 if (des_x >= min_des_x && des_x <= max_des_x) { 00845 angle_hist[ang_idx][des_x - min_des_x]++; 00846 } 00847 } 00848 } 00849 } 00850 } 00851 00852 // find the histogram with the lowest entropy 00853 float entropy; 00854 double best_entropy = 0.0f; 00855 double norm_val; 00856 00857 best_ang = -1; 00858 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 00859 entropy = 0.0f; 00860 00861 for (x = min_des_x; x <= max_des_x; x++) { 00862 if (angle_hist[ang_idx][x - min_des_x] > 0) { 00863 norm_val = (1.0f * angle_hist[ang_idx][x - min_des_x] / hgt_); 00864 entropy += (-1.0f * norm_val * log(norm_val)); 00865 } 00866 } 00867 00868 if (best_ang == -1 || entropy < best_entropy) { 00869 best_ang = ang_idx; 00870 best_entropy = entropy; 00871 } 00872 00873 // free the histogram 00874 delete[] angle_hist[ang_idx]; 00875 } 00876 delete[] angle_hist; 00877 00878 // deslant 00879 if (best_ang != -1) { 00880 unsigned char **dest_lines; 00881 int old_wid = wid_; 00882 00883 // create a new buffer 00884 wid_ = des_wid; 00885 dest_lines = CreateBmpBuffer(); 00886 if (dest_lines == NULL) { 00887 return false; 00888 } 00889 00890 for (y = 0; y < hgt_; y++) { 00891 for (x = 0; x < old_wid; x++) { 00892 // find a non-bkgrnd pixel 00893 if (line_buff_[y][x] != 0xff) { 00894 des_y = hgt_ - y - 1; 00895 // compute new pos 00896 des_x = x + static_cast<int>(0.5f + (des_y * tan_table_[best_ang])); 00897 dest_lines[y][des_x - min_des_x] = 0; 00898 } 00899 } 00900 } 00901 00902 // free old buffer 00903 FreeBmpBuffer(line_buff_); 00904 line_buff_ = dest_lines; 00905 } 00906 return true; 00907 } 00908 00909 // Load dimensions & contents of bitmap from raw data 00910 bool Bmp8::LoadFromCharDumpFile(unsigned char **raw_data_ptr) { 00911 unsigned short wid; 00912 unsigned short hgt; 00913 unsigned short x; 00914 unsigned short y; 00915 unsigned char *raw_data = (*raw_data_ptr); 00916 int buf_size; 00917 int pix; 00918 unsigned int val32; 00919 00920 // read and check 32 bit marker 00921 memcpy(&val32, raw_data, sizeof(val32)); 00922 raw_data += sizeof(val32); 00923 00924 if (val32 != kMagicNumber) { 00925 return false; 00926 } 00927 00928 // read wid and hgt 00929 memcpy(&wid, raw_data, sizeof(wid)); 00930 raw_data += sizeof(wid); 00931 00932 memcpy(&hgt, raw_data, sizeof(hgt)); 00933 raw_data += sizeof(hgt); 00934 00935 // read buf size 00936 memcpy(&buf_size, raw_data, sizeof(buf_size)); 00937 raw_data += sizeof(buf_size); 00938 00939 // validate buf size: for now, only 3 channel (RBG) is supported 00940 if (buf_size != (3 * wid * hgt)) { 00941 return false; 00942 } 00943 00944 wid_ = wid; 00945 hgt_ = hgt; 00946 00947 line_buff_ = CreateBmpBuffer(); 00948 if (line_buff_ == NULL) { 00949 return false; 00950 } 00951 00952 // copy the data 00953 for (y = 0, pix = 0; y < hgt_; y++) { 00954 for (x = 0; x < wid_; x++, pix += 3) { 00955 // for now we only support gray scale, 00956 // so we expect R = G = B, it this is not the case, bail out 00957 if (raw_data[pix] != raw_data[pix + 1] || 00958 raw_data[pix] != raw_data[pix + 2]) { 00959 return false; 00960 } 00961 00962 line_buff_[y][x] = raw_data[pix]; 00963 } 00964 } 00965 00966 (*raw_data_ptr) = raw_data + buf_size; 00967 return true; 00968 } 00969 00970 float Bmp8::ForegroundRatio() const { 00971 int fore_cnt = 0; 00972 00973 if (wid_ == 0 || hgt_ == 0) { 00974 return 1.0; 00975 } 00976 00977 for (int y = 0; y < hgt_; y++) { 00978 for (int x = 0; x < wid_; x++) { 00979 fore_cnt += (line_buff_[y][x] == 0xff ? 0 : 1); 00980 } 00981 } 00982 00983 return (1.0 * (fore_cnt / hgt_) / wid_); 00984 } 00985 00986 // generates a deslanted bitmap from the passed bitmap 00987 bool Bmp8::HorizontalDeslant(double *deslant_angle) { 00988 int x; 00989 int y; 00990 int des_y; 00991 int ang_idx; 00992 int best_ang; 00993 int min_des_y; 00994 int max_des_y; 00995 int des_hgt; 00996 00997 // compute tan table if necess. 00998 if (tan_table_ == NULL && !ComputeTanTable()) { 00999 return false; 01000 } 01001 01002 // compute min and max values for x after deslant 01003 min_des_y = min(0, static_cast<int>((wid_ - 1) * tan_table_[0])); 01004 max_des_y = (hgt_ - 1) + 01005 max(0, static_cast<int>((wid_ - 1) * tan_table_[kDeslantAngleCount - 1])); 01006 01007 des_hgt = max_des_y - min_des_y + 1; 01008 01009 // alloc memory for histograms 01010 int **angle_hist = new int*[kDeslantAngleCount]; 01011 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 01012 angle_hist[ang_idx] = new int[des_hgt]; 01013 if (angle_hist[ang_idx] == NULL) { 01014 delete[] angle_hist; 01015 return false; 01016 } 01017 memset(angle_hist[ang_idx], 0, des_hgt * sizeof(*angle_hist[ang_idx])); 01018 } 01019 01020 // compute histograms 01021 for (y = 0; y < hgt_; y++) { 01022 for (x = 0; x < wid_; x++) { 01023 // find a non-bkgrnd pixel 01024 if (line_buff_[y][x] != 0xff) { 01025 // stamp all histograms 01026 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 01027 des_y = y - static_cast<int>(x * tan_table_[ang_idx]); 01028 if (des_y >= min_des_y && des_y <= max_des_y) { 01029 angle_hist[ang_idx][des_y - min_des_y]++; 01030 } 01031 } 01032 } 01033 } 01034 } 01035 01036 // find the histogram with the lowest entropy 01037 float entropy; 01038 float best_entropy = 0.0f; 01039 float norm_val; 01040 01041 best_ang = -1; 01042 for (ang_idx = 0; ang_idx < kDeslantAngleCount; ang_idx++) { 01043 entropy = 0.0f; 01044 01045 for (y = min_des_y; y <= max_des_y; y++) { 01046 if (angle_hist[ang_idx][y - min_des_y] > 0) { 01047 norm_val = (1.0f * angle_hist[ang_idx][y - min_des_y] / wid_); 01048 entropy += (-1.0f * norm_val * log(norm_val)); 01049 } 01050 } 01051 01052 if (best_ang == -1 || entropy < best_entropy) { 01053 best_ang = ang_idx; 01054 best_entropy = entropy; 01055 } 01056 01057 // free the histogram 01058 delete[] angle_hist[ang_idx]; 01059 } 01060 delete[] angle_hist; 01061 01062 (*deslant_angle) = 0.0; 01063 01064 // deslant 01065 if (best_ang != -1) { 01066 unsigned char **dest_lines; 01067 int old_hgt = hgt_; 01068 01069 // create a new buffer 01070 min_des_y = min(0, static_cast<int>((wid_ - 1) * -tan_table_[best_ang])); 01071 max_des_y = (hgt_ - 1) + 01072 max(0, static_cast<int>((wid_ - 1) * -tan_table_[best_ang])); 01073 hgt_ = max_des_y - min_des_y + 1; 01074 dest_lines = CreateBmpBuffer(); 01075 if (dest_lines == NULL) { 01076 return false; 01077 } 01078 01079 for (y = 0; y < old_hgt; y++) { 01080 for (x = 0; x < wid_; x++) { 01081 // find a non-bkgrnd pixel 01082 if (line_buff_[y][x] != 0xff) { 01083 // compute new pos 01084 des_y = y - static_cast<int>((x * tan_table_[best_ang])); 01085 dest_lines[des_y - min_des_y][x] = 0; 01086 } 01087 } 01088 } 01089 01090 // free old buffer 01091 FreeBmpBuffer(line_buff_); 01092 line_buff_ = dest_lines; 01093 01094 (*deslant_angle) = kMinDeslantAngle + (best_ang * kDeslantAngleDelta); 01095 } 01096 01097 return true; 01098 } 01099 01100 float Bmp8::MeanHorizontalHistogramEntropy() const { 01101 float entropy = 0.0f; 01102 01103 // compute histograms 01104 for (int y = 0; y < hgt_; y++) { 01105 int pix_cnt = 0; 01106 01107 for (int x = 0; x < wid_; x++) { 01108 // find a non-bkgrnd pixel 01109 if (line_buff_[y][x] != 0xff) { 01110 pix_cnt++; 01111 } 01112 } 01113 01114 if (pix_cnt > 0) { 01115 float norm_val = (1.0f * pix_cnt / wid_); 01116 entropy += (-1.0f * norm_val * log(norm_val)); 01117 } 01118 } 01119 01120 return entropy / hgt_; 01121 } 01122 01123 int *Bmp8::HorizontalHistogram() const { 01124 int *hist = new int[hgt_]; 01125 if (hist == NULL) { 01126 return NULL; 01127 } 01128 01129 // compute histograms 01130 for (int y = 0; y < hgt_; y++) { 01131 hist[y] = 0; 01132 01133 for (int x = 0; x < wid_; x++) { 01134 // find a non-bkgrnd pixel 01135 if (line_buff_[y][x] != 0xff) { 01136 hist[y]++; 01137 } 01138 } 01139 } 01140 01141 return hist; 01142 } 01143 01144 } // namespace tesseract