You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
6.4 KiB
C

1 month ago
#pragma once
#include "GSurface.h"
#include "clipper2\clipper.h"
using Contour = std::vector<cv::Point2f>;
using Contours = std::vector<Contour>;
class ContourUtils
{
public:
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD>
static void SmoothContour(Contours& contours, const Contour& border, std::vector<cv::Vec4i> m_hierarchy, int nTimes)
{
int N = contours.size();
for (int i = 0; i < N; i++)
{
//һ<><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (-1 == m_hierarchy[i][3])
SmoothRootContour(contours[i], border, nTimes);
else
SmoothContour(contours[i], nTimes);
}
}
//ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>,<2C><><EFBFBD>ܲ<EFBFBD><DCB2><EFBFBD><EFBFBD>ڱ߽<DAB1><DFBD><EFBFBD>
static void SmoothRootContour(Contour& inputContour, const Contour& border, int nTimes)
{
if (nTimes < 1 || inputContour.size() < 4)
return;
TrimContour(inputContour, nTimes);
int N = inputContour.size();
//<2F><><EFBFBD><EFBFBD>inputContour<75>ڱ߽<DAB1><DFBD>ϵIJ<CFB5><C4B2><EFBFBD>
int istart = 0;
int iend = 0;
int i = 0;
std::vector<cv::Point2f> subline;
while (i < N)
{
if (!isPtOnBorder(inputContour[i], border)) //<2F>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD>ڱ߽<DAB1><DFBD>ϵ<EFBFBD><CFB5>׵<EFBFBD>
{
istart = i;
for (i = istart + 1; i < N; i++)
{
if (N - 1 == i)
{
iend = N - 1;
break;
}
else if (isPtOnBorder(inputContour[i], border))
{
iend = i - 1;
break;
}
}
if (istart > 0)
istart--;
if (iend < N - 1)
iend++;
if (iend - istart > 3) //<2F><><EFBFBD><EFBFBD>5<EFBFBD><35><EFBFBD><EFBFBD>
{
//ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD>ڱ߽<DAB1><DFBD><EFBFBD><EFBFBD>ϵľֲ<C4BE><D6B2><EFBFBD><EFBFBD><EFBFBD>[istart,iend]
subline.clear();
subline.reserve(iend - istart + 1);
for (int k = istart; k <= iend; k++)
subline.push_back(inputContour[k]);
SmoothContour(subline, nTimes);
int t = 0;
int nMax = subline.size();
for (int k = istart; k <= iend && t < nMax; k++)
{
inputContour[k] = subline[t];
t++;
}
}
}
i++;
}
}
// ȥ<><C8A5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
static void TrimContour(Contour& inputContour,int times)
{
using namespace Clipper2Lib;
double dScale = 100000;
Path64 sourcePath;
for (cv::Point2f item : inputContour) {
sourcePath.push_back(Point64((int64_t)(item.x*dScale), (int64_t)(item.y*dScale)));
}
int nSmoothScale = (int)(times*0.333);
if (nSmoothScale < 1) {
nSmoothScale = 1;
}
Path64 destPath = TrimCollinear(sourcePath, nSmoothScale);
inputContour.clear();
// <20><><EFBFBD><EFBFBD><EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD>
inputContour.reserve(destPath.size()); // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><D0A7>
for (Point64 pt : destPath) {
inputContour.push_back(cv::Point2f(pt.x / dScale, pt.y / dScale));
}
}
//<2F><><EFBFBD>Ƿ<EFBFBD><C7B7>ڱ߽<DAB1><DFBD><EFBFBD><EFBFBD><EFBFBD>
static bool isPtOnBorder(const cv::Point &pt, const Contour& border)
{
for (auto& p : border)
{
if (fabs(pt.x - p.x) < 0.5 && fabs(pt.y - p.y) < 0.5)
return true;
}
return false;
}
//ƽ<><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
static void SmoothContour(Contour& inputContour, int nTimes)
{
TrimContour(inputContour, nTimes);
if (nTimes < 1 || inputContour.size() < 4)
return;
int N = inputContour.size();
double* x = new double[N];
double* y = new double[N];
double tx, ty;
for (int i = 0; i < N; i++)
{
tx = inputContour[i].x;
ty = inputContour[i].y;
x[i] = tx;
y[i] = ty;
}
if (N > 4 && nTimes > 0)
{
Smooth53(x, N, nTimes);
Smooth53(y, N, nTimes);
}
for (int i = 0; i < N; i++)
{
inputContour[i].x = x[i];
inputContour[i].y = y[i];
}
delete[]x;
delete[]y;
}
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD> n <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>4
static bool Smooth53(double* val, int n, int stimes)
{
if (n < 5 || stimes < 1) return false;
int i, k; //kΪƽ<CEAA><C6BD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
double *xx = new double[n];
for (k = 0; k < stimes; k++)
{
xx[0] = 69 * val[0] + 4 * val[1] - 6 * val[2] + 4 * val[3] - val[4];
xx[0] = xx[0] / 70;
xx[1] = 2 * val[0] + 27 * val[1] + 12 * val[2] - 8 * val[3];
xx[1] = (xx[1] + 2 * val[4]) / 35;
for (i = 2; i <= n - 3; i++)
{
xx[i] = -3 * val[i - 2] + 12 * val[i - 1] + 17 * val[i];
xx[i] = (xx[i] + 12 * val[i + 1] - 3 * val[i + 2]) / 35;
}
xx[n - 2] = 2 * val[n - 5] - 8 * val[n - 4] + 12 * val[n - 3];
xx[n - 2] = (xx[n - 2] + 27 * val[n - 2] + 2 * val[n - 1]) / 35;
xx[n - 1] = -val[n - 5] + 4 * val[n - 4] - 6 * val[n - 3];
xx[n - 1] = (xx[n - 1] + 4 * val[n - 2] + 69 * val[n - 1]) / 70;
for (int i = 0; i < n; i++)
val[i] = xx[i];
}
delete[]xx;
return true;
}
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
static bool WriteContours(const Contours& contours, const char* path)
{
FILE* fw = fopen(path, "w");
if (nullptr == fw)
return false;
float x, y;
for (int j = 0; j < contours.size(); j++)
{
if (contours[j].size() < 3)
continue;
fprintf(fw, "Pline.%d\n", j);
for (auto& p : contours[j])
{
x = p.x;
y = p.y;
fprintf(fw, "%.12g,%.12g\n", x, y);
}
auto p = contours[j][0];
x = p.x;
y = p.y;
fprintf(fw, "%.12g,%.12g\n", x, y);
fprintf(fw, "\n");
}
fclose(fw);
return true;
}
static char* WriteContours(const Contours& contours)
{
// <20><>ʼ<EFBFBD><CABC>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>С<EFBFBD>Ļ<EFBFBD><C4BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С
size_t initial_capacity = 1024; // <20><>ʼ<EFBFBD><CABC><EFBFBD>Դ<EFBFBD>8<EFBFBD><38><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char* buffer = new char[initial_capacity]();
size_t capacity = initial_capacity-1; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>С
size_t data_size = 0;
const char* newline = "\r\n"; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>з<EFBFBD>
size_t newline_size = std::strlen(newline); // <20><><EFBFBD>з<EFBFBD><D0B7><EFBFBD><EFBFBD>ֽڴ<D6BD>С
// <20><>̬<EFBFBD><CCAC>ÿ<EFBFBD><C3BF>Point<6E><74><EFBFBD><EFBFBD>д<EFBFBD><EFBFBD><EBBBBA><EFBFBD><EFBFBD>
//float x, y;
for (int i = 0; i < contours.size(); i++)
{
// <20><><EFBFBD><EFBFBD>"pline <<3C><><EFBFBD><EFBFBD>>\n"<22>ַ<EFBFBD><D6B7><EFBFBD>
std::ostringstream oss;
oss << "Pline." << (i + 1) << "\r\n"; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
std::string pline_with_number = oss.str();
writeString2Buffer(pline_with_number.c_str(), capacity, data_size, buffer);
// д<><D0B4><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD>
for (auto& p : contours[i])
{
// <20><><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(x, y)д<><EFBFBD><EBBBBA><EFBFBD><EFBFBD>
std::ostringstream oss;
oss << std::fixed << std::setprecision(6); // <20><><EFBFBD>ñ<EFBFBD><C3B1><EFBFBD>С<CEBB><D0A1>
oss << p.x << "," << p.y << "\r\n"; // ת<><D7AA>x<EFBFBD><78><79>ַ<EFBFBD><D6B7><EFBFBD>
std::string point_str = oss.str();
writeString2Buffer(point_str.c_str(), capacity, data_size, buffer);
}
// д<><EFBFBD>з<EFBFBD>
writeString2Buffer(newline, capacity, data_size, buffer);
}
return buffer; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>յ<EFBFBD><D5B5><EFBFBD><EFBFBD>л<EFBFBD><D0BB><EFBFBD><EFBFBD><EFBFBD>
}
static void writeString2Buffer(const char* src, size_t &capacity, size_t& dataSize, char * &buffer)
{
size_t sizeSrc = std::strlen(src);
size_t required_size = dataSize + sizeSrc;
if (required_size >= capacity)
{
size_t new_capacity = capacity * 2;
char* new_buffer = new char[new_capacity + 1]();
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݸ<EFBFBD><DDB8>Ƶ<EFBFBD><C6B5>»<EFBFBD><C2BB><EFBFBD><EFBFBD><EFBFBD>
std::memcpy(new_buffer, buffer, dataSize);
// <20>ͷžɻ<C5BE><C9BB><EFBFBD><EFBFBD><EFBFBD>
delete[] buffer;
buffer = new_buffer;
capacity = new_capacity;
}
std::memcpy(buffer+ dataSize, src, sizeSrc);
dataSize += sizeSrc;
}
};