[C++/CLI] エッジ画像をポリラインへ変換

```CPP
#pragma once

#ifdef _DEBUG
#pragma comment(lib, "..\\build\\x64\\vc15\\lib\\opencv_world440d.lib")
#else
#pragma comment(lib, "..\\build\\x64\\vc15\\lib\\opencv_world440.lib")
#endif

#include <opencv2\opencv.hpp>

using namespace System;

namespace OpenCvCppLib 
{
#pragma managed(push, off)
	private class Unmanaged
	{
	public:
		static std::vector<cv::Mat_<cv::Point>> edges2polylines(cv::Mat1b edges, float th_n, float th_c)
		{
			auto h = edges.rows;
			auto w = edges.cols;
			auto edges_ = edges.clone();

			th_c = (th_c != 0) ? th_c : cv::pow(th_n, 2);

			auto edgepoints = get_edgepoints(edges_, cv::Point(0, 0));
			std::vector<cv::Mat_<cv::Point>> polylines;

			while (edgepoints.total() != 0)
			{
				cv::Mat_<cv::Point> polyline;
				auto point = edgepoints(0);
				polyline.push_back(point);
				edges_(point.y, point.x) = 0;

				auto reversed = false;
				while (true)
				{
					auto current = polyline.end()[-1];

					auto row1 = (current.y - th_n < 0) ? 0 : current.y - th_n;
					auto col1 = (current.x - th_n < 0) ? 0 : current.x - th_n;
					auto row2 = (current.y + th_n > h) ? h : current.y + th_n;
					auto col2 = (current.x + th_n > w) ? w : current.x + th_n;
					auto roi = edges_(cv::Rect(col1, row1, col2 - col1, row2 - row1)); // ROI
					auto neighbor = get_edgepoints(roi, cv::Point(col1, row1));

					if (neighbor.total() == 0)
					{
						if (reversed == false)
						{
							std::reverse(polyline.begin(), polyline.end());
							reversed = true;
							continue;
						}
						else
						{
							break;
						}
					}

					cv::Point nearest;

					if (polyline.total() == 1)
					{
						nearest = *std::min_element(neighbor.begin(), neighbor.end()
							, [&](auto a, auto b) -> int
							{
								return (distance(a, current) < distance(b, current));
							});
					}
					else
					{
						auto d = (cv::Point2f)(current - polyline.end()[-2]) + 0.47 * (cv::Point2f)(polyline.end()[-2] - polyline.end()[-3]);
						d /= cv::norm(d);

						nearest = *std::min_element(neighbor.begin(), neighbor.end()
							, [&](auto a, auto b) -> int
							{
								return (cost_function(a, current, d) < cost_function(b, current, d));
							});

						if (cost_function(nearest, current, d) > th_c) {
							if (reversed == false)
							{
								std::reverse(polyline.begin(), polyline.end());
								reversed = true;
								continue;
							}
							else
							{
								break;
							}
						}
					}

					polyline.push_back(nearest);
					edges_(nearest.y, nearest.x) = 0;
				}

				polylines.push_back(polyline);
				edgepoints = get_edgepoints(edges_, cv::Point(0, 0));
			}

			return polylines;
		}

		static cv::Mat_<cv::Point> get_edgepoints(cv::Mat1b edges, cv::Point offset)
		{
			cv::Mat edgepoints;
			cv::findNonZero(edges, edgepoints);

			if (edgepoints.total() != 0 && (offset != cv::Point(0, 0)))
			{
				cv::Mat planes[2];
				cv::split(edgepoints, planes);
				if (offset.x != 0) planes[0] += offset.x; // col
				if (offset.y != 0) planes[1] += offset.y; // row					
				cv::merge(planes, 2, edgepoints);
			}

			return edgepoints;
		}

		static float distance(cv::Point a, cv::Point b)
		{
			return cv::norm(a - b);
		}

		static float cost_function(cv::Point p, cv::Point line_end, cv::Point2f d)
		{
			cv::Point2f r = p - line_end;
			auto r_norm = cv::sqrt(r.dot(r));
			return cv::pow(r_norm, 2) - 0.99 * r.dot(d);
		}
	};
#pragma managed(pop)

	public ref class OpenCvCppClass
	{
	public:
		static void gamma(cv::InputArray image, cv::OutputArray dest, double gamma)
		{
			cv::Mat1b table(1, 256);
			for (int i = 0; i < 256; i++) {
				table(i) = cv::pow(i / 255.0, gamma) * 255.0;
			}
			cv::LUT(image, table, dest);
		}

		static void Run()
		{
			auto img = cv::imread(".\\businessman.png", cv::IMREAD_GRAYSCALE);

			gamma(img, img, 1.5);

			cv::Mat edges;
			cv::Canny(img, edges, 100, 200);

			auto polylines = Unmanaged::edges2polylines(edges, 6, 0);

			cv::Mat output;
			cvtColor(edges, output, cv::COLOR_GRAY2BGR);

			Random random;
			for (auto n : polylines) {
				auto color = cv::Scalar(random.Next(64, 255), random.Next(64, 255), random.Next(64, 255));
				cv::polylines(output, n, false, color, 2);
			}

			cv::imshow("output", output);
		}
	};
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/7b9eb24c-e6a1-df13-ddbc-3394fa3c6928.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/662099/9f8c72a3-b844-7a45-c180-f60d2bb4de66.png)

・以下のサイトを参考にさせていただきました。
[python+OpenCVで検出した画像のエッジを曲線(折れ線)に変換する[Python3] @s-col](https://qiita.com/s-col/items/115b7f7d80133f89359d)