From 1c2ed2876f68188abea6c9805e8d30b6d9972820 Mon Sep 17 00:00:00 2001 From: Dizhenin Vlad <39303687+SimpleVlad@users.noreply.github.com> Date: Mon, 16 Mar 2020 17:10:25 +0300 Subject: [PATCH] Merge pull request #16695 from SimpleVlad:intelligent_scissors Intelligent scissors * Start * Remove whitespace * Re onMouse * replased double to float * Draw contours * CV_FILLED -> FILLED * Remove line 210 * Change 'about' * Remove M_PI * Remove warning * CP_PI * double to float * CV_PI to Float * Add struct for data * line 172, 191 whitespace * Change name * Fix Warnings * Set const * line 180 * rewrite keys * &img = param->img --- samples/cpp/intelligent_scissors.cpp | 232 +++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 samples/cpp/intelligent_scissors.cpp diff --git a/samples/cpp/intelligent_scissors.cpp b/samples/cpp/intelligent_scissors.cpp new file mode 100644 index 0000000..6141c1f --- /dev/null +++ b/samples/cpp/intelligent_scissors.cpp @@ -0,0 +1,232 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cv; +struct Pix +{ + Point next_point; + double cost; + + bool operator > (const Pix &b) const + { + return cost > b.cost; + } +}; + +struct Parameters +{ + Mat img, img_pre_render, img_render; + Point end; + std::vector > contours; + std::vector tmp_contour; + Mat zero_crossing, gradient_magnitude, Ix, Iy, hit_map_x, hit_map_y; +}; + + +static float local_cost(const Point& p, const Point& q, const Mat& gradient_magnitude, const Mat& Iy, const Mat& Ix, const Mat& zero_crossing) +{ + float fG = gradient_magnitude.at(q.y, q.x); + float dp; + float dq; + const float WEIGHT_LAP_ZERO_CROSS = 0.43f; + const float WEIGHT_GRADIENT_MAGNITUDE = 0.14f; + const float WEIGHT_GRADIENT_DIRECTION = 0.43f; + bool isDiag = (p.x != q.x) && (p.y != q.y); + + if ((Iy.at(p) * (q.x - p.x) - Ix.at(p) * (q.y - p.y)) >= 0) + { + dp = Iy.at(p) * (q.x - p.x) - Ix.at(p) * (q.y - p.y); + dq = Iy.at(q) * (q.x - p.x) - Ix.at(q) * (q.y - p.y); + } + else + { + dp = Iy.at(p) * (p.x - q.x) + (-Ix.at(p)) * (p.y - q.y); + dq = Iy.at(q) * (p.x - q.x) + (-Ix.at(q)) * (p.y - q.y); + } + if (isDiag) + { + dp /= sqrtf(2); + dq /= sqrtf(2); + } + else + { + fG /= sqrtf(2); + } + return WEIGHT_LAP_ZERO_CROSS * zero_crossing.at(q) + + WEIGHT_GRADIENT_DIRECTION * (acosf(dp) + acosf(dq)) / static_cast(CV_PI) + + WEIGHT_GRADIENT_MAGNITUDE * fG; +} + +static void find_min_path(const Point& start, Parameters* param) +{ + Pix begin; + Mat &img = param->img; + Mat cost_map(img.size(), CV_32F, Scalar(FLT_MAX)); + Mat expand(img.size(), CV_8UC1, Scalar(0)); + Mat processed(img.size(), CV_8UC1, Scalar(0)); + Mat removed(img.size(), CV_8UC1, Scalar(0)); + std::priority_queue < Pix, std::vector, std::greater > L; + + cost_map.at(start) = 0; + processed.at(start) = 1; + begin.cost = 0; + begin.next_point = start; + + L.push(begin); + while (!L.empty()) + { + Pix P = L.top(); + L.pop(); + Point p = P.next_point; + processed.at(p) = 0; + if (removed.at(p) == 0) + { + expand.at(p) = 1; + for (int i = -1; i <= 1; i++) + { + for(int j = -1; j <= 1; j++) + { + int tx = p.x + i; + int ty = p.y + j; + if (tx < 0 || tx >= img.cols || ty < 0 || ty >= img.rows) + continue; + if (expand.at(ty, tx) == 0) + { + Point q = Point(tx, ty); + float cost = cost_map.at(p) + local_cost(p, q, param->gradient_magnitude, param->Iy, param->Ix, param->zero_crossing); + if (processed.at(q) == 1 && cost < cost_map.at(q)) + { + removed.at(q) = 1; + } + if (processed.at(q) == 0) + { + cost_map.at(q) = cost; + param->hit_map_x.at(q)= p.x; + param->hit_map_y.at(q) = p.y; + processed.at(q) = 1; + Pix val; + val.cost = cost_map.at(q); + val.next_point = q; + L.push(val); + } + } + } + } + } + } +} + + +static void onMouse(int event, int x, int y, int , void* userdata) +{ + Parameters* param = reinterpret_cast(userdata); + Point &end = param->end; + std::vector > &contours = param->contours; + std::vector &tmp_contour = param->tmp_contour; + Mat &img_render = param->img_render; + Mat &img_pre_render = param->img_pre_render; + + if (event == EVENT_LBUTTONDOWN) + { + end = Point(x, y); + if (!contours.back().empty()) + { + for (int i = static_cast(tmp_contour.size()) - 1; i >= 0; i--) + { + contours.back().push_back(tmp_contour[i]); + } + tmp_contour.clear(); + } + else + { + contours.back().push_back(end); + } + find_min_path(end, param); + + img_render.copyTo(img_pre_render); + imshow("lasso", img_render); + } + else if (event == EVENT_RBUTTONDOWN) + { + img_pre_render.copyTo(img_render); + drawContours(img_pre_render, contours, static_cast(contours.size()) - 1, Scalar(0,255,0), FILLED); + addWeighted(img_pre_render, 0.3, img_render, 0.7, 0, img_render); + contours.resize(contours.size() + 1); + imshow("lasso", img_render); + } + else if (event == EVENT_MOUSEMOVE && !contours.back().empty()) + { + tmp_contour.clear(); + img_pre_render.copyTo(img_render); + Point val_point = Point(x, y); + while (val_point != end) + { + tmp_contour.push_back(val_point); + Point cur = Point(param->hit_map_x.at(val_point), param->hit_map_y.at(val_point)); + line(img_render, val_point, cur, Scalar(255, 0, 0), 2); + val_point = cur; + } + imshow("lasso", img_render); + } +} + +const char* keys = +{ + "{help h | |}" + "{@image | fruits.jpg| Path to image to process}" +}; + + +int main( int argc, const char** argv ) +{ + Parameters param; + const int EDGE_THRESHOLD_LOW = 50; + const int EDGE_THRESHOLD_HIGH = 100; + CommandLineParser parser(argc, argv, keys); + parser.about("\nThis program demonstrates implementation of 'intelligent scissors' algorithm\n" + "To start drawing a new contour select a pixel, click LEFT mouse button.\n" + "To fix a path click LEFT mouse button again.\n" + "To finish drawing a contour click RIGHT mouse button.\n"); + if (parser.has("help")) + { + parser.printMessage(); + return 1; + } + std::vector > c(1); + param.contours = c; + std::string filename = parser.get(0); + + Mat grayscale, img_canny; + param.img = imread(samples::findFile(filename)); + param.hit_map_x.create(param.img.rows, param.img.cols, CV_32SC1); + param.hit_map_y.create(param.img.rows, param.img.cols, CV_32SC1); + + cvtColor(param.img, grayscale, COLOR_BGR2GRAY); + Canny(grayscale, img_canny, EDGE_THRESHOLD_LOW, EDGE_THRESHOLD_HIGH); + + threshold(img_canny, param.zero_crossing, 254, 1, THRESH_BINARY_INV); + Sobel(grayscale, param.Ix, CV_32FC1, 1, 0, 1); + Sobel(grayscale, param.Iy, CV_32FC1, 0, 1, 1); + param.Ix.convertTo(param.Ix, CV_32F, 1.0/255); + param.Iy.convertTo(param.Iy, CV_32F, 1.0/255); + + // Compute gradients magnitude. + double max_val = 0.0; + magnitude(param.Iy, param.Ix, param.gradient_magnitude); + minMaxLoc(param.gradient_magnitude, 0, &max_val); + param.gradient_magnitude.convertTo(param.gradient_magnitude, CV_32F, -1/max_val, 1.0); + + param.img.copyTo(param.img_pre_render); + param.img.copyTo(param.img_render); + + namedWindow("lasso"); + setMouseCallback("lasso", onMouse, ¶m); + imshow("lasso", param.img); + waitKey(0); +} -- 2.7.4