Das Ergebnis der Konturerkennung ist fragmentiert, wenn es nicht geschlossen wirdC++

Programme in C++. Entwicklerforum
Guest
 Das Ergebnis der Konturerkennung ist fragmentiert, wenn es nicht geschlossen wird

Post by Guest »

Ich habe ein Qt-Projekt mit opencv und c++. Es handelt sich um ein Konturerkennungsprogramm, das die Konturen einer handgezeichneten Skizze erkennt, sie einem Polygon annähert und die Ausgabe auf einem zweiten Etikett anzeigt. Es gibt übergeordnete und untergeordnete Konturen. Wenn eine Kontur keine untergeordneten Konturen hat, handelt es sich um eine übergeordnete Kontur ohne Löcher. Wenn das übergeordnete Element zufällig untergeordnete Konturen hat, bedeutet das, dass es Löcher enthält.
Mein Problem ist, dass die Ausgabe fragmentiert ist, wenn die Kontur offen ist (also das übergeordnete Element keine untergeordneten Elemente hat). Es sieht so aus, als ob einige Punkte nicht richtig erkannt wurden. Ich stelle sicher, dass die Argumente von „isClosed“ usw. richtig gesetzt sind, je nachdem, ob die Kontur geschlossen ist oder nicht. Aber das Problem bleibt bestehen.
Image

Wie Sie sehen können, befindet sich links die Eingabe, bei der es sich um eine schwarze handgezeichnete Skizze handelt. Wenn Sie die Maus loslassen, werden die Konturen der Zeichnung (noch nicht an ein Polygon angenähert) als Umrisse in Blau um die Formen herum angezeigt (Ausgaben wie gewünscht). Auf der rechten Seite werden die angenäherten Polygone der gezeichneten Formen angezeigt (auch ausgegeben). Wie Sie sehen können, wird die geschlossene Kontur, die wie eine „8“ aussieht, genau wie gewünscht angezeigt, die anderen drei „offenen“ Konturen werden jedoch alle fragmentiert in Hell-Dunkel-Linien dargestellt.
Basierend auf dem, was ich bisher recherchiert habe, war alles, was ich als Antwort erhalten habe, entweder den Epsilon-Wert in etwas Größeres ODER Kleineres zu manipulieren oder den Schwellenwert zu ändern. Bei mir hat keiner davon funktioniert.
Hier ist meine OpenCV-Klasse, die die Kontur erkennt (ScribbleWidget):

Code: Select all

// 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[i][2] != -1);

double epsilon = isClosed ? 0.002 * cv::arcLength(contours[i], true)
: 0.001 * cv::arcLength(contours[i], false);

cv::approxPolyDP(contours[i], 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[i];
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[i][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[i].x, contour[i].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[i];
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[i], 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

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post