Моя проблема в том, что если контур открыт (то есть у родителя нет дочерних элементов), выходные данные имеют фрагментированный посмотрите, как будто некоторые моменты не правильно распознаны. Я проверяю, что аргументы «isClosed» и т. д. установлены правильно в зависимости от того, закрыт контур или нет. Но проблема остается.

Как видите, слева находится ввод, представляющий собой черную нарисованную от руки каракулю. Когда мышь отпущена, контуры рисунка (еще не приближенные к многоугольнику) отображаются вокруг фигур в виде синих контуров (выходные данные по желанию). В правой части показаны аппроксимированные полигоны нарисованных фигур (тоже выходные данные). Как видите, замкнутый контур, похожий на цифру «8», отображается именно так, как хотелось бы, а вот остальные три «открытых» контура показаны в виде обрезанных линий, фрагментированы.
Основываясь на том, что я исследовал до сих пор, все, что я получил в качестве ответа, это либо манипулировать значением эпсилона, превращая его в нечто большее ИЛИ меньше, либо изменить пороговое значение. Мне ничего не помогло.
Вот мой класс opencv, который определяет контур (ScribbleWidget):
// priv
class ScribbleWidgetPriv
{
friend ScribbleWidget;
public:
explicit ScribbleWidgetPriv(ScribbleWidget* widget);
~ScribbleWidgetPriv();
QPair separateParentChildPaths(const std::vector& contours, const std::vector& hierarchy);
signals:
void mapExtracted(std::map contourMap);
private:
ScribbleWidget* _widget = nullptr;
cv::Mat _canvas;
QPoint _lastPoint;
};
ScribbleWidgetPriv::ScribbleWidgetPriv(ScribbleWidget* widget)
{
_widget = widget;
}
ScribbleWidgetPriv::~ScribbleWidgetPriv()
{
}
// end of priv
ScribbleWidget::ScribbleWidget(QWidget *parent)
: QLabel(parent), _drawing(false), _penWidth(20)
{
_priv = new ScribbleWidgetPriv(this);
_priv->_canvas = cv::Mat::zeros(600, 800, CV_8UC3);
_priv->_canvas.setTo(cv::Scalar(255, 255, 255)); // Initialize the drawing canvas as white
updateDisplay(); // Initially shows a white empty canvas
}
void ScribbleWidget::mousePressEvent(QMouseEvent *event) {
if ( event->button() == Qt::LeftButton ) {
_drawing = true;
_priv->_lastPoint = event->pos();
}
}
void ScribbleWidget::mouseMoveEvent(QMouseEvent *event) {
if ( _drawing && (event->buttons() & Qt::LeftButton) ) {
cv::Point currentPoint(event->pos().x(), event->pos().y());
cv::line(_priv->_canvas, cv::Point(_priv->_lastPoint.x(), _priv->_lastPoint.y()), currentPoint, cv::Scalar(0, 0, 0), _penWidth, cv::LINE_AA);
_priv->_lastPoint = event->pos();
updateDisplay();
}
}
void ScribbleWidget::mouseReleaseEvent(QMouseEvent *event) {
if ( event->button() == Qt::LeftButton ) {
_drawing = false;
emit drawingFinished(_priv->_canvas); // Pass the current canvas to MainWindow
}
}
void ScribbleWidget::updateDisplay() {
QImage displayImage(_priv->_canvas.data, _priv->_canvas.cols, _priv->_canvas.rows, _priv->_canvas.step, QImage::Format_RGB888);
setPixmap(QPixmap::fromImage(displayImage.rgbSwapped()));
}
cv::Mat ScribbleWidget::generateContoursAndOverlay(const cv::Mat &inputImage, int threshold) {
cv::Mat gray, edges, contourImage, resultImage;
// Convert the input image to grayscale
cv::cvtColor(inputImage, gray, cv::COLOR_BGR2GRAY);
// Apply Canny edge detection
cv::Canny(gray, edges, threshold, threshold * 2);
// Create an empty canvas for contours (ensure the result canvas is blank)
contourImage = cv::Mat::zeros(inputImage.size(), CV_8UC3); // Blank canvas for contours
// Find contours and hierarchy
std::vector contours;
std::vector hierarchy;
cv::findContours(edges, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
// for QPainterPath:
QPair paths = _priv->separateParentChildPaths(contours, hierarchy);
organizeContours(contours, hierarchy);
// Create a result canvas by copying the original image (which is the drawing)
resultImage = inputImage.clone();
// Iterate through contours and approximate polygons
for (size_t i = 0; i < contours.size(); ++i) {
std::vector approxPolygon;
// Check if the contour is closed or open based on the hierarchy
bool isClosed = (hierarchy[2] != -1);
double epsilon = isClosed ? 0.002 * cv::arcLength(contours, true)
: 0.001 * cv::arcLength(contours, false);
cv::approxPolyDP(contours, approxPolygon, epsilon, isClosed);
// Draw the approximated polygon (only the contour lines)
cv::polylines(contourImage, std::vector{approxPolygon}, isClosed, cv::Scalar(255, 0, 0), 4);
}
cv::addWeighted(resultImage, 1.0, contourImage, 1.0, 0.0, resultImage); // Overlay contours on top
return resultImage;
QPair ScribbleWidgetPriv::separateParentChildPaths(const std::vector& contours, const std::vector& hierarchy)
{
QList parentPaths;
QList childPaths;
for ( size_t i = 0; i < contours.size(); ++i ) {
// Convert the current contour to a QPainterPath
const auto& contour = contours;
QPainterPath path;
if ( !contour.empty() ) {
path.moveTo(contour[0].x, contour[0].y);
for ( size_t j = 1; j < contour.size(); ++j ) {
path.lineTo(contour[j].x, contour[j].y);
}
path.closeSubpath();
}
// Use hierarchy to classify as parent or child
if ( hierarchy[3] == -1 ) {
// No parent, so it's a top-level (parent) contour
parentPaths.append(path);
}
else {
// Has a parent, so it's a nested (child) contour
childPaths.append(path);
}
}
return {parentPaths, childPaths};
}
QPainterPath ScribbleWidget::contourToPainterPath( const std::vector& contour )
{
QPainterPath path;
if (!contour.empty()) {
path.moveTo(contour[0].x, contour[0].y);
// Add all the points in the contour
for (size_t i = 1; i < contour.size(); ++i) {
path.lineTo(contour.x, contour.y);
}
// Check if the contour is closed (first and last points are the same)
bool isClosed = (contour[0] == contour[contour.size() - 1]);
// Only close the path if it's a closed contour
if (isClosed) {
path.closeSubpath(); // Close the path explicitly if it’s closed
}
}
return path;
}
void ScribbleWidget::organizeContours(
const std::vector& contours,
const std::vector& hierarchy)
{
std::map contourMap;
for (size_t i = 0; i < hierarchy.size(); ++i) {
const cv::Vec4i& h = hierarchy;
int parentIdx = h[3]; // The index of the parent contour
std::vector approxPolygon;
bool hasChildren = (h[2] != -1); // Check if the contour has children
bool isClosed = hasChildren; // Treat contours with children as closed
// Adjust epsilon for open vs closed contours
double epsilon = isClosed ? 0.002 * cv::arcLength(contours, true)
: 0.001 * cv::arcLength(contours[i], false);
// Approximate polygon based on open or closed status
if (isClosed) {
cv::approxPolyDP(contours[i], approxPolygon, epsilon, true);
} else {
// Approximate the smoothed open contour
cv::approxPolyDP(contours[i], approxPolygon, epsilon, false);
}
QPainterPath currentPath;
QPolygonF polygon;
for (const auto& pt : approxPolygon) {
polygon
Подробнее здесь: https://stackoverflow.com/questions/793 ... not-closed