CVB++ 14.1
edge.hpp
1#pragma once
2
3#include "../_cexports/c_edge.h"
4
5#include "../global.hpp"
6#include "../image.hpp"
7#include "../exception.hpp"
8#include "../rect.hpp"
9#include "../area_2d.hpp"
10
11#include <vector>
12#include <string>
13#include <sstream>
14#include <memory>
15#include <utility>
16#include <algorithm>
17
18
19namespace Cvb
20{
21CVB_BEGIN_INLINE_NS
22
23
24namespace Foundation { namespace Edge { class Projection; } }
25
26template <>
27inline HandleGuard<Foundation::Edge::Projection>::HandleGuard(void * handle) noexcept
28 : HandleGuard<Foundation::Edge::Projection>(handle, [](void* h) { CVB_CALL_CAPI(ReleaseProjection(h)); })
29{
30}
31
32
33
34namespace Foundation
35{
36
38
42namespace Edge
43{
44
47{
49
61
65};
66
68enum class EdgeType
69{
71 Ignore = CExports::POLARITY_DONT_CARE,
73 Positive = CExports::POLARITY_POSITIVE,
75 Negative = CExports::POLARITY_NEGATIVE
76};
77
80{
82 Average,
84 Sum
85};
86
88
91{
92public:
93 EdgeResult () noexcept
94 : x_ (0.0), y_ (0.0), quality_ (0.0), type_ (EdgeType::Ignore)
95 {}
96
97 EdgeResult (double x, double y, double quality, EdgeType type) noexcept
98 : x_ (x), y_ (y), quality_ (quality), type_ (type)
99 {}
100
101public:
103
107 static EdgeResult Nothing () noexcept
108 {
109 return EdgeResult ();
110 }
111
113
117 double X () const noexcept
118 {
119 return x_;
120 }
121
123
127 double Y () const noexcept
128 {
129 return y_;
130 }
131
133
137 double Quality () const noexcept
138 {
139 return quality_;
140 }
141
143
147 EdgeType Type () const noexcept
148 {
149 return type_;
150 }
151
152private:
153 double x_;
154 double y_;
155 double quality_;
156 EdgeType type_;
157};
158
160
167inline bool operator!= (const EdgeResult &lhs, const EdgeResult &rhs) noexcept
168{
169 return (lhs.X() != rhs.X()) || (lhs.Y() != rhs.Y()) || (lhs.Quality() != rhs.Quality()) || (lhs.Type() != rhs.Type());
170}
171
173
180inline bool operator== (const EdgeResult &lhs, const EdgeResult &rhs) noexcept
181{
182 return (!(lhs != rhs));
183}
184
186
189{
192
195};
196
197namespace Private
198{
199/* Convert an edge results collection to an edge vector. */
200inline std::vector<EdgeResult> EdgeResultsToArray (CExports::EDGERESULTS hResults, EdgeType type)
201{
203
204 size_t resultCount = CVB_CALL_CAPI (EdgeResultsCount (hResults));
205 for (size_t i = 0; i < resultCount; ++i)
206 {
208 CVB_CALL_CAPI_CHECKED (EdgeResult (hResults, i, x, y, q));
209 results.push_back (EdgeResult(x, y, q, type));
210 }
211
212 return results;
213}
214
215typedef HandleGuard<void, CVB_CALL_CAPI(ReleaseEdgeResultsVoid)> ReleaseEdgeResultsGuard;
216
217} /* namespace Private */
218
220
231inline EdgeResult FindFirstEdge (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type, double threshold, Area2D aoi, double density = 1.0)
232{
234 {
235 throw std::invalid_argument ("invalid combination of EdgeType and EdgeSearchMode requested");
236 }
237
238 CExports::TEdgeResult edgeRes = { 0 };
239 CExports::cvbbool_t success = false;
240 switch (mode)
241 {
243 success = CVB_CALL_CAPI (CFindFirstEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, edgeRes));
244 break;
246 success = CVB_CALL_CAPI (CSFindFirstEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, edgeRes));
247 break;
249 success = CVB_CALL_CAPI (TFindFirstEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, edgeRes));
250 break;
252 success = CVB_CALL_CAPI (TSFindFirstEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, edgeRes));
253 break;
255 success = CVB_CALL_CAPI (OSFindFirstEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, static_cast<CExports::TEdgePolarity>(type), edgeRes));
256 break;
257 default:
258 throw std::invalid_argument ("unknown edge search mode requested");
259 }
260
261 if (success)
262 {
263 return EdgeResult (edgeRes.x, edgeRes.y, edgeRes.Quality, type);
264 }
265 else
266 {
267 return EdgeResult::Nothing ();
268 }
269}
270
272
282inline EdgeResult FindFirstEdge (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type, double threshold, double density = 1.0)
283{
284 return FindFirstEdge (plane, mode, type, threshold, Area2D (static_cast<Rect<double>>(plane.Parent ().Bounds ())), density);
285}
286
288
301inline EdgeResultPair FindEdgePair(const ImagePlane& plane, EdgeSearchMode mode, EdgeType type1, double threshold1, EdgeType type2, double threshold2, Area2D aoi, double density = 1.0)
302{
304 {
305 throw std::invalid_argument("invalid combination of EdgeType and EdgeSearchMode requested");
306 }
308 {
309 throw std::invalid_argument("invalid combination of EdgeType and EdgeSearchMode requested");
310 }
311
312 CExports::TEdgeResult edgeRes1 = { 0 };
313 CExports::TEdgeResult edgeRes2 = { 0 };
314 CExports::cvbbool_t success = false;
315 switch (mode)
316 {
318 success = CVB_CALL_CAPI (CFindEdgePair(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold1, type1 == EdgeType::Positive, edgeRes1, threshold2, type2 == EdgeType::Positive, edgeRes2));
319 break;
321 success = CVB_CALL_CAPI (CSFindEdgePair(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold1, type1 == EdgeType::Positive, edgeRes1, threshold2, type2 == EdgeType::Positive, edgeRes2));
322 break;
324 success = CVB_CALL_CAPI (TFindEdgePair(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold1, type1 == EdgeType::Positive, edgeRes1, threshold2, type2 == EdgeType::Positive, edgeRes2));
325 break;
327 success = CVB_CALL_CAPI (TSFindEdgePair(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold1, type1 == EdgeType::Positive, edgeRes1, threshold2, type2 == EdgeType::Positive, edgeRes2));
328 break;
330 success = CVB_CALL_CAPI (OSFindEdgePair(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold1, static_cast<CExports::TEdgePolarity>(type1), edgeRes1, threshold2, static_cast<CExports::TEdgePolarity>(type2), edgeRes2));
331 break;
332 default:
333 throw std::invalid_argument ("unknown edge search mode requested");
334 }
335
336 if (success)
337 {
338 return EdgeResultPair { {edgeRes1.x, edgeRes1.y, edgeRes1.Quality, type1},
339 {edgeRes1.x, edgeRes1.y, edgeRes1.Quality, type1} };
340 }
341 else
342 {
344 }
345}
346
348
360inline EdgeResultPair FindEdgePair (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type1, double threshold1, EdgeType type2, double threshold2, double density = 1.0)
361{
362 return FindEdgePair (plane, mode, type1, threshold1, type2, threshold2, Area2D (static_cast<Rect<double>>(plane.Parent ().Bounds ())), density);
363}
364
366
378inline EdgeResultPair FindEdgePair (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type1, EdgeType type2, double threshold, Area2D aoi, double density = 1.0)
379{
380 return FindEdgePair (plane, mode, type1, threshold, type2, threshold, aoi, density);
381}
382
384
395inline EdgeResultPair FindEdgePair (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type1, EdgeType type2, double threshold, double density = 1.0)
396{
397 return FindEdgePair (plane, mode, type1, threshold, type2, threshold, Area2D (static_cast<Rect<double>>(plane.Parent ().Bounds ())), density);
398}
399
400
402
413inline std::vector<EdgeResult> FindAllEdges (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type, double threshold, Area2D aoi, double density = 1.0)
414{
416 {
417 throw std::invalid_argument ("invalid combination of EdgeType and EdgeSearchMode requested");
418 }
419
420 // second derivation is different to the rest...
422 {
423 CExports::EDGERESULTS hEdgeResults = nullptr;
424 CVB_CALL_CAPI (OSFindAllEdges(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, static_cast<CExports::TEdgePolarity>(type), hEdgeResults));
425 Private::ReleaseEdgeResultsGuard hEdgeResHolder (hEdgeResults);
426 return Private::EdgeResultsToArray(hEdgeResults, type);
427 }
428
429 std::vector<CExports::TEdgeResult> rawResults (Round(aoi.Size()).Width());
430 size_t maxEdges = rawResults.size ();
431 size_t edgeCount = 0;
432 switch (mode)
433 {
435 CVB_CALL_CAPI (CFindAllEdges(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, maxEdges, rawResults.data(), edgeCount));
436 break;
438 CVB_CALL_CAPI (CSFindAllEdges(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, maxEdges, rawResults.data(), edgeCount));
439 break;
441 CVB_CALL_CAPI (TFindAllEdges(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, maxEdges, rawResults.data(), edgeCount));
442 break;
444 CVB_CALL_CAPI (TSFindAllEdges(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), threshold, type == EdgeType::Positive, maxEdges, rawResults.data(), edgeCount));
445 break;
446 default:
447 throw std::invalid_argument ("unknown edge search mode requested");
448 }
449
450 std::vector<EdgeResult> fullResults (edgeCount);
451 std::transform (rawResults.begin (), rawResults.begin () + edgeCount, fullResults.begin (), // NOLINT
452 [=](CExports::TEdgeResult raw) { return EdgeResult (raw.x, raw.y, raw.Quality, type); });
453 return fullResults;
454}
455
457
467inline std::vector<EdgeResult> FindAllEdges (const ImagePlane & plane, EdgeSearchMode mode, EdgeType type, double threshold, double density = 1.0)
468{
469 return FindAllEdges (plane, mode, type, threshold, Area2D (static_cast<Rect<double>>(plane.Parent ().Bounds ())), density);
470}
471
473
482inline EdgeResult FindBestEdge (const ImagePlane & plane, EdgeType type, Area2D aoi, double density = 1.0)
483{
484 CExports::TEdgeResult edgeRes = { 0 };
485 auto success = CVB_CALL_CAPI (OSFindBestEdge(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi), static_cast<CExports::TEdgePolarity>(type), edgeRes));
486
487 if (success)
488 {
489 return EdgeResult (edgeRes.x, edgeRes.y, edgeRes.Quality, type);
490 }
491 else
492 {
493 return EdgeResult::Nothing ();
494 }
495}
496
498
506inline EdgeResult FindBestEdge (const ImagePlane & plane, EdgeType type, double density = 1.0)
507{
508 return FindBestEdge (plane, type, Area2D (static_cast<Rect<double>>(plane.Parent ().Bounds ())), density);
509}
510
512
519inline void WriteProjection (const ImagePlane & plane, Area2D aoi, double density = 1.0)
520{
521 CVB_CALL_CAPI (WriteProjection(plane.Parent().Handle(), plane.Plane(), static_cast<int>(density * 1000.0), reinterpret_cast<CExports::TArea&>(aoi)));
522}
523
525
528{
530
535
537 double Value = 0.0;
538};
539
541
549{
550private:
551 // Internal helper constructor version
552 Projection (HandleGuard<Projection>&& guard, ProjectionMode mode) noexcept
553 : handle_(std::move(guard)),
554 mode_ (mode)
555 {
556 }
557
558public:
560
567 Projection (const ImagePlane &plane, Area2D aoi, ProjectionMode mode = ProjectionMode::Sum, double density = 1.0)
568 : Projection (HandleGuard<Projection>(CreateProjectionHandle (plane, aoi, mode, density)), mode)
569 { }
570
571
572 Projection(const Projection& other) = delete;
573 Projection& operator=(const Projection& other) = delete;
574 Projection (Projection&& other) noexcept = default;
575 Projection& operator=(Projection&& other) noexcept = default;
576 ~Projection() = default;
577
578
580
585 {
586 if (Length() != values_.size())
587 {
588 std::vector<ProjectionValue> values(Length());
589 Internal::DoBoolCall([&]()
590 {
591 return CVB_CALL_CAPI(CopyDoubleBuffer(Handle(), reinterpret_cast<CExports::pDoubleProjection>(values.data())));
592 });
593 values_.swap(values);
594 }
595 return values_;
596 }
597
598private:
599 // Helper to create the projection handle.
600 static CExports::PROJECTIONEX CreateProjectionHandle (const ImagePlane &plane, Area2D aoi, ProjectionMode mode, double density)
601 {
602 CExports::PROJECTIONEX projection = nullptr;
603 /* Note: the binary compatibility between Area2D and TArea is guaranteed, therefore the cast below is legal. */
604 switch (mode)
605 {
607 CVB_CALL_CAPI_CHECKED (GetNormProjectionEx(plane.Parent().Handle(), plane.Plane(), static_cast<CExports::cvbdensity_t>(density * 1000.0),
608 reinterpret_cast<CExports::TArea&>(aoi), projection));
609 break;
611 CVB_CALL_CAPI_CHECKED (GetProjectionEx(plane.Parent().Handle(), plane.Plane(), static_cast<CExports::cvbdensity_t>(density * 1000.0),
612 reinterpret_cast<CExports::TArea&>(aoi), projection));
613 break;
614 default:
615 throw std::invalid_argument ("unknown projection mode");
616 }
617 return projection;
618 }
619
620public:
622
630 static std::unique_ptr<Projection> Create (const ImagePlane &plane, Area2D aoi, ProjectionMode mode = ProjectionMode::Sum, double density = 1.0)
631 {
632 return std::make_unique<Projection>(plane, aoi, mode, density);
633 }
634
636
642 void * Handle() const noexcept
643 {
644 return handle_.Handle();
645 }
646
648
656 static std::unique_ptr<Projection> FromHandle (HandleGuard<Projection>&& guard, ProjectionMode mode)
657 {
658 if (!guard.Handle ())
659 {
660 throw std::invalid_argument ("invalid projection native handle");
661 }
662 return std::unique_ptr<Projection>(new Projection(std::move(guard), mode));
663 }
664
666
672 {
673 CExports::EDGERESULTS hResults = nullptr;
674 CVB_CALL_CAPI_CHECKED (ZeroCrossings (Handle(), Length(), hResults));
675
676 Private::ReleaseEdgeResultsGuard hEdgeResHolder (hResults);
677
678 return Private::EdgeResultsToArray (hResults, EdgeType::Ignore);
679 }
680
682
688 {
689 return Internal::DoBoolCallObjectOut<Projection>([&](void* & resproj)
690 {
691 return CVB_CALL_CAPI(Derivation (Handle (), resproj));
692 },
694 }
695
697
703 template <class RANGE>
704 inline typename TypedRange<std::unique_ptr<Projection>, int, RANGE>::type Filter (const RANGE &kernel)
705 {
706 auto kernelRange = MakeRangeAdapter<int> (kernel, 1);
707
708 return Internal::DoBoolCallObjectOut<Projection>([&](void* & resproj)
709 {
710 return CVB_CALL_CAPI(Filter (Handle (), reinterpret_cast<CExports::cvbval_t *>(kernelRange.Data()),
711 kernelRange.Size(), resproj));
712 },
714 }
715
717
723 {
724 return Internal::DoBoolCallObjectOut<Projection>([&](void* & resproj)
725 {
726 return CVB_CALL_CAPI(Normalize (Handle (), resproj));
727 },
729 }
730
732
736 ProjectionMode Mode() const noexcept
737 {
738 return mode_;
739 }
740
741private:
742 /* The length (= number of values) of the projection. */
743 size_t Length () const
744 {
745 size_t length = 0;
746 CVB_CALL_CAPI_CHECKED (GetLength (Handle(), length));
747 return length;
748 }
749
750private:
751 HandleGuard<Projection> handle_;
752 ProjectionMode mode_;
753 mutable std::vector<ProjectionValue> values_;
754}; /* class Projection */
755
758
759} /* namespace Edge */
760
762using Edge::EdgeType;
764
765using Edge::EdgeResult;
768using Edge::Projection;
770
776
777using Edge::Projection;
779
780
781
782} /* namespace Foundation */
783CVB_END_INLINE_NS
784} /* namespace Cvb */
785
Structure that represents an area of interest in the image.
Definition: area_2d.hpp:21
Edge search result
Definition: edge.hpp:91
EdgeType Type() const noexcept
Type of the edge.
Definition: edge.hpp:147
double Quality() const noexcept
Quality of detection, depending on the method used.
Definition: edge.hpp:137
double Y() const noexcept
Y-position relative to the last line processed.
Definition: edge.hpp:127
double X() const noexcept
X-position relative to the last line processed.
Definition: edge.hpp:117
static EdgeResult Nothing() noexcept
The edge result returned when nothing has been found.
Definition: edge.hpp:107
Projection that the Edge analysis of Common Vision Blox is using.
Definition: edge.hpp:549
Projection(const ImagePlane &plane, Area2D aoi, ProjectionMode mode=ProjectionMode::Sum, double density=1.0)
Create a projection object.
Definition: edge.hpp:567
static std::unique_ptr< Projection > FromHandle(HandleGuard< Projection > &&guard, ProjectionMode mode)
Creates projection from a classic API handle.
Definition: edge.hpp:656
TypedRange< std::unique_ptr< Projection >, int, RANGE >::type Filter(const RANGE &kernel)
Filter this projection using a 1D filter kernel.
Definition: edge.hpp:704
std::vector< EdgeResult > CalculateZeroCrossings()
Get all the zero crossings, that are in the projection.
Definition: edge.hpp:671
std::unique_ptr< Projection > Derive()
Create derivative of this projection.
Definition: edge.hpp:687
ProjectionMode Mode() const noexcept
Gets the mode of the projection.
Definition: edge.hpp:736
static std::unique_ptr< Projection > Create(const ImagePlane &plane, Area2D aoi, ProjectionMode mode=ProjectionMode::Sum, double density=1.0)
Create a projection object.
Definition: edge.hpp:630
std::unique_ptr< Projection > ToAverageProjection()
Create a new averaged projection from this one.
Definition: edge.hpp:722
std::vector< ProjectionValue > Values() const
Retrieve the values of the projection.
Definition: edge.hpp:584
void * Handle() const noexcept
Classic API PROJECTIONEX handle.
Definition: edge.hpp:642
Image plane information container.
Definition: decl_image_plane.hpp:33
Rectangle object.
Definition: rect.hpp:26
std::vector< EdgeResult > FindAllEdges(const ImagePlane &plane, EdgeSearchMode mode, EdgeType type, double threshold, Area2D aoi, double density=1.0)
Find all edges inside the aoi.
Definition: edge.hpp:413
EdgeResult FindBestEdge(const ImagePlane &plane, EdgeType type, Area2D aoi, double density=1.0)
Use the 2nd derivative method to find the edge with the highest intensity in the area of interest.
Definition: edge.hpp:482
EdgeResultPair FindEdgePair(const ImagePlane &plane, EdgeSearchMode mode, EdgeType type1, double threshold1, EdgeType type2, double threshold2, Area2D aoi, double density=1.0)
Find an edge pair (as specified) in the aoi.
Definition: edge.hpp:301
EdgeType
Type of the edges to be searched.
Definition: edge.hpp:69
@ Positive
Intensities increase along the scan direction.
@ Ignore
Used for EdgeResult::Nothing and with the 2nd derivative edge detection functions.
@ Negative
Intensities decrease along the scan direction.
ProjectionMode
Options affecting the result of the projection.
Definition: edge.hpp:80
@ Sum
Projection value is the sum of all gray values per projection line.
@ Average
Projection value is the average value per projection line.
std::shared_ptr< Projection > ProjectionPtr
Convenience shared pointer for Projection.
Definition: edge.hpp:757
bool operator!=(const EdgeResult &lhs, const EdgeResult &rhs) noexcept
Comparison operator for EdgeResult objects.
Definition: edge.hpp:167
void WriteProjection(const ImagePlane &plane, Area2D aoi, double density=1.0)
Write the projection that Edge is using internally as the basis for its calculations into an image pl...
Definition: edge.hpp:519
bool operator==(const EdgeResult &lhs, const EdgeResult &rhs) noexcept
Comparison operator for EdgeResult objects.
Definition: edge.hpp:180
EdgeResult FindFirstEdge(const ImagePlane &plane, EdgeSearchMode mode, EdgeType type, double threshold, Area2D aoi, double density=1.0)
Find the first edge (as specified) in the aoi.
Definition: edge.hpp:231
EdgeSearchMode
Determines the algorithm for finding an edge.
Definition: edge.hpp:47
@ SecondDerivativeSubPixel
Find edges based on the 2nd derivative of the intensities with sub pixel accuracy.
@ Intensity
Find edges based on absolute intensities in the image.
@ IntensitySubPixel
Like Intensity, but with sub pixel accuracy.
@ Contrast
Find edges based on the contrasts in the image (1st derivative of the gray values).
@ ContrastSubPixel
Like Contrast, but with sub pixel accuracy.
Root namespace for the Image Manager interface.
Definition: c_barcode.h:24
Point2D< int > Round(const Point2D< T > &rhs) noexcept
Round to an integer point.
Definition: point_2d.hpp:380
T quiet_NaN(T... args)
A pair of found edges.
Definition: edge.hpp:189
EdgeResult Second
Second found edge.
Definition: edge.hpp:194
EdgeResult First
First found edge.
Definition: edge.hpp:191
Single projection value.
Definition: edge.hpp:528
double Value
Projection value as determined by the ProjectionMode.
Definition: edge.hpp:537
Point2D< double > Position
Coordinate of the projection.
Definition: edge.hpp:534