/* ** bmp8 must be grayscale ** (x1,y1) and (x2,y2) from top left of bitmap */ void jocr_single_word_from_bmp8(char *text,int maxlen,WILLUSBITMAP *bmp8, int x1,int y1,int x2,int y2,int allow_spaces, int std_proc) { job_t *job,_job; int i,w,h,dw,dh,bw; unsigned char *src,*dst; // static char *funcname="jocr_single_word_from_bmp8"; char *buf; int pgm2asc(job_t *job); char *getTextLine(int); if (x1>x2) { w=x1; x1=x2; x2=w; } w=x2-x1+1; bw=w/40; if (bw<6) bw=6; dw=w+bw*2; if (y1>y2) { h=y1; y1=y2; y2=h; } h=y2-y1+1; dh=h+bw*2; job=&_job; job_init(job); job_init_image(job); // willus_mem_alloc_warn((void **)&job->src.p.p,w*h,funcname,10); /* Must use malloc since job_free_image counts on this. */ job->src.p.p=malloc(dw*dh); job->src.p.x=dw; job->src.p.y=dh; job->src.p.bpp=1; src=bmp_rowptr_from_top(bmp8,y1)+x1; memset(job->src.p.p,255,dw*dh); dst=(unsigned char *)job->src.p.p + dw*bw + bw; for (i=y1;i<=y2;i++,dst+=dw,src+=bmp8->width) memcpy(dst,src,w); pgm2asc(job); buf=getTextLine(0); if (buf) { strncpy(text,buf,maxlen-1); text[maxlen-1]='\0'; if (std_proc) ocr_text_proc(text,allow_spaces); } else text[0]='\0'; // willus_mem_free((double **)&job->src.p.p,funcname); job_free_image(job); }
/* ------------------------------------------------------------- // ------ MAIN - replace this by your own aplication! // ------------------------------------------------------------- */ int main(int argn, char *argv[]) { int multipnm=1; job_t job1, *job; /* fixme, dont want global variables for lib */ job=OCR_JOB=&job1; setvbuf(stdout, (char *) NULL, _IONBF, 0); /* not buffered */ job_init(job); /* init cfg and db */ process_arguments(job, argn, argv); /* load character data base (JS1002: now outside pgm2asc) */ if ( job->cfg.mode & 2 ) /* check for db-option flag */ load_db(job); /* load_db uses readpnm() and would conflict with multi images */ while (multipnm==1) { /* multi-image loop */ job_init_image(job); /* single image */ mark_start(job); multipnm = read_picture(job); /* separation of main and rest for using as lib this will be changed later => introduction of set_option() for better communication to the engine */ if (multipnm<0) break; /* read error */ /* call main loop */ pgm2asc(job); mark_end(job); print_output(job); job_free_image(job); } return ((multipnm<0)?multipnm:0); /* -1=255 on error, 0 ok */ }
char* gocr_main(job_t* job) { int multipnm=1; char* output; setvbuf(stdout, (char *) NULL, _IONBF, 0); /* not buffered */ /* load character data base (JS1002: now outside pgm2asc) */ if ( job->cfg.mode & 2 ) /* check for db-option flag */ load_db(job); /* load_db uses readpnm() and would conflict with multi images */ while (multipnm==1) { /* multi-image loop */ job_init_image(job); /* single image */ mark_start(job); multipnm = read_picture(job); /* separation of main and rest for using as lib this will be changed later => introduction of set_option() for better communication to the engine */ if (multipnm<0) break; /* read error */ /* call main loop */ pgm2asc(job); mark_end(job); output = print_output(job); job_free_image(job); } // return ((multipnm<0)?multipnm:0); /* -1=255 on error, 0 ok */ return output; }
char get_atom_label(const Magick::Image &image, const Magick::ColorGray &bg, int x1, int y1, int x2, int y2, double THRESHOLD, int dropx, int dropy, bool no_filtering, bool verbose, bool numbers) { char c = UNKNOWN_CHAR; const int width = x2 - x1 + 1; const int height = y2 - y1 + 1; unsigned char *pixmap = (unsigned char *) malloc(width * height); for (int i = y1; i <= y2; i++) for (int j = x1; j <= x2; j++) pixmap[(i - y1) * width + j - x1] = (unsigned char) (255 - 255 * get_pixel(image, bg, j, i, THRESHOLD)); // Here we drop down from the top of the box, middle of x coordinate and extract connected component int t = 1; int y = dropy - y1 + 1; int x = dropx - x1; while ((t != 0) && (y < height)) { t = pixmap[y * width + x]; y++; } if (t != 0) { free(pixmap); return 0; } #pragma omp critical { y--; pixmap[y * width + x] = 2; list<int> cx; list<int> cy; cx.push_back(x); cy.push_back(y); while (!cx.empty()) { x = cx.front(); y = cy.front(); cx.pop_front(); cy.pop_front(); pixmap[y * width + x] = 1; // this goes around 3x3 square touching the chosen pixel for (int i = x - 1; i < x + 2; i++) for (int j = y - 1; j < y + 2; j++) if (i < width && j < height && i >= 0 && j >= 0 && pixmap[j * width + i] == 0) { cx.push_back(i); cy.push_back(j); pixmap[j * width + i] = 2; } } // Flatten the bitmap. Note: the bitmap is inverted after this cycle (255 means "empty", 0 means "pixel"). for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) pixmap[i * width + j] = (pixmap[i * width + j] == 1 ? 0 : 255); job_t gocr_job; // The list of all characters, that can be recognised as atom label: string char_filter = RECOGNIZED_CHARS; if (numbers) char_filter = "1"; if (no_filtering) char_filter.clear(); job_init(&gocr_job); job_init_image(&gocr_job); //gocr_job.cfg.cs = 160; //gocr_job.cfg.certainty = 80; //gocr_job.cfg.dust_size = 1; gocr_job.src.p.x = width; gocr_job.src.p.y = height; gocr_job.src.p.bpp = 1; gocr_job.src.p.p = pixmap; if (char_filter.empty()) gocr_job.cfg.cfilter = (char*)NULL; else gocr_job.cfg.cfilter = (char*) char_filter.c_str(); struct OCRAD_Pixmap *ocrad_pixmap = new OCRAD_Pixmap(); unsigned char *ocrad_bitmap = (unsigned char *) malloc(width * height); memset(ocrad_bitmap, 0, width * height); ocrad_pixmap->height = height; ocrad_pixmap->width = width; ocrad_pixmap->mode = OCRAD_bitmap; ocrad_pixmap->data = ocrad_bitmap; // Number of non-zero pixels on the bitmap, excluding the 1px border: int pixmap_pixels_count = 0; // Number of zero pixels on the bitmap, excluding the 1px border: int pixmap_zeros_count = 0; // The code below initialises "opix->data" buffer ("bitmap_data") for OCRAD from "tmp" buffer: #ifdef HAVE_CUNEIFORM_LIB Magick::Image cuneiform_img(Magick::Geometry(2 * width + 2, height), "white"); // From cuneiform_src/cli/cuneiform-cli.cpp::preprocess_image(Magick::Image&):168 cuneiform_img.monochrome(); cuneiform_img.type(Magick::BilevelType); #endif for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (pixmap[y * width + x] == 0) { ocrad_bitmap[y * width + x] = 1; #ifdef HAVE_CUNEIFORM_LIB // Draw two identical samples that follow one another. We do so because Cuneiform has difficulties in recognizing single characters: cuneiform_img.pixelColor(x, y, "black"); cuneiform_img.pixelColor(x + width + 2, y, "black"); #endif if (x > 0 && x < width - 1 && y > 0 && y < height - 1) pixmap_pixels_count++; } else if (x > 0 && x < width - 1 && y > 0 && y < height - 1) pixmap_zeros_count++; } } if (verbose) { cout << "Box to OCR: " << x1 << "x" << y1 << "-" << x2 << "x" << y2 << " w/h: " << width << "x" << height << endl; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) cout << (gocr_job.src.p.p[i * width + j] / 255 ? '#' : '.'); cout << endl; } } if (pixmap_pixels_count <= MIN_CHAR_POINTS || pixmap_zeros_count <= MIN_CHAR_POINTS) goto FINALIZE; c = osra_gocr_ocr(gocr_job); if (verbose) cout << "GOCR: c=" << c << endl; //c = UNKNOWN_CHAR; // Switch off GOCR recognition // Character recognition succeeded for GOCR: if (c != UNKNOWN_CHAR) goto FINALIZE; // Character recognition failed for GOCR and we try OCRAD: c = osra_ocrad_ocr(ocrad_pixmap, char_filter); if (verbose) cout << "OCRAD: c=" << c << endl; //c = UNKNOWN_CHAR; // Switch off OCRAD recognition // Character recognition succeeded for OCRAD: if (c != UNKNOWN_CHAR) goto FINALIZE; #ifdef HAVE_TESSERACT_LIB c = osra_tesseract_ocr(gocr_job.src.p.p, width, height, char_filter); if (verbose) cout << "Tesseract: c=" << c << endl; //c = UNKNOWN_CHAR; // Switch off Tesseract recognition // Character recognition succeeded for Tesseract: if (c != UNKNOWN_CHAR) goto FINALIZE; #endif #ifdef HAVE_CUNEIFORM_LIB // TODO: Why box width should be more than 7 for Cuneiform? if (width <= 7) goto FINALIZE; c = osra_cuneiform_ocr(cuneiform_img, char_filter); if (verbose) cout << "Cuneiform: c=" << c << endl; //c = UNKNOWN_CHAR; // Switch off Cuneiform recognition #endif FINALIZE: // "pixmap" is freed together with "gocr_job". job_free_image(&gocr_job); OCR_JOB = NULL; JOB = NULL; delete ocrad_pixmap; // delete OCRAD Pixmap free(ocrad_bitmap); // TODO: Why there are problems with "7" with a given box size? If the problem is engine-specific, it should be moved to appropriate section if (c == '7' && (width <= 10 || height <= 20)) c = UNKNOWN_CHAR; } // #pragma omp critical return(c == UNKNOWN_CHAR ? 0 : c); }