VoronoiBasis::VoronoiBasis(const Points & points, const Points & centers): m_points(points),m_centers(centers){ assert(points.n_cols == centers.n_cols); m_dist = dist_mat(m_points,m_centers); n_basis = centers.n_rows; n_dim = points.n_cols; n_points = points.n_rows; assert(n_dim == centers.n_cols); }
std::vector<cv::Rect> SoftPPWordSplitter::split(const CCGroup &grp) { cv::Rect bb = grp.get_rect(); // generate the projection profile sums cv::Mat sums(1, bb.width, CV_32FC1, cv::Scalar(0)); ProjectionProfileComputer pp_computer(cv::Size(bb.width, 1), bb.x); for (int i = 0; i < grp.ccs.size(); i++) { sums = pp_computer.compute(grp.ccs[i].pixels, sums); } int threshold = pp_computer.compute_threshold(sums); if (_verbose) { std::cout << "Projection Profile Threshold: " << threshold << std::endl; } cv::Mat gaps = sums < threshold; // now shrink each bounding rect on the border with the gaps matrix std::vector<cv::Rect> original_rects(grp.ccs.size()); std::transform( grp.ccs.begin(), grp.ccs.end(), original_rects.begin(), [](const CC &cc) -> cv::Rect { return cc.rect; }); std::sort( original_rects.begin(), original_rects.end(), [](const cv::Rect &a, const cv::Rect &b) -> bool { return a.x < b.x; }); RectShrinker shrinker(0.10, bb.x); std::vector<cv::Rect> shrinked_rects(shrinker.shrink(original_rects, gaps)); //cv::Mat img(grp.get_image()); //cv::imshow("RECTS-wo-rects", img); //cv::waitKey(0); //for (cv::Rect r : shrinked_rects) { // cv::rectangle(img, r.tl(), r.br(), cv::Scalar(128)); //} //cv::imshow("RECTS", img); //cv::waitKey(0); std::vector<bool> collide(bb.width, false); for (int i = 0; i < shrinked_rects.size(); i++) { for (int j = shrinked_rects[i].x; j < shrinked_rects[i].x + shrinked_rects[i].width; j++) { collide[j-bb.x] = true; } } //std::vector<bool> collide(bb.width, false); //for (int i = 0; i < ccs.size(); i++) { // for (int j = ccs[i].rect.x; j < ccs[i].rect.x + ccs[i].rect.width; j++) { // collide[j-bb.x] = true; // } //} std::vector<float> heights(grp.ccs.size(), 0.0); std::transform( grp.ccs.begin(), grp.ccs.end(), heights.begin(), [] (const CC &c) -> float { return c.rect.height; }); float mean_height = cv::sum(heights)[0] / heights.size(); // Now find the rects from this binary mask. // This merges overlapping/touching CCs into a single component std::vector<cv::Rect> rects; cv::Rect last_rect(bb.x, bb.y, 1, bb.height); for (int i = 0; i < collide.size(); i++) { if (collide[i]) { last_rect.width += 1; } else { if (last_rect.width > 0) { rects.push_back(last_rect); } last_rect = cv::Rect(bb.x + i, bb.y, 0, bb.height); } } if (last_rect.width > 0) { rects.push_back(last_rect); } if (_verbose) std::cout << "#Rects: " << rects.size() << std::endl; if (rects.size() <= 2) { std::vector<cv::Rect> result; result.push_back(bb); return result; } // find the dists std::vector<float> dists; for (int i = 1; i < rects.size(); i++) { dists.push_back(rects[i].tl().x - rects[i-1].br().x); } // kmeans cv::Mat dist_mat(dists.size(), 1, CV_32FC1); for (size_t i = 0; i < dists.size(); i++) { dist_mat.at<float>(i,0) = dists[i]; } cv::Mat centers; cv::Mat labels;//(dists.size(),1, CV_32SC1, cv::Scalar(0)); /* float min = *std::min_element(dists.begin(), dists.end()); float max = *std::max_element(dists.begin(), dists.end()); for (size_t i = 0; i < dists.size(); i++) { labels.at<int>(i,0) = std::abs(dists[i] - min) < std::abs(dists[i] - max) ? 0 : 1; } */ if (_verbose) std::cout << dist_mat << std::endl; kmeans(dist_mat, 2, labels, cv::TermCriteria(CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 100, .01), 5, cv::KMEANS_PP_CENTERS, centers); if (_verbose) std::cout << centers << std::endl; std::vector<float> cpy(dists); std::sort(cpy.begin(), cpy.end()); float median = cpy[cpy.size() / 2]; if (cpy.size() % 2 == 0) { median = cpy[cpy.size() / 2] + cpy[cpy.size() / 2 - 1]; median = median / 2.0f; } float medval = median; float height = std::abs(centers.at<float>(0,0) - centers.at<float>(1,0)) / mean_height; median = std::abs(centers.at<float>(0,0) - centers.at<float>(1,0)) / (median + 1e-10); if (_verbose) { std::cout << dists.size() << " " << medval << " " << median << " " << height << std::endl; } // liblinear: 92% ACC: (10-F) // ./train -v 10 -B 1 -w1 2 -c 100 dists_cleaned.dat // do we have a single cluster?! //if (dists.size() > 3 && median * 0.84320891 + height * 0.3127415 < 1.23270849 || // dists.size() <= 3 && height < 0.43413942) { if (median * 0.33974138 + height * 0.47850904 < 0.56307525) { std::vector<cv::Rect> result; result.push_back(bb); return result; } // get the index of the smallest center int small_center = centers.at<float>(0,0) < centers.at<float>(1,0) ? 0 : 1; // count the distance to cluster assignments int cnt[2] = {0,0}; for (int i = 0; i < labels.rows; i++) { cnt[labels.at<int>(i,0)]++; } // we have more word gaps than letter gaps -> don't split! if (cnt[small_center] < cnt[1-small_center]) { std::vector<cv::Rect> result; result.push_back(bb); return result; } // start from left to right and iteratively merge rects if the // distance between them is clustered into the smaller center last_rect = rects[0]; std::vector<cv::Rect> word_candidates; for (int i = 1; i < rects.size(); i++) { if (_allow_single_letters) { if (labels.at<int>(i-1,0) == small_center) { // extend the last rect last_rect = last_rect | rects[i]; } else { // do not extend it! word_candidates.push_back(last_rect); last_rect = rects[i]; } } else { if (labels.at<int>(i-1,0) == small_center) { // extend the last rect last_rect = last_rect | rects[i]; } else if (i < labels.rows && labels.at<int>(i,0) == small_center) { // do not extend it! word_candidates.push_back(last_rect); last_rect = rects[i]; } else { last_rect = last_rect | rects[i]; } } } word_candidates.push_back(last_rect); // for each rect, find the original connected component rects std::vector<cv::Rect> words; for (cv::Rect candidate : word_candidates) { std::vector<cv::Rect> word; for (size_t i = 0; i < grp.ccs.size(); i++) { cv::Rect intersect(grp.ccs[i].rect & candidate); if (float (intersect.width * intersect.height) / float (grp.ccs[i].rect.width * grp.ccs[i].rect.height) >= 0.8f) { cv::Rect r = grp.ccs[i].rect; // set the text height correctly r.y = bb.y; r.height = bb.height; word.push_back(r); } } if (_verbose) { std::cout << "Accumulated: " << word.size() << " rects!" << std::endl; } if (word.empty()) continue; assert(!word.empty()); cv::Rect r = word[0]; for (size_t i = 1; i < word.size(); i++) { r = r | word[i]; } words.push_back(r); } return words; }