296 const cv::Mat &image,
const cv::Mat &mask,
float black_point,
298 constexpr int MAXIMUM_WORK_DIMENSION = 512;
299 const int image_max_dimension = std::max(image.cols, image.rows);
300 const double work_scale =
301 image_max_dimension > MAXIMUM_WORK_DIMENSION
302 ?
static_cast<double>(MAXIMUM_WORK_DIMENSION) /
305 const cv::Size work_size(
306 std::max(1, cvRound(image.cols * work_scale)),
307 std::max(1, cvRound(image.rows * work_scale)));
310 if (mask.channels() == 1) {
311 mask.convertTo(soft, CV_32F,
312 mask.depth() == CV_8U ? 1.0 / 255.0 : 1.0);
315 cv::cvtColor(mask, gray, cv::COLOR_BGR2GRAY);
316 gray.convertTo(soft, CV_32F,
317 gray.depth() == CV_8U ? 1.0 / 255.0 : 1.0);
319 if (soft.size() != work_size) {
320 cv::resize(soft, soft, work_size, 0, 0, cv::INTER_LINEAR);
322 cv::threshold(soft, soft, 1.0, 1.0, cv::THRESH_TRUNC);
323 cv::threshold(soft, soft, 0.0, 0.0, cv::THRESH_TOZERO);
326 cv::threshold(soft,
binary, 0.5F, 255.0F, cv::THRESH_BINARY);
328 const auto scaled_kernel_size = [work_scale](
int full_size) {
329 int size = std::max(1, cvRound(full_size * work_scale));
330 if ((size & 1) == 0) {
335 const int open_size = scaled_kernel_size(3);
336 const int close_size = scaled_kernel_size(7);
337 const int erode_size = scaled_kernel_size(3);
339 const cv::Mat kernel = cv::getStructuringElement(
340 cv::MORPH_ELLIPSE, cv::Size(open_size, open_size));
341 cv::morphologyEx(
binary,
binary, cv::MORPH_OPEN, kernel);
343 if (close_size > 1) {
344 const cv::Mat kernel = cv::getStructuringElement(
345 cv::MORPH_ELLIPSE, cv::Size(close_size, close_size));
346 cv::morphologyEx(
binary,
binary, cv::MORPH_CLOSE, kernel);
352 const int label_count = cv::connectedComponentsWithStats(
353 binary, labels, stats, centroids, 8, CV_32S);
354 if (label_count > 1) {
357 for (
int label = 1; label < label_count; ++label) {
358 const int area = stats.at<
int>(label, cv::CC_STAT_AREA);
359 if (area > best_area) {
364 const int minimum_area =
366 if (best_label > 0 && best_area >= minimum_area) {
367 cv::compare(labels, best_label,
binary, cv::CMP_EQ);
370 if (erode_size > 1) {
371 const cv::Mat kernel = cv::getStructuringElement(
372 cv::MORPH_ELLIPSE, cv::Size(erode_size, erode_size));
377 binary.convertTo(silhouette, CV_32F, 1.0 / 255.0);
378 cv::multiply(soft, silhouette, soft);
379 cv::GaussianBlur(soft, soft, cv::Size(), 1.2 * work_scale);
382 std::max(white_point - black_point, 1.0e-6F);
383 soft.convertTo(soft, CV_32F, 1.0F / range,
384 -black_point / range);
385 cv::threshold(soft, soft, 1.0, 1.0, cv::THRESH_TRUNC);
386 cv::threshold(soft, soft, 0.0, 0.0, cv::THRESH_TOZERO);
387 cv::pow(soft, 1.6, soft);
388 if (soft.size() != image.size()) {
389 cv::resize(soft, soft, image.size(), 0, 0, cv::INTER_LINEAR);