استخدام مكتبة OpenCV للتعرف على الأقواس الإهليلجية في أقسام ثنائية الأبعاد من السحب ثلاثية الأبعاد

فيما يتعلق بانتشار الماسحات الضوئية الليزرية الميسورة التكلفة (lidars) القادرة على استقبال السحب النقطية ثلاثية الأبعاد ( 3DOT ) والتطبيق الأوسع لهذه التقنية في مجالات مختلفة (من الهندسة الميكانيكية إلى الأمن ، من صناعة النفط إلى الهندسة المعمارية) ، فقد انتعش الاهتمام بمعالجة الخوارزميات غيوم من النقاط.

أحد التطبيقات الشائعة لـ 3d في الصناعة هو إنشاء وثائق التصميم للمعدات المنشأة أو القديمة أو المحولة فقط ، والتي تتكون عادة من خطوط الأنابيب والهياكل الأخرى للهندسة الأسطوانية.

للكشف عن الأوليات الهندسية في 3DOT ، يتم استخدام المكتبات ثلاثية الأبعاد المتخصصة ، على سبيل المثال ، Microsoft PCL. النهج مع استخدام المكتبات الجاهزة ، إلى جانب المزايا ، له عيوب. على سبيل المثال ، من الصعب دمجها في مخططات معالجة Kadov الحالية ، والتي عادة ما يكون لها بعد ثنائي الأبعاد.

دعونا نفكر كيف سيكون من الممكن معالجة 3dOT ، على سبيل المثال ، محطة ضخ ، بدءًا من أقسام ثنائية الأبعاد واستخدام ترسانة كاملة من المعالجة ثنائية الأبعاد ، والتي تتوفر في مكتبات معالجة الصور الموثوقة والمحسنة ، على سبيل المثال OpenCV .


الشكل 1. نموذج 3D OT لمحطة ضخ

العنصر الرئيسي للأقسام التي تم الحصول عليها عن طريق مسح هياكل الأنابيب المختلفة هو الأقواس الإهليلجية .


الشكل 2: المقطع العرضي الأفقي لنموذج ثلاثي الأبعاد لمحطة ضخ بمستوى متوسط.

بالنسبة لهذه المقالة ، نقتصر في اعتبارنا على خوارزمية رئيسية واحدة تسمح لنا باكتشاف الأقواس الإهليلجية التعسفية - هذه خوارزمية تكرارية لنمو أجزاء القوس وربط المنطقة ( نمو المنطقة وربط الحافة ).

خوارزميات النمو هي الأكثر وضوحًا والتي يمكن التحقق منها بسهولة ، وإن كانت تستغرق وقتًا طويلاً مقارنة بالخوارزميات الإحصائية ، والتي تكون مناسبة بشكل أفضل للحالة عندما يحتوي المشهد على أشياء بعيدة مقرونة بشكل فضفاض تنتمي إلى القطع الناقص. ستتم مناقشة هذه الخوارزميات في المقالات المستقبلية.


في الوقت الحالي ، من أجل البساطة ، نحذف إجراء الحصول على قسم من ملف 3dOT المصدر ، ومعالجة قسم ما ، وتجميعه لعزل الأوليات الهندسية ، وكذلك عمليات الربط والتصحيح والعمليات التصويرية الأخرى اللازمة للحصول على معلمات النموذج. لن نناقش تحديد معايير خوارزميات البحث الإرشادية بنفس الطريقة. دعونا نصف جميع العمليات الأساسية التي بنيت منها الخوارزمية.

نفترض أننا بحاجة إلى اكتشاف (التعرف على ، وتصنيف) قوس بيضاوي الشكل (أي حساب معلمات القطع الناقص ، وكذلك الزاوية الأولية والنهائية للقوس الإهليلجي) في هذه الصورة ، مقطوعة من القسم الأفقي من سحابة النقطة.


الشكل 3. أحد الأقواس الإهليلجية للمقطع العرضي للنموذج ثلاثي الأبعاد (بعد التنعيم)

من أجل تقليل العمل مع النقطية بشكل أعمى ، سنقوم بتنفيذ جميع العمليات مع النقطية من خلال الخطوط العريضة .

في مكتبة برمجية مفتوحة للرؤية الحاسوبية findContours إجراء الاكتشافات على حصيرة النقطية كل الخارجية (بدون الأشكال الداخلية) معالم في شكل ناقلات ناقلات نقطة عدد صحيح (في إحداثيات خطوط المسح):
 Mat mat(size);
 vector<vector<Point>> contours;
 findContours(mat, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

هذه هي عمليتنا الرئيسية ، والتي في بعض الحالات البسيطة تحل المهمة تمامًا . ولكن نظرًا لأن الحالات المتدهورة لا يتم العثور عليها دائمًا ، دعنا نفكر بمزيد من التفصيل في تكنولوجيا المعالجة من خلال تحديد الخطوط. تبدو

العملية العكسية ، التي تولد بيانات نقطية وفقًا للدائرة الخارجية الحالية باستخدام وظيفة OpenCV ، بسيطة أيضًا:
 drawContours(mat, contours, -1, Scalar(255), -1);

كما أنه يستخدم في كثير من الأحيان لإخفاء الخطوط ، والرسم ، أو لحساب المساحة.

وبالتالي ، في المرحلة الأولية ، لدينا مجموعة من البقع (قطع من منحنى معين) تحتاج إلى توصيلها بقوس بيضاوي الشكل ، مما يزيل أجزاء من المكونات الأخرى للهيكل (على سبيل المثال ، مثبتات) أو ضوضاء بصرية ناتجة عن التظليل أثناء المسح وغيرها أسباب.

دعنا ننشئ دالة تمييزية ستعيد نوع الكفاف (القطع الناقص ، الجزء الخطي ، الفقس أو أي شيء آخر) ، بالإضافة إلى نقاط النهاية للكونتور ومستطيل المخطط الدائري المستدير:
 contourTypeSearch(
   const vector<Point> &contour, Vec4i &def, RotatedRect &rc);

تساعد نسبة طول وعرض المستطيل على التمييز بسرعة بين الخطوط القريبة من الأجزاء الخطية ، بالإضافة إلى حدود الضوضاء الصغيرة .

يحتوي المستطيل المدور في OpenCV على نظام إحداثيات معقد . إذا لم تكن الزاوية نفسها هي المطلوبة ، ولكن وظائفها المثلثية ، فهي أكثر وضوحًا من السياق. إذا تم استخدام القيمة المطلقة للزاوية ، يجب أن تؤخذ في الاعتبار أن الزاوية تحسب من الأفقي إلى الحافة الأولى للمستطيل عكس اتجاه عقارب الساعة ولها قيمة سالبة .

في النهاية ل يتم العثور عليها ملامح بيضاوي الشكل باستخدام إجراءاتنا، الذي يتلقى حصيرة النقطيةبمخطط مميز مستخرج من الصورة الأصلية عن طريق إخفاء وإرجاع الحد الأقصى للعيب :
 contourConvFeature(mat, &def, … );

الرمز الرئيسي لهذه الوظيفة هو استدعاء إجراءين OpenCV :

 vector<int> *hull = new vector<int>();
 convexHull(contour, *hull);
 vector<Vec4i> *defs = new vector<Vec4i>();
 convexityDefects(contour, *hull, *defs);

يعثر الإجراء الأول على مضلع محدب للكفاف قيد الدراسة ، والثاني - يحسب كل عيوب التحدب .

نأخذ فقط أكبر عيب من حيث التحدب ، مع الأخذ في الاعتبار أنه يحدد نقاط نهاية الكفاف. قد لا يكون هذا هو الحال إذا كانت الحدود الخارجية أو الداخلية للكونتور تحتوي على ميزات . من أجل تسهيل لهم ، ونحن نطبق تجانس إضافي للكفاف قيد الدراسة (وليس إلى الصورة بأكملها حتى لا "طمس" البرازخ بين ملامح وألا تنتهك طوبولوجيا الأصلي).


الشكل 4. حساب عيب الانتفاخ:

الخيار (أ) يحدد عن طريق الخطأ نقطة النهاية الحمراء. الخيار (ب)يحدد نقاط النهاية بشكل صحيح. يعيد الخيار (ج) تحديد نقاط النهاية على الشكل الأصلي.

نظرًا لأنه في التكنولوجيا التي اعتمدناها ، يتم تجديد الدائرة في كل مرة ، يتعين علينا إعادة البحث عن نقاط المراسلات (أو بالأحرى ، مؤشراتها) من خلال إجراء البحث الشامل :
 nearestContourPtIdx(const vector<Point> &contour, const Point& pt);

في الحالات التي يتعذر فيها التخلص تمامًا من الميزات ، تم أيضًا تنفيذ وضع إضافي لفصل القوس (العمل بشكل منفصل مع القوس الداخلي / الخارجي). هذا مهم ، على سبيل المثال ، في الحالات التي يكون فيها القوس الخارجي للكونتور على اتصال بأشياء أخرى أو يكون فيه ضوضاء . في هذه الحالة ، يمكنك الذهاب للعمل مع القوس الداخلي. في هذه الحالة ، ليس من الضروري معالجة الأقواس الخارجية والداخلية بشكل منفصل.

أيضًا ، وفقًا للصيغة المعروفة جيدًا لنسبة التحدب للقوس ، يتم تقدير نصف قطر الدائرة تقريبًا ويتم رفض الحذف الكبير:
R = bulge / 2 + SQR(hypot) / (8 * bulge);

وهكذا ، بالنسبة لجميع الخطوط ، تم العثور على مقياس العيب المحدب (أو يتم تصنيفها على أنها خطية أو صغيرة وإزالتها من الإجراء). في المرحلة الأخيرة ، تتم إضافة معلمات إضافية إلى المقياس الأصلي ، مثل معلمة البُعد المُدار ، وما إلى ذلك ، ويتم ترتيب مجموعة المقاييس الكاملة قيد الدراسة حسب الحجم .
 typedef tuple<int , // 
   RotatedRect, //  
   Vec4i, //  
   int> // 
   RectDefMetric;


خوارزمية لربط مقاطع القوس عند نقاط النهاية


خوارزمية النمو واضحة وواضحة: نأخذ أكبر كفاف كبذرة ونحاول أن ننميها ، أي نعثر على أقرب التصحيحات ونلحقها بنقاطها النهائية التي تلبي شروط النمو. في الشكل المتنامي ، ندخل القوس البيضاوي المطلوب . قم بإخفاء الشكل وطرحه من المجموعة الأصلية. نكرر إجراء النمو حتى تنفد المجموعة الأولية .

يبدو الإجراء الأساسي لخوارزمية النمو كما يلي:
 vector<Point> *patch =
    growingContours(contour, def, tmp, hull);

حيث الكفاف هو الكفاف قيد الدراسة ، def هو عيب التحدب ، الهيكل هو مضلع محدب للمنطقة بأكملها ، tmp هو مصفوفة العازلة المساعدة. عند الإخراج ، نحصل على كفاف نما ناقل.

يتكون الإجراء من دورة محاولات لنمو البذور ، وتنتهي إما باستنفاد البقع المتاحة للنمو ، أو مقيدة بمعلمة الحد الأقصى لعدد التكرارات .


الشكل 5. العديد من البقع للنمو بدون بذرة

الصعوبة الرئيسية هي اختيار أقرب البقع إلى نقاط نهاية الكفاف ، بحيث ينمو الشكل إلى الأمام فقط . لل الاتجاه عرضيةنأخذ الخط المتوسط ​​للقوس بالقرب من نقطة النهاية. في الشكل 6 يعرض المرشحين للاتصال البذور في التكرار معين.


الشكل 6: البذور محاطة بعدد كبير من تصحيحات النمو المرشحة ،

لكل رقعة مرشحة ، يحسب المقياس التالي:
typedef tuple<
   double, //    2      2   
   bool, bool, //,  4   
   int, // 
   Vec4i> //  
   DistMetric;

فقط البقع التي تقع في المخروط التماسي تؤخذ في الاعتبار . ثم يتم تحديد التصحيح مع أصغر مسافة ، ومن خلال طباعة قسم الاتصال في البيانات النقطية ، يتصل بالطرف المقابل من البذرة. بالنسبة للطرف الآخر من البذرة ، يتم البحث عن تصحيح يطابق المعلمات ، وإذا تم العثور عليه ، فإنه متصل أيضًا بالبذرة. ثم يتم إخفاء البذور وطرحها من العديد من البقع. يتم تكرار الإجراء من البداية.

في نهاية إجراء النمو ، حصلنا على قوس بيضاوي الشكل ، لا يزال يتعين التحقق منه.

للبدء ، استخدم OpenCV القياسيالإجراء الذي تتلقاه رقعتنا (في شكل مسار ، نتذكر أن المسار والنقطية قابلة للتبديل معنا) ويعيد البعد المدور ، أي قطع ناقص كامل.
 RotatedRect box = fitEllipse(patch);

بعد ذلك ، نرفض علامات الحذف الكبيرة والصغيرة جدًا ، ثم نطبق إجراءنا الأصلي لمقارنة مناطق القوس البيضاوي الناتج وتصحيح النمو الأولي في شكل نقطي . يتضمن هذا الإجراء بعض الحيل المقنعة ، لذلك سنحذف وصفه الآن.

وأخيرًا ، نجد المعلمات المتبقية للقطع الناقص المكتشف - زوايا البداية والنهاية (نحن نعرف بالفعل نصف المحاور من fitEllipse ).

لتحديد زوايا البداية والنهاية ، نتابع على النحو التالي: نقوم بتحويل القطع الناقص الكامل ، إلى المضلع ، وبالعد المباشر نجد نقاطه الأقرب إلى نهاياتنا. على الإحداثيات الزاوي(في الواقع ، المؤشرات) وستكون زوايا البداية والنهاية لقوس بيضاوي الشكل. في التعليمات البرمجية ، يبدو هذا (مبسط قليلاً):
 pair<int, int>
   ellipseAngles(const RotatedRect &box,
   vector<Point> &ell, const Point &ps, 
   const Point &pe, const Point &pm) 
 {
    vector<Point> ell0;
    ellipse2Poly(Point(box.center.x, box.center.y), 
      Size(box.size.width / 2, box.size.height / 2),
      box.angle, 0, 355, 5, ell0);
    int i0 = nearestContourPtIdx(ell0, ps);
    int i1 = nearestContourPtIdx(ell0, pe);
    cutSides(ell0, i0, i1, i2, ell, nullptr);
    return pair<int, int>(i0, i1);
}

يأخذ إجراء cutSides الخاص بنا في الاعتبار طوبولوجيا اجتياز القوس البيضاوي. في المجموع ، يجب النظر في ثماني حالات محتملة لتجاوز المؤشرات i0 و i1 و i2 . هل نذهب على طول الكفاف الخارجي أو الداخلي ، وأي المؤشرات أكبر أو أولي أو نهائي؟

أسهل لرؤية الرمز:
 void cutSides(
   const vector<Point> &ell0, int i0, int i1, int i2, 
   vector<Point> *ell_in, vector<Point> *ell_out)
 {
   if (i0 < i1) {
      if (i2 > i0 && i2 < i1) {
         if (ell_in) {...}
            if (ell_out) {...}
        } else {
            if (ell_in) {...}
            if (ell_out) {...}
        }}
    else {
        if (i2 > i1 && i2 < i0) {
            if (ell_in) {...}
            if (ell_out) {...}
        } else {
            if (ell_in) {...}
            if (ell_out) {...}
        }}}

يتم عرض بعض نتائج الكشف عن الحذف في الحالات المعقدة في الشكل 7 .



في المقالات التالية ، سيتم النظر في طرق الكشف الإحصائي.

All Articles