// -*- C++ -*- // Copyright 2006-2007 Deutsches Forschungszentrum fuer Kuenstliche Intelligenz // or its licensors, as applicable. // // You may not use this file except under the terms of the accompanying license. // // Licensed under the Apache License, Version 2.0 (the "License"); you // may not use this file except in compliance with the License. You may // obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Project: ocr-bpnet - neural network classifier // File: make-garbage.cc // Purpose: producing garbage (wrongly segmented) characters from ground truth // Responsible: mezhirov // Reviewer: // Primary Repository: // Web Sites: www.iupr.org, www.dfki.de #include "colib.h" #include "imglib.h" #include "imgio.h" #include "ocr-utils.h" #include "segmentation.h" #include "line-info.h" #include "ocr-segmentations.h" #include "grid.h" #include "grouper.h" #include "make-garbage.h" using namespace colib; using namespace iulib; using namespace ocropus; namespace { enum {MAX_PIX_DIFF = 7}; colib::vec2 get_mass_center(const bytearray &image) { double sx = 0; double sy = 0; double mass = 0; int w = image.dim(0); int h = image.dim(1); for (int x = 0; x < w; x++) { for (int y = 0; y < h; y++) { byte m = 255 - image(x, y); sx += x * m; sy += y * m; mass += m; } } colib::vec2 center; center[0] = float(sx / mass); center[1] = float(sy / mass); return center; } double black_pixel_diff_one_way(bytearray &img1, colib::vec2 center1, bytearray &img2, colib::vec2 center2) { double penalty = 0; colib::vec2 shift = center2 - center1; int shift_x = int(floor(shift(0) + .5)); int shift_y = int(floor(shift(1) + .5)); for(int x1 = 0; x1 < img1.dim(0); x1++) for(int y1 = 0; y1 < img1.dim(1); y1++) { if (img1(x1, y1)) continue; int x2 = x1 + shift_x; int y2 = y1 + shift_y; if (x2 < 0 || y2 < 0 || x2 >= img2.dim(0) || y2 >= img2.dim(1)) penalty += 255; else penalty += img2(x2, y2); } return penalty; } double black_pixel_diff(bytearray &img1, colib::vec2 center1, bytearray &img2, colib::vec2 center2) { return black_pixel_diff_one_way(img1, center1, img2, center2) + black_pixel_diff_one_way(img2, center2, img1, center1); } double black_pixel_diff(bytearray &img1, bytearray &img2) { return black_pixel_diff(img1, get_mass_center(img1), img2, get_mass_center(img2)); } bool match(bytearray &image1, bytearray &image2) { return black_pixel_diff(image1, image2) <= 255 * MAX_PIX_DIFF; } bool has_match(objlist &image_set, bytearray &image) { for(int i = 0; i < image_set.length(); i++) { if(match(image_set[i], image)) return true; } return false; } /// Extract the set of pixels with the given value and return it /// as a black-on-white image. static void extract_segment(bytearray &result, intarray &image, int n) { makelike(result, image); fill(result, 255); for(int i = 0; i < image.length1d(); i++) { if(image.at1d(i) == n) result.at1d(i) = 0; } } void extract_components(objlist &components, intarray &black_seg) { narray bboxes; bounding_boxes(bboxes, black_seg); components.resize(bboxes.length() - 1); for(int i = 0; i < components.length(); i++) { intarray subimage; rectangle &b = bboxes[i + 1]; extract_subimage(subimage, black_seg, b.x0, b.y0, b.x1, b.y1); extract_segment(components[i], subimage, i + 1); } } struct PixelDifferenceGarbageFilter : IGarbageFilter { objlist true_chars; virtual void setGroundTruth(intarray &segmentation) { extract_components(true_chars, segmentation); } virtual bool isReallyGarbage(bytearray &component) { return !has_match(true_chars, component); } }; void make_garbage_given_true_chars(narray &bboxes, objlist &garbage, IGarbageFilter &filter, intarray &oversegmented_line) { autodel grouper(make_SimpleGrouper()); grouper->setSegmentation(oversegmented_line); bytearray binary_line; make_line_segmentation_white(oversegmented_line); forget_segmentation(binary_line, oversegmented_line); int n = grouper->length(); for(int i = 0; i < n; i++) { bytearray component; bytearray mask; grouper->extract(component, mask, binary_line, i); invert(mask); if(filter.isReallyGarbage(mask)) { bboxes.push(grouper->boundingBox(i)); move(garbage.push(), mask); } } } } namespace ocropus { IGarbageFilter *make_PixelDifferenceGarbageFilter() { return new PixelDifferenceGarbageFilter(); } void make_garbage(narray &bboxes, narray &result_garbage, intarray &orig_segmented_line, ISegmentLine &segmenter) { objlist garbage; intarray segmented_line; copy(segmented_line, orig_segmented_line); make_line_segmentation_black(segmented_line); bytearray binary_line; forget_segmentation(binary_line, segmented_line); intarray oversegmented_line; segmenter.charseg(oversegmented_line, binary_line); make_line_segmentation_black(oversegmented_line); autodel filter(make_PixelDifferenceGarbageFilter()); filter->setGroundTruth(segmented_line); make_garbage_given_true_chars(bboxes, garbage, *filter, oversegmented_line); result_garbage.resize(garbage.length()); for(int i = 0; i < garbage.length(); i++) move(result_garbage[i], garbage[i]); } void make_garbage(narray &bboxes, narray &result_garbage, intarray &orig_segmented_line) { autodel segmenter(make_CurvedCutSegmenter()); make_garbage(bboxes, result_garbage, orig_segmented_line, *segmenter); } }