29#include <opencv2/core/core.hpp>
30#include <opencv2/imgproc/imgproc.hpp>
31#include <opencv2/calib3d/calib3d.hpp>
33#include <Eigen/Geometry>
42 "wraparound, 30=yellow, 45=light green, 60=green, 75=green cyan, 90=cyan, "
43 "105=light blue, 120=blue, 135=purple, 150=pink)",
56 "detected in a frame, we skip that frame before we even try to analyze shapes of the blobs",
61 "if you want to skip shape analysis of very large or very small blobs",
66 "occupies a smaller fraction of its convex hull. This parameter sets an upper bound, "
67 "fuller shapes will be rejected.",
80 "to remove small contour defects before the shape is analyzed.",
89 "provided by user parameters; thread 1 broadens that fixed range a bit; threads 2-3 use a "
90 "narrow and broader learned HSV window over time",
103 "When dopose is true, 3D serial messages are sent out, otherwise 2D serial messages.",
112 cv::Size_<float>(0.28F, 0.175F), ParamCateg);
116 "gets closer than the margin to the frame borders, the shape will be rejected. This is to "
117 "avoid possibly bogus 6D pose estimation when the shape starts getting truncated as it "
118 "partially exits the camera's field of view.",
288 public jevois::Parameter<hcue, scue, vcue, maxnumobj, hullarea, hullfill, erodesize,
289 dilatesize, epsilon, debug, threads, showthread, ethresh,
290 dopose, iou, objsize, margin>
312 hsvcue(
unsigned char h,
unsigned char hsig,
unsigned char s,
unsigned char ssig,
313 unsigned char v,
unsigned char vsig) :
muh(
h),
sih(hsig),
mus(s),
sis(ssig),
muv(v),
siv(vsig)
319 muh = std::min(179.0F, std::max(1.0F,
muh));
sih = std::max(1.0F, std::min(
sih, 360.0F));
320 mus = std::min(254.0F, std::max(1.0F,
mus));
sis = std::max(1.0F, std::min(
sis, 512.0F));
321 muv = std::min(254.0F, std::max(1.0F,
muv));
siv = std::max(1.0F, std::min(
siv, 512.0F));
326 {
return cv::Scalar(std::max(0.0F,
muh -
sih), std::max(0.0F,
mus -
sis), std::max(0.0F,
muv -
siv)); }
330 {
return cv::Scalar(std::min(179.0F,
muh +
sih), 255, 255); }
367 cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs,
368 cv::Mat & _rvecs, cv::Mat & _tvecs) :
369 objPoints(_objPoints), corners(_corners), cameraMatrix(_cameraMatrix),
370 distCoeffs(_distCoeffs), rvecs(_rvecs), tvecs(_tvecs)
375 int const begin = range.start;
376 int const end = range.end;
378 for (
int i = begin; i < end; ++i)
379 cv::solvePnP(objPoints, corners.getMat(i), cameraMatrix, distCoeffs,
380 rvecs.at<cv::Vec3d>(i), tvecs.at<cv::Vec3d>(i));
385 cv::InputArrayOfArrays corners;
386 cv::InputArray cameraMatrix, distCoeffs;
387 cv::Mat & rvecs, tvecs;
399 itsKalH = addSubComponent<Kalman1D>(
"kalH");
400 itsKalS = addSubComponent<Kalman1D>(
"kalS");
401 itsKalV = addSubComponent<Kalman1D>(
"kalV");
407 itsCalib = engine()->loadCameraCalibration();
418 void estimatePose(std::vector<std::vector<cv::Point2f> > & corners, cv::OutputArray _rvecs,
419 cv::OutputArray _tvecs)
421 auto const osiz = objsize::get();
427 corners.push_back(std::vector<cv::Point2f>());
428 std::vector<cv::Point2f> & v = corners.back();
429 for (
auto const & p : d.hull) v.push_back(cv::Point2f(p));
435 cv::Mat objPoints(4, 1, CV_32FC3);
436 objPoints.ptr< cv::Vec3f >(0)[0] = cv::Vec3f(-osiz.width * 0.5F, -osiz.height * 0.5F, 0);
437 objPoints.ptr< cv::Vec3f >(0)[1] = cv::Vec3f(-osiz.width * 0.5F, osiz.height * 0.5F, 0);
438 objPoints.ptr< cv::Vec3f >(0)[2] = cv::Vec3f(osiz.width * 0.5F, osiz.height * 0.5F, 0);
439 objPoints.ptr< cv::Vec3f >(0)[3] = cv::Vec3f(osiz.width * 0.5F, -osiz.height * 0.5F, 0);
441 int nobj = (int)corners.size();
442 _rvecs.create(nobj, 1, CV_64FC3); _tvecs.create(nobj, 1, CV_64FC3);
443 cv::Mat rvecs = _rvecs.getMat(), tvecs = _tvecs.getMat();
456 cv::inRange(imghsv, rmin, rmax, imgth);
457 std::string str =
jevois::sformat(
"T%zu: H=%03d-%03d S=%03d-%03d V=%03d-%03d ", tnum,
int(rmin.val[0]),
458 int(rmax.val[0]),
int(rmin.val[1]),
int(rmax.val[1]),
459 int(rmin.val[2]),
int(rmax.val[2]));
466 std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy;
467 cv::findContours(imgth, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
470 double const epsi = epsilon::get();
471 int const m = margin::get();
474 std::string str2, beststr2;
475 if (hierarchy.size() > 0 && hierarchy.size() <= maxnumobj::get())
477 for (
int index = 0; index >= 0; index = hierarchy[index][0])
480 if (str2.length() > beststr2.length()) beststr2 = str2;
484 std::vector<cv::Point>
const & c = contours[index];
488 double const area = cv::contourArea(c,
false);
491 std::vector<cv::Point> rawhull;
492 cv::convexHull(c, rawhull,
true);
493 double const rawhullperi = cv::arcLength(rawhull,
true);
494 cv::approxPolyDP(rawhull, d.
hull, epsi * rawhullperi * 3.0,
true);
497 if (d.
hull.size() != 4)
continue;
500 double const huarea = cv::contourArea(d.
hull,
false);
501 if ( ! hullarea::get().contains(
int(huarea + 0.4999)))
continue;
504 int const hufill = int(
area / huarea * 100.0 + 0.4999);
505 if (hufill > hullfill::get())
continue;
509 double const peri = cv::arcLength(c,
true);
510 cv::approxPolyDP(c, d.
approx, epsi * peri,
true);
511 if (d.
approx.size() < 7 || d.
approx.size() > 9)
continue;
515 d.
serr = 100.0 * cv::matchShapes(c, d.
approx, cv::CONTOURS_MATCH_I1, 0.0);
516 if (d.
serr > ethresh::get())
continue;
523 for (
size_t i = 0; i < c.size(); ++i)
524 if (c[i].x < m || c[i].x >= imghsv.cols - m || c[i].y < m || c[i].y >= imghsv.rows - m)
525 { reject =
true;
break; }
526 if (reject)
continue;
538 std::complex<float> v10p23(
float(d.
hull[0].x - d.
hull[1].x + d.
hull[3].x - d.
hull[2].x),
540 float const len10p23 = std::abs(v10p23);
541 std::complex<float> v03p12(
float(d.
hull[3].x - d.
hull[0].x + d.
hull[2].x - d.
hull[1].x),
543 float const len03p12 = std::abs(v03p12);
546 cv::Moments
const momC = cv::moments(c);
547 cv::Moments
const momH = cv::moments(d.
hull);
548 std::complex<float> vCH(momH.m10 / momH.m00 - momC.m10 / momC.m00, momH.m01 / momH.m00 - momC.m01 / momC.m00);
549 float const lenCH = std::abs(vCH);
551 if (len10p23 < 0.1F || len03p12 < 0.1F || lenCH < 0.1F)
continue;
554 float const good = (v10p23.real() * vCH.real() + v10p23.imag() * vCH.imag()) / (len10p23 * lenCH);
555 float const bad = (v03p12.real() * vCH.real() + v03p12.imag() * vCH.imag()) / (len03p12 * lenCH);
558 if (vCH.imag() >= -2.0F)
continue;
562 if (bad > good) { d.
hull.insert(d.
hull.begin(), d.
hull.back()); d.
hull.pop_back(); }
567 std::lock_guard<std::mutex> _(
itsDetMtx);
570 if (str2.length() > beststr2.length()) beststr2 = str2;
574 if (outimg && outimg->valid())
576 if (tnum == showthread::get() &&
int(outimg->width) == 2 * imgth.cols)
586 float const spread = 0.2F;
592 for (
size_t i = 0; i < nthreads; ++i)
594 hsvcue cue(hcue::get(), scue::get(), vcue::get());
595 cue.
sih *= (1.0F + spread * i); cue.
sis *= (1.0F + spread * i); cue.
siv *= (1.0F + spread * i);
615 for (
size_t i = 3; i <
itsHSV.size(); ++i)
618 itsHSV[i].sih *= (1.0F + spread * i);
619 itsHSV[i].sis *= (1.0F + spread * i);
620 itsHSV[i].siv *= (1.0F + spread * i);
631 bool keepgoing =
true;
632 double const iouth = iou::get();
637 keepgoing =
false;
int delidx = -1;
641 for (
size_t i = 0; i < siz; ++i)
643 for (
size_t j = 0; j < i; ++j)
646 for (cv::Point
const & p :
itsDetections[j].hull) pts.push_back(p);
647 std::vector<cv::Point> hull;
648 cv::convexHull(pts, hull);
649 double uarea = cv::contourArea(hull);
653 double const inoun = iarea / uarea;
660 if (delidx != -1)
break;
670 int const w = imgbgr.cols,
h = imgbgr.rows;
674 auto median_fut =
jevois::async([&](){ cv::medianBlur(imgbgr, medimgbgr, 3); } );
677 std::vector<std::vector<cv::Point> > contours;
681 std::future<void> drawc_fut;
682 if (debug::get() && outimg && outimg->valid())
685 cv::Mat outuc2(outimg->height, outimg->width, CV_8UC2, outimg->pixelsw<
unsigned char>());
690 cv::Mat mask(
h, w, CV_8UC1, (
unsigned char)0);
691 cv::drawContours(mask, contours, -1, 255, -1);
698 cv::meanStdDev(medimgbgr, mean, std, mask);
701 cv::Mat bgrmean(2, 1, CV_8UC3); bgrmean.at<cv::Vec3b>(0, 0) = mean; bgrmean.at<cv::Vec3b>(1, 0) = std;
702 cv::Mat hsvmean; cv::cvtColor(bgrmean, hsvmean, cv::COLOR_BGR2HSV);
704 cv::Vec3b hsv = hsvmean.at<cv::Vec3b>(0, 0);
705 int H = hsv.val[0], S = hsv.val[1],
V = hsv.val[2];
707 cv::Vec3b sighsv = hsvmean.at<cv::Vec3b>(1, 0);
708 int sH = sighsv.val[0], sS = sighsv.val[1], sV = sighsv.val[2];
715 float const eta = 0.4F;
716 itsHSV[2].sih = (1.0F - eta) *
itsHSV[2].sih + eta * sH;
717 itsHSV[2].sis = (1.0F - eta) *
itsHSV[2].sis + eta * sS;
718 itsHSV[2].siv = (1.0F - eta) *
itsHSV[2].siv + eta * sV;
727 void sendAllSerial(
int w,
int h, std::vector<std::vector<cv::Point2f> >
const & corners,
728 std::vector<cv::Vec3d>
const & rvecs, std::vector<cv::Vec3d>
const & tvecs)
730 if (rvecs.empty() ==
false)
733 auto const osiz = objsize::get();
734 for (
size_t i = 0; i < corners.size(); ++i)
736 cv::Vec3d
const & rv = rvecs[i];
737 cv::Vec3d
const & tv = tvecs[i];
740 float theta = std::sqrt(rv[0] * rv[0] + rv[1] * rv[1] + rv[2] * rv[2]);
741 Eigen::Vector3f axis(rv[0], rv[1], rv[2]);
742 Eigen::Quaternion<float> q(Eigen::AngleAxis<float>(theta, axis));
745 osiz.width, osiz.height, 1.0F,
746 q.w(), q.x(), q.y(), q.z(),
753 for (
size_t i = 0; i < corners.size(); ++i)
762 int e = erodesize::get();
765 if (e)
itsErodeElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(e, e));
769 int d = dilatesize::get();
772 if (d)
itsDilateElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(d, d));
790 cv::Mat imghsv; cv::cvtColor(imgbgr, imghsv, cv::COLOR_BGR2HSV);
791 size_t const nthreads = threads::get();
801 std::vector<std::future<void> > dfut;
802 for (
size_t i = 0; i < nthreads - 1; ++i)
803 dfut.push_back(
jevois::async([&](
size_t tn) { detect(imghsv, tn, 3, h+2); }, i));
804 detect(imghsv, nthreads - 1, 3,
h+2);
819 std::vector<std::vector<cv::Point2f> > corners; std::vector<cv::Vec3d> rvecs, tvecs;
840 inimg.
require(
"input", w,
h, V4L2_PIX_FMT_YUYV);
847 outimg = outframe.get();
849 if (outimg.
width != w && outimg.
width != w * 2)
LFATAL(
"Output image width should be 1x or 2x input width");
857 cv::Mat imghsv; cv::cvtColor(imgbgr, imghsv, cv::COLOR_BGR2HSV);
858 size_t const nthreads = threads::get();
868 std::vector<std::future<void> > dfut;
869 for (
size_t i = 0; i < nthreads - 1; ++i)
870 dfut.push_back(
jevois::async([&](
size_t tn) { detect(imghsv, tn, 3, h+2, &outimg); }, i));
871 detect(imghsv, nthreads - 1, 3,
h+2, &outimg);
889 std::vector<std::vector<cv::Point2f> > corners; std::vector<cv::Vec3d> rvecs, tvecs;
906 std::string
const & fpscpu = timer.
stop();
915 std::vector<cv::Vec3d>
const & rvecs, std::vector<cv::Vec3d>
const & tvecs)
917 auto const osiz = objsize::get();
float const w = osiz.width,
h = osiz.height;
918 int nobj = int(corners.size());
924 for (
int i = 0; i < nobj; ++i)
926 std::vector<cv::Point2f>
const & obj = corners[i];
929 for (
int j = 0; j < 4; ++j)
931 cv::Point2f
const & p0 = obj[j];
932 cv::Point2f
const & p1 = obj[ (j+1) % 4 ];
943 float const hw = w * 0.5F, hh =
h * 0.5F, dd = -0.5F * std::max(w,
h);
945 for (
int i = 0; i < nobj; ++i)
948 std::vector<cv::Point3f> axisPoints;
949 axisPoints.push_back(cv::Point3f(0.0F, 0.0F, 0.0F));
950 axisPoints.push_back(cv::Point3f(hw, 0.0F, 0.0F));
951 axisPoints.push_back(cv::Point3f(0.0F, hh, 0.0F));
952 axisPoints.push_back(cv::Point3f(0.0F, 0.0F, dd));
954 std::vector<cv::Point2f> imagePoints;
959 int(imagePoints[1].x + 0.5F),
int(imagePoints[1].y + 0.5F),
962 int(imagePoints[2].x + 0.5F),
int(imagePoints[2].y + 0.5F),
965 int(imagePoints[3].x + 0.5F),
int(imagePoints[3].y + 0.5F),
969 std::vector<cv::Point3f> cubePoints;
970 cubePoints.push_back(cv::Point3f(-hw, -hh, 0.0F));
971 cubePoints.push_back(cv::Point3f(hw, -hh, 0.0F));
972 cubePoints.push_back(cv::Point3f(hw, hh, 0.0F));
973 cubePoints.push_back(cv::Point3f(-hw, hh, 0.0F));
974 cubePoints.push_back(cv::Point3f(-hw, -hh, dd));
975 cubePoints.push_back(cv::Point3f(hw, -hh, dd));
976 cubePoints.push_back(cv::Point3f(hw, hh, dd));
977 cubePoints.push_back(cv::Point3f(-hw, hh, dd));
979 std::vector<cv::Point2f> cuf;
983 std::vector<cv::Point> cu;
984 for (
auto const & p : cuf) cu.push_back(cv::Point(
int(p.x + 0.5F),
int(p.y + 0.5F)));
JEVOIS_REGISTER_MODULE(ArUcoBlob)
double area(const std::vector< Point2D< T > > &polygon, const bool getsigned=false)
What is the area of a polygon?
ParallelLoopBody class for the parallelization of the single markers pose estimation.
void operator()(cv::Range const &range) const
SinglePoseEstimationParallel(cv::Mat &_objPoints, cv::InputArrayOfArrays _corners, cv::InputArray _cameraMatrix, cv::InputArray _distCoeffs, cv::Mat &_rvecs, cv::Mat &_tvecs)
Simple color-based detection of a U-shaped object for FIRST Robotics.
virtual ~FirstVision()
Virtual destructor for safe inheritance.
void sendAllSerial(int w, int h, std::vector< std::vector< cv::Point2f > > const &corners, std::vector< cv::Vec3d > const &rvecs, std::vector< cv::Vec3d > const &tvecs)
Send serial messages about each detection:
JEVOIS_DECLARE_PARAMETER(maxnumobj, size_t, "Max number of objects to declare a clean image. If more blobs are " "detected in a frame, we skip that frame before we even try to analyze shapes of the blobs", 100, ParamCateg)
Parameter.
void onParamChange(scue const &, unsigned char const &) override
virtual void process(jevois::InputFrame &&inframe) override
Processing function, no USB video output.
std::shared_ptr< Kalman1D > itsKalV
void detect(cv::Mat const &imghsv, size_t tnum, int dispx=3, int dispy=242, jevois::RawImage *outimg=nullptr)
HSV object detector, we run several of those in parallel with different hsvcue settings.
void onParamChange(hcue const &, unsigned char const &) override
jevois::CameraCalibration itsCalib
Camera calibration parameters.
std::vector< detection > itsDetections
Our detections, combined across all threads.
FirstVision(std::string const &instance)
Constructor.
virtual void process(jevois::InputFrame &&inframe, jevois::OutputFrame &&outframe) override
Processing function, with USB video output.
JEVOIS_DECLARE_PARAMETER(iou, double, "Intersection-over-union ratio over which duplicates are eliminated", 0.3, jevois::Range< double >(0.01, 0.99), ParamCateg)
Parameter.
std::shared_ptr< Kalman1D > itsKalH
Kalman filters to learn and adapt HSV windows over time.
void drawDetections(jevois::RawImage &outimg, std::vector< std::vector< cv::Point2f > > corners, std::vector< cv::Vec3d > const &rvecs, std::vector< cv::Vec3d > const &tvecs)
JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(scue, unsigned char, "Initial cue for target saturation lower bound", 50, ParamCateg)
Parameter.
bool itsCueChanged
True when users change ranges.
void updateStructuringElements()
Update the morphology structuring elements if needed.
JEVOIS_DECLARE_PARAMETER(dopose, bool, "Compute (and show) 6D object pose, requires a valid camera calibration. " "When dopose is true, 3D serial messages are sent out, otherwise 2D serial messages.", true, ParamCateg)
Parameter.
void cleanupDetections()
Clean up the detections by eliminating duplicates:
JEVOIS_DECLARE_PARAMETER(epsilon, double, "Shape smoothing factor (higher for smoother). Shape smoothing is applied " "to remove small contour defects before the shape is analyzed.", 0.015, jevois::Range< double >(0.001, 0.999), ParamCateg)
Parameter.
void estimatePose(std::vector< std::vector< cv::Point2f > > &corners, cv::OutputArray _rvecs, cv::OutputArray _tvecs)
Estimate 6D pose of detected objects, if dopose parameter is true, otherwise just 2D corners.
JEVOIS_DECLARE_PARAMETER(hullarea, jevois::Range< unsigned int >, "Range of object area (in pixels) to track. Use this " "if you want to skip shape analysis of very large or very small blobs", jevois::Range< unsigned int >(20 *20, 300 *300), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(hcue, unsigned char, "Initial cue for target hue (0=red/do not use because of " "wraparound, 30=yellow, 45=light green, 60=green, 75=green cyan, 90=cyan, " "105=light blue, 120=blue, 135=purple, 150=pink)", 45, jevois::Range< unsigned char >(0, 179), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(debug, bool, "Show contours of all object candidates if true", false, ParamCateg)
Parameter.
void updateHSV(size_t nthreads)
Initialize (e.g., if user changes cue params) or update our HSV detection ranges.
JEVOIS_DECLARE_PARAMETER(hullfill, int, "Max fill ratio of the convex hull (percent). Lower values mean your shape " "occupies a smaller fraction of its convex hull. This parameter sets an upper bound, " "fuller shapes will be rejected.", 50, jevois::Range< int >(1, 100), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(threads, size_t, "Number of parallel vision processing threads. Thread 0 uses the HSV values " "provided by user parameters thread 1 broadens that fixed range a bit threads 2-3 use a " "narrow and broader learned HSV window over time", 4, jevois::Range< size_t >(2, 4), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(objsize, cv::Size_< float >, "Object size (in meters)", cv::Size_< float >(0.28F, 0.175F), ParamCateg)
Parameter.
void onParamChange(vcue const &, unsigned char const &) override
JEVOIS_DECLARE_PARAMETER(erodesize, size_t, "Erosion structuring element size (pixels), or 0 for no erosion", 2, ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER_WITH_CALLBACK(vcue, unsigned char, "Initial cue for target value (brightness) lower bound", 200, ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(dilatesize, size_t, "Dilation structuring element size (pixels), or 0 for no dilation", 4, ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(ethresh, double, "Shape error threshold (lower is stricter for exact shape)", 900.0, jevois::Range< double >(0.01, 1000.0), ParamCateg)
Parameter.
JEVOIS_DECLARE_PARAMETER(margin, size_t, "Margin from from frame borders (pixels). If any corner of a detected shape " "gets closer than the margin to the frame borders, the shape will be rejected. This is to " "avoid possibly bogus 6D pose estimation when the shape starts getting truncated as it " "partially exits the camera's field of view.", 5, ParamCateg)
Parameter.
std::shared_ptr< Kalman1D > itsKalS
void learnHSV(size_t nthreads, cv::Mat const &imgbgr, jevois::RawImage *outimg=nullptr)
Learn and update our HSV ranges.
cv::Mat itsErodeElement
Erosion and dilation kernels shared across all detect threads.
std::vector< hsvcue > itsHSV
void postInit()
Load camera calibration on init.
JEVOIS_DECLARE_PARAMETER(showthread, size_t, "Thread number that is used to display HSV-thresholded image", 0, jevois::Range< size_t >(0, 3), ParamCateg)
Parameter.
void require(char const *info, unsigned int w, unsigned int h, unsigned int f) const
void sendSerialStd3D(float x, float y, float z, float w=0.0F, float h=0.0F, float d=0.0F, float q1=0.0F, float q2=0.0F, float q3=0.0f, float q4=0.0F, std::string const &id="", std::string const &extra="")
StdModule(std::string const &instance)
void sendSerialContour2D(unsigned int camw, unsigned int camh, std::vector< cv::Point_< T > > points, std::string const &id="", std::string const &extra="")
std::string const & stop(double *seconds)
std::string warnAndIgnoreException(std::string const &prefix="")
void paste(RawImage const &src, RawImage &dest, int dx, int dy)
void writeText(RawImage &img, std::string const &txt, int x, int y, unsigned int col, Font font=Font6x10)
void drawFilledRect(RawImage &img, int x, int y, unsigned int w, unsigned int h, unsigned int col)
cv::Mat convertToCvBGR(RawImage const &src)
void drawLine(RawImage &img, int x1, int y1, int x2, int y2, unsigned int thick, unsigned int col)
void pasteGreyToYUYV(cv::Mat const &src, RawImage &dest, int dx, int dy)
std::future< std::invoke_result_t< std::decay_t< Function >, std::decay_t< Args >... > > async(Function &&f, Args &&... args)
std::string sformat(char const *fmt,...) __attribute__((format(__printf__
unsigned short constexpr MedPurple
unsigned short constexpr Black
unsigned short constexpr LightPink
unsigned short constexpr White
unsigned short constexpr MedGreen
unsigned short constexpr MedGrey
unsigned short constexpr LightGreen
Helper struct for a detected object.
std::vector< cv::Point > approx
Smoothed approximation of the contour.
size_t threadnum
Thread number that detected this object.
std::vector< cv::Point > hull
Convex hull of the contour.
std::vector< cv::Point > contour
The full detailed contour.
float serr
Shape error score (higher for rougher contours with defects)
Helper struct for an HSV range triplet, where each range is specified as a mean and sigma:
float sis
Mean and sigma for S.
cv::Scalar rmax() const
Get maximum triplet for use by cv::inRange()
void fix()
Fix ranges so they don't go out of bounds.
float siv
Mean and sigma for V.
float sih
Mean and sigma for H.
hsvcue(unsigned char h, unsigned char s, unsigned char v)
Constructor.
cv::Scalar rmin() const
Get minimum triplet for use by cv::inRange()
hsvcue(unsigned char h, unsigned char hsig, unsigned char s, unsigned char ssig, unsigned char v, unsigned char vsig)
Constructor.