From 8c413c5adbe7a8c8a3cbb01d07d85e8745f1d09c Mon Sep 17 00:00:00 2001 From: Zer01HD Date: Tue, 8 May 2018 13:39:15 -0500 Subject: [PATCH] tuning; better tracking; began on distance calc; --- src/Rectangle/BoundTracker.cpp | 4 ++ src/Rectangle/BoundTracker.h | 1 + src/Rectangle/Confidence.cpp | 7 +++- src/Rectangle/Confidence.h | 4 +- src/Rectangle/ContourAnalyzer.cpp | 51 +++++++++++++++------- src/Rectangle/ContourAnalyzer.h | 5 ++- src/Rectangle/Ruler.cpp | 70 +++++++++++++++++++++++++++++++ src/Rectangle/Ruler.h | 41 ++++++++++++++++++ src/UI.cpp | 10 +++-- src/UI.h | 7 ++-- src/main.cpp | 67 ++++++++++++++++++++++------- 11 files changed, 225 insertions(+), 42 deletions(-) create mode 100755 src/Rectangle/Ruler.cpp create mode 100755 src/Rectangle/Ruler.h diff --git a/src/Rectangle/BoundTracker.cpp b/src/Rectangle/BoundTracker.cpp index 72d6f58..d66f22f 100755 --- a/src/Rectangle/BoundTracker.cpp +++ b/src/Rectangle/BoundTracker.cpp @@ -31,6 +31,10 @@ bool BoundTracker::track() { } } +void BoundTracker::setROI(cv::Rect2d roi) { + BoundTracker::roi = roi; +} + cv::Rect2d BoundTracker::getROI() const { return roi; } diff --git a/src/Rectangle/BoundTracker.h b/src/Rectangle/BoundTracker.h index d097e89..cfba046 100755 --- a/src/Rectangle/BoundTracker.h +++ b/src/Rectangle/BoundTracker.h @@ -17,6 +17,7 @@ public: BoundTracker(int *lastKey, UI *ui); void init(cv::Rect2d roi); bool track(); + void setROI(cv::Rect2d roi); cv::Rect2d getROI() const; virtual ~BoundTracker(); }; diff --git a/src/Rectangle/Confidence.cpp b/src/Rectangle/Confidence.cpp index f381685..03a450f 100755 --- a/src/Rectangle/Confidence.cpp +++ b/src/Rectangle/Confidence.cpp @@ -1,9 +1,10 @@ #include "Confidence.h" -Confidence::Confidence(int *lastKey, UI *ui, unsigned validityLength) { +Confidence::Confidence(int *lastKey, UI *ui, unsigned validityLength, float similarity) { queueLength = validityLength; Confidence::lastKey = lastKey; Confidence::ui = ui; + Confidence::similarity = similarity; } bool Confidence::check(std::vector bounds) { @@ -29,7 +30,7 @@ bool Confidence::check(std::vector bounds) { } for (unsigned frameRectID = 0; frameRectID < confidenceQueue[frameID].size(); frameRectID++) { cv::Rect previous = confidenceQueue[frameID][frameRectID]; - if ((current & previous).area() < minimumDifference*(current.area())) { + if ((current & previous).area() < similarity*(current.area())) { consistent = false; break; } @@ -47,6 +48,8 @@ bool Confidence::check(std::vector bounds) { if (drawBounds) { drawBoundsOnMat(*(ui->drawnFrame())); + cv::putText(*(ui->drawnFrame()), "Showing confident rectangles.", + cv::Point{5, 45}, cv::FONT_HERSHEY_PLAIN, 0.8, boundColor); } // std::cout << confident.size() << std::endl; diff --git a/src/Rectangle/Confidence.h b/src/Rectangle/Confidence.h index 1db1d50..266dd43 100755 --- a/src/Rectangle/Confidence.h +++ b/src/Rectangle/Confidence.h @@ -6,7 +6,7 @@ #include "../UI.h" class Confidence { - float minimumDifference = 0.76; + float similarity; unsigned queueLength = 0; int *lastKey; UI *ui; @@ -17,7 +17,7 @@ public: std::deque> confidenceQueue; std::vector confident; - Confidence(int *lastKey, UI *ui, unsigned history = 5); + Confidence(int *lastKey, UI *ui, unsigned history = 5, float similarity = 0.75); bool check(std::vector contours); std::vector rectangleBounds() const; void drawBoundsOnMat(cv::Mat &mat); diff --git a/src/Rectangle/ContourAnalyzer.cpp b/src/Rectangle/ContourAnalyzer.cpp index e5d6e12..9a315e6 100755 --- a/src/Rectangle/ContourAnalyzer.cpp +++ b/src/Rectangle/ContourAnalyzer.cpp @@ -10,18 +10,7 @@ ContourAnalyzer::ContourAnalyzer(UI *ui, int *lastKey) { } void ContourAnalyzer::analyze() { - prunedContours.clear(); - if (*lastKey == 99) { - cannyRec = !cannyRec; - } else if (*lastKey == 118) { - drawContours = !drawContours; - } - minSize = ui->getWidth()*ui->getHeight()*(minSizeScale/(float)10000); - - if (cannyRec) { - cannyUpper = std::min(3*cannyLower, 255); - cv::setTrackbarPos("Can. Upper", UI::DEBUG_WINDOW, cannyUpper); - } + preAnalyze(); cv::cvtColor(*(ui->currentFrame(-1)), *(ui->nextFrame()), cv::COLOR_BGR2GRAY); cv::GaussianBlur(*(ui->currentFrame(-1)), *(ui->nextFrame()), cv::Size(5, 5), 0); @@ -29,7 +18,30 @@ void ContourAnalyzer::analyze() { cv::Canny(*(ui->currentFrame(-1)), *(ui->nextFrame()), cannyLower, cannyUpper); cv::findContours(*(ui->currentFrame()), contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); + removeRedundancy(); + if (drawContours) { + drawContoursOntoMat(*(ui->drawnFrame())); + cv::putText(*(ui->drawnFrame()), "Showing possible triangles rectangles.", + cv::Point{5, 30}, cv::FONT_HERSHEY_PLAIN, 0.8, selectColor); + } +} + +void ContourAnalyzer::analyze(cv::Rect rect) { + preAnalyze(); + cv::Mat frame; + ui->getOriginalFrame().copyTo(frame); + + cv::Mat cropped = frame(rect); + cv::cvtColor(cropped, cropped, cv::COLOR_BGR2GRAY); + cv::GaussianBlur(cropped, cropped, cv::Size(5, 5), 0); + cv::threshold(cropped, cropped, binLower, 255, cv::THRESH_BINARY); + cv::Canny(cropped, cropped, cannyLower, cannyUpper); + cv::findContours(cropped, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE); + removeRedundancy(); +} + +void ContourAnalyzer::removeRedundancy() { for (unsigned i = 0; i < contours.size(); i++) { if ((cv::contourArea(contours[i]) > minSize) && (cv::contourArea(contours[i]) < (ui->currentFrame(0)->cols * ui->currentFrame(0)->rows) -25)) { double perimeter = cv::arcLength(contours[i], true); @@ -62,9 +74,20 @@ void ContourAnalyzer::analyze() { } } +} - if (drawContours) { - drawContoursOntoMat(*(ui->drawnFrame())); +void ContourAnalyzer::preAnalyze() { + prunedContours.clear(); + if (*lastKey == 99) { + cannyRec = !cannyRec; + } else if (*lastKey == 118) { + drawContours = !drawContours; + } + minSize = ui->getWidth()*ui->getHeight()*(minSizeScale/(float)10000); + + if (cannyRec) { + cannyUpper = std::min(3*cannyLower, 255); + cv::setTrackbarPos("Can. Upper", UI::DEBUG_WINDOW, cannyUpper); } } diff --git a/src/Rectangle/ContourAnalyzer.h b/src/Rectangle/ContourAnalyzer.h index 2735018..0372dae 100755 --- a/src/Rectangle/ContourAnalyzer.h +++ b/src/Rectangle/ContourAnalyzer.h @@ -22,8 +22,10 @@ class ContourAnalyzer: public UIListener { std::vector> prunedContours; std::vector bounds; + void removeRedundancy(); + void preAnalyze(); public: - int binLower = 52; + int binLower = 58; int minSize = 0; int minSizeScale = 18; @@ -37,6 +39,7 @@ public: cv::Scalar selectColor { 0, 255, 0 }; cv::Scalar fontColor { 0, 0, 255 }; void analyze(); + void analyze(cv::Rect rect); void drawContoursOntoMat(cv::Mat &frame); std::string contourToString(std::vector); void convertContoursToBounds(); diff --git a/src/Rectangle/Ruler.cpp b/src/Rectangle/Ruler.cpp new file mode 100755 index 0000000..dc69ca6 --- /dev/null +++ b/src/Rectangle/Ruler.cpp @@ -0,0 +1,70 @@ +/* + * Ruler.cpp + * + * Created on: May 8, 2018 + * Author: yunya + */ + +#include "Ruler.h" +#include + +Ruler::Ruler(UI *ui, int *lastKey) { + Ruler::ui = ui; + Ruler::lastKey = lastKey; + focalLength = estimateFocalLength(); +} + +void Ruler::update(cv::Rect rect) { + focalLength = estimateFocalLength(); + distance = estimateDistance(rect); + inputUpdate(); + updateUI(); +} + +float Ruler::estimateFocalLength(int HFOV) { + return (ui->getWidth()/2.0)*(1/(std::tan(HFOV/2.0))); +} + +void Ruler::updateUI() { + if (inputtingVal) { + cv::putText(*(ui->drawnFrame()), ("Given width: " << widthInCM), confTextPos, cv::FONT_HERSHEY_DUPLEX, 0.7, textFontColor + 50); + } else { + cv::putText(*(ui->drawnFrame()), ("Given width: " << widthInCM), confTextPos, cv::FONT_HERSHEY_DUPLEX, 0.7, textFontColor); + } + cv::putText(*(ui->drawnFrame()), ("distance: " << distance << "cm"), distanceTextPos, cv::FONT_HERSHEY_DUPLEX, 0.7, textFontColor); + +} + +void Ruler::toggleInput() { + inputtingVal = !inputtingVal; + if (!inputtingVal) { + numBuild.clear(); + } +} + +int Ruler::estimateDistance(cv::Rect rect) { + return (widthInCM*focalLength) / rect.width; +} + +void Ruler::inputUpdate() { + if (*lastKey == 13) { + toggleInput(); + } + if (inputtingVal) { + if (*lastKey >= 48 && *lastKey <= 57) { + char val = '0' + (*lastKey - 48); + numBuild.push_back(val); + } + std::string numString{numBuild.begin(), numBuild.end()}; + numString >> widthInCM; + } +} + +void Ruler::componentSetup() { + +} + +Ruler::~Ruler() { + // TODO Auto-generated destructor stub +} + diff --git a/src/Rectangle/Ruler.h b/src/Rectangle/Ruler.h new file mode 100755 index 0000000..b5ce937 --- /dev/null +++ b/src/Rectangle/Ruler.h @@ -0,0 +1,41 @@ +/* + * Ruler.h + * + * Created on: May 8, 2018 + * Author: yunya + */ + +#ifndef RECTANGLE_RULER_H_ +#define RECTANGLE_RULER_H_ +#include "../UI.h" +#include "../UIListener.hpp"; + +class Ruler: public UIListener { + cv::Point distanceTextPos {5, 60}; + cv::Point confTextPos {5, 75}; + cv::Scalar textFontColor {20, 20, 20}; + + UI *ui; + int *lastKey; + float distance = 0; + float widthInCM = 0; + float focalLength; + int HFOV = 65; + bool inputtingVal = false; + std::deque numBuild; + +public: + Ruler(UI *ui, int *lastKey); + float estimateFocalLength(int HFOV = Ruler::HFOV); + int estimateDistance(cv::Rect rect); + void update(cv::Rect rect); + void componentSetup(); + void updateUI(); + void toggleInput(); + virtual ~Ruler(); + +private: + void inputUpdate(); +}; + +#endif /* RECTANGLE_RULER_H_ */ diff --git a/src/UI.cpp b/src/UI.cpp index f27696b..11fb955 100755 --- a/src/UI.cpp +++ b/src/UI.cpp @@ -8,8 +8,9 @@ const std::string UI::NORMAL_WINDOW = "Normal"; const std::string UI::DEBUG_WINDOW = "Debug"; void UI::render() { - if (state < frames.size()) { + if (state < frames.size() -1) { frames.resize(state + 1); + debugFrame = std::min(state, debugFrame); } state = 0; cv::imshow(UI::NORMAL_WINDOW, frame); @@ -60,6 +61,7 @@ void UI::setOriginalFrame(cv::Mat frame) { height = frame.rows; frames[0] = frame; frames[0].copyTo(UI::frame); + frames[0].copyTo(originalFrame); } cv::Mat* UI::drawnFrame() { @@ -67,14 +69,14 @@ cv::Mat* UI::drawnFrame() { } cv::Mat UI::getOriginalFrame() const { - return frames[0]; + return originalFrame; } -unsigned UI::getWidth() const { +int UI::getWidth() const { return width; } -unsigned UI::getHeight() const { +int UI::getHeight() const { return height; } diff --git a/src/UI.h b/src/UI.h index 88a0827..95d50ae 100755 --- a/src/UI.h +++ b/src/UI.h @@ -15,10 +15,11 @@ class UI { bool debug = false, showSlider = false; unsigned debugFrame = 0; int *lastKey; - unsigned width = 0, height = 0; + int width = 0, height = 0; unsigned state = 0; std::vector frames = {cv::Mat()}; cv::Mat frame; + cv::Mat originalFrame; public: static const std::string NORMAL_WINDOW; @@ -35,8 +36,8 @@ public: cv::Mat* drawnFrame(); cv::Mat getOriginalFrame() const; - unsigned getWidth() const; - unsigned getHeight() const; + int getWidth() const; + int getHeight() const; virtual ~UI(); }; diff --git a/src/main.cpp b/src/main.cpp index d454e4c..799f498 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,17 +15,18 @@ Webcam webcam = 0; int *lastKey = new int(0); UI *ui = new UI(lastKey); ContourAnalyzer ca { ui, lastKey }; -Confidence conf { lastKey, ui, 5}; -BoundTracker tracker {lastKey, ui}; -cv::Point statusTextPos{5, 25}; -cv::Scalar textFontColor{35, 125, 35}; -cv::Scalar rectangleBoundColor{255, 0, 0}; +Confidence conf { lastKey, ui, 6, 0.7}; +BoundTracker tracker { lastKey, ui }; +cv::Point statusTextPos {5, 15}; +cv::Scalar textFontColor { 20, 20, 20}; +cv::Scalar rectangleBoundColor { 255, 0, 0 }; unsigned frameCount = 0; void run() { switch (state) { case detection: - cv::putText(*(ui->drawnFrame()), "Looking for rectangle...", statusTextPos, cv::FONT_HERSHEY_PLAIN, 0.7, 35); + cv::putText(*(ui->drawnFrame()), "Looking for rectangle...", + statusTextPos, cv::FONT_HERSHEY_PLAIN, 0.8, textFontColor); ca.analyze(); ca.convertContoursToBounds(); if (conf.check(ca.getBounds())) { @@ -38,21 +39,53 @@ void run() { } tracker.init(largest); - state = track; } break; case track: - cv::putText(*(ui->drawnFrame()), "Tracking...", statusTextPos, cv::FONT_HERSHEY_PLAIN, 0.7, 35); + { + int growBy = 20; + cv::Rect scaledSearchArea; + cv::putText(*(ui->drawnFrame()), "Tracking...", statusTextPos, cv::FONT_HERSHEY_PLAIN, 0.8, textFontColor); if (!tracker.track()) { state = cross_check; } - cv::rectangle(*(ui->drawnFrame()), tracker.getROI(), cv::Scalar{255, 0, 0}, 2); - break; + scaledSearchArea = tracker.getROI(); + scaledSearchArea += cv::Size{growBy*2, growBy*2}; + scaledSearchArea.x -= growBy; + scaledSearchArea.y -= growBy; + + scaledSearchArea.x = std::max(scaledSearchArea.x, 0); + scaledSearchArea.y = std::max(scaledSearchArea.y, 0); + + scaledSearchArea.x = std::min(scaledSearchArea.x, ui->getWidth()-scaledSearchArea.width -1); + scaledSearchArea.y = std::min(scaledSearchArea.y, ui->getHeight()-scaledSearchArea.height -1); + + ca.analyze(scaledSearchArea); + ca.convertContoursToBounds(); + + + if (ca.getBounds().size() > 0) { + cv::Rect largest; + for (unsigned i = 0; i < ca.getBounds().size(); i++) { + if (ca.getBounds()[i].area() > largest.area()) { + largest = ca.getBounds()[i]; + } + } + + largest.x += scaledSearchArea.x; + largest.y += scaledSearchArea.y; + tracker.setROI(largest); + } + cv::rectangle(*(ui->drawnFrame()), scaledSearchArea, cv::Scalar {150, 0, 0}, 1); + cv::rectangle(*(ui->drawnFrame()), tracker.getROI(), cv::Scalar {255, 0, 0}, 2); + } + break; case cross_check: - cv::putText(*(ui->drawnFrame()), "Cross checking...", statusTextPos, cv::FONT_HERSHEY_PLAIN, 0.7, 35); + cv::putText(*(ui->drawnFrame()), "Cross track...", statusTextPos, + cv::FONT_HERSHEY_PLAIN, 0.8, textFontColor); ca.analyze(); ca.convertContoursToBounds(); cv::Rect2d largest; @@ -66,20 +99,22 @@ void run() { tracker.track(); unsigned largestArea = tracker.getROI().area() > largest.area() ? tracker.getROI().area() : largest.area(); - if (largest.area() != 0 && (tracker.getROI() & largest).area() >= largestArea*(0.75)) { + + if (largest.area() != 0 && (tracker.getROI() | largest).area() <= largestArea * (1.2)) { + tracker.setROI(largest); state = track; } else { frameCount++; - if (frameCount > 15) { + if (frameCount > 30) { state = detection; frameCount = 0; } } - cv::rectangle(*(ui->drawnFrame()), tracker.getROI(), cv::Scalar{255, 0, 0}, 2); - cv::rectangle(*(ui->drawnFrame()), largest, cv::Scalar{255, 30, 30}, 1); - + cv::rectangle(*(ui->drawnFrame()), tracker.getROI(), cv::Scalar { 255, 0, 0 }, 2); + cv::rectangle(*(ui->drawnFrame()), largest, cv::Scalar { 255, 30, 30 }, 1); break; } + ui->render(); }