Created
February 16, 2018 12:02
-
-
Save elliotwoods/ea9535667d346061b416c4f302f22628 to your computer and use it in GitHub Desktop.
Comparison of subpixel routines
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "pch.h" | |
#include "ofApp.h" | |
//-------------------------------------------------------------- | |
void ofApp::setup(){ | |
this->gui.init(); | |
} | |
//-------------------------------------------------------------- | |
void ofApp::init() { | |
try { | |
auto directory = ofDirectory("."); | |
directory.listDir(); | |
ofxRulr::Utils::ScopedProcess scopedProcess("Loading images", true, directory.size()); | |
for (const auto & file : directory) { | |
ofxRulr::Utils::ScopedProcess scopedProcess("Loading " + file.getFileName(), false); | |
auto extension = ofToLower(file.getExtension()); | |
if (extension != "jpg") { | |
cout << file.path(); | |
continue; | |
} | |
auto cameraImage = make_unique<CameraImage>(); | |
cameraImage->filename = file.getFileName(); | |
cameraImage->image.load(file); | |
if (cameraImage->image.getPixels().getNumChannels() != 1) { | |
//we're jpg so this is actually always true | |
cv::cvtColor(ofxCv::toCv(cameraImage->image.getPixels()) | |
, cameraImage->grayscale | |
, CV_RGB2GRAY); | |
} | |
else { | |
cameraImage->grayscale = ofxCv::toCv(cameraImage->image.getPixels()); | |
} | |
if (!this->cameraImages.empty()) { | |
const auto & firstImage = this->cameraImages.front(); | |
if (cameraImage->image.getWidth() != firstImage->image.getWidth() | |
|| cameraImage->image.getHeight() != firstImage->image.getHeight()) { | |
throw(ofxRulr::Exception("Image size mismatch")); | |
} | |
} | |
{ | |
//build panel | |
cameraImage->panel = this->gui.add(cameraImage->image, cameraImage->filename); | |
auto & imageRef = *cameraImage; | |
cameraImage->panel->onDrawImage += [&imageRef](ofxCvGui::DrawImageArguments & args) { | |
for (const auto & marker : imageRef.markers) { | |
ofPolyline line; | |
line.addVertex(ofxCv::toOf(marker[0])); | |
line.addVertex(ofxCv::toOf(marker[1])); | |
line.addVertex(ofxCv::toOf(marker[2])); | |
line.addVertex(ofxCv::toOf(marker[3])); | |
line.addVertex(ofxCv::toOf(marker[0])); | |
ofPushStyle(); | |
{ | |
ofSetColor(0); | |
ofSetLineWidth(3.0f); | |
line.draw(); | |
ofSetColor(255, 0, 0); | |
ofSetLineWidth(1.0f); | |
line.draw(); | |
} | |
ofPopStyle(); | |
} | |
}; | |
} | |
this->cameraImages.emplace_back(move(cameraImage)); | |
} | |
if (this->cameraImages.empty()) { | |
throw(ofxRulr::Exception("No images found. Please add evaluation images to the data folder.")); | |
} | |
scopedProcess.end(); | |
} | |
RULR_CATCH_ALL_TO_ALERT; | |
try { | |
ofxRulr::Utils::ScopedProcess scopedProcess("Finding markers", true, this->cameraImages.size()); | |
aruco::MarkerDetector markerDetector; | |
markerDetector.setDetectionMode(aruco::DetectionMode::DM_NORMAL); | |
markerDetector.setDictionary(aruco::Dictionary::ARUCO_MIP_36h12); | |
for (const auto & cameraImage : this->cameraImages) { | |
ofxRulr::Utils::ScopedProcess scopedProcess("Analyzing " + cameraImage->filename, false); | |
//just in case aruco does anything at all with the RGB info | |
markerDetector.detect(ofxCv::toCv(cameraImage->image.getPixels()), cameraImage->markers); | |
for (auto & marker : cameraImage->markers) { | |
//perform sub-pixel detection if we're on our winged marker | |
if (marker.id == WINGED_MARKER_ID) { | |
int windowSize = (ofxCv::toOf(marker[1]) - ofxCv::toOf(marker[0])).length() / 16; | |
cv::TermCriteria criteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER | |
, 30 | |
, 0.001); | |
cv::cornerSubPix(cameraImage->grayscale | |
, (vector<cv::Point2f> &)marker | |
, cv::Size(windowSize, windowSize) | |
, cv::Size(2, 2) | |
, criteria); | |
} | |
//record image points | |
cameraImage->markerImagePoints.emplace(marker.id, marker); | |
} | |
} | |
scopedProcess.end(); | |
} | |
RULR_CATCH_ALL_TO_ALERT; | |
try { | |
//calculate deviations | |
{ | |
//set blank values | |
this->cornerResults.emplace(0, vector<CornerResult>(4)); | |
this->cornerResults.emplace(1, vector<CornerResult>(4)); | |
} | |
//accumulate data | |
for (const auto & cameraImage : this->cameraImages) { | |
for (const auto & marker : cameraImage->markers) { | |
for (int i = 0; i < 4; i++) { | |
this->cornerResults[marker.id][i].positions.push_back(marker[i]); | |
} | |
} | |
} | |
//calculate | |
for (auto & markerCornerResult : this->cornerResults) { | |
for (auto & cornerResult : markerCornerResult.second) { | |
https://stackoverflow.com/questions/11567307/calculate-mean-for-vector-of-points | |
cv::Mat mean_; | |
cv::reduce(cornerResult.positions, mean_, 1, CV_REDUCE_AVG); | |
cornerResult.mean.x = mean_.at<float>(0, 0); | |
cornerResult.mean.y = mean_.at<float>(0, 1); | |
float deviation = 0.0f; | |
for (const auto & position : cornerResult.positions) { | |
deviation += (ofxCv::toOf(position) - ofxCv::toOf(cornerResult.mean)).lengthSquared(); | |
} | |
deviation /= cornerResult.positions.size(); | |
deviation = sqrt(deviation); | |
cornerResult.deviation = deviation; | |
} | |
} | |
} | |
RULR_CATCH_ALL_TO_ALERT; | |
try { | |
//add the summary panel | |
cv::Mat average; | |
{ | |
const auto & firstImage = this->cameraImages.front()->image; | |
cv::Mat accumulate = cv::Mat::zeros(cv::Size(firstImage.getWidth(), firstImage.getHeight()), CV_32F); | |
for (const auto & cameraImage : this->cameraImages) { | |
cv::add(accumulate | |
, cameraImage->grayscale | |
, accumulate | |
, cv::Mat() | |
, CV_32F); | |
} | |
accumulate /= (float) this->cameraImages.size(); | |
accumulate.convertTo(average, CV_8U); | |
} | |
ofxCv::copy(average, this->previewAverage.getPixels()); | |
this->previewAverage.update(); | |
auto panel = this->gui.add(this->previewAverage, "Summary"); | |
panel->onDrawImage += [this](ofxCvGui::DrawImageArguments &) { | |
ofPushStyle(); | |
{ | |
ofSetColor(255, 0, 0); | |
for (const auto & cornerResultMarker : this->cornerResults) { | |
for (const auto & cornerResult : cornerResultMarker.second) { | |
for (const auto & position : cornerResult.positions) { | |
ofPushMatrix(); | |
{ | |
//draw a cross | |
ofTranslate(ofxCv::toOf(position)); | |
ofDrawLine(-5, 0, 5, 0); | |
ofDrawLine(0, -5, 0, 5); | |
} | |
ofPopMatrix(); | |
} | |
ofDrawBitmapString("Deviation : " + ofToString(cornerResult.deviation), ofxCv::toOf(cornerResult.mean)); | |
} | |
} | |
} | |
ofPopStyle(); | |
}; | |
} | |
RULR_CATCH_ALL_TO_ALERT; | |
} | |
//-------------------------------------------------------------- | |
void ofApp::update(){ | |
if (ofGetFrameNum() == 1) { | |
this->init(); | |
} | |
} | |
//-------------------------------------------------------------- | |
void ofApp::draw(){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::keyPressed(int key){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::keyReleased(int key){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseMoved(int x, int y ){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseDragged(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mousePressed(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseReleased(int x, int y, int button){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseEntered(int x, int y){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::mouseExited(int x, int y){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::windowResized(int w, int h){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::gotMessage(ofMessage msg){ | |
} | |
//-------------------------------------------------------------- | |
void ofApp::dragEvent(ofDragInfo dragInfo){ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "ofMain.h" | |
#include "ofxCvGui.h" | |
#include "ofxCvMin.h" | |
#include "ofxRulr.h" | |
#define WINGED_MARKER_ID 1 | |
struct CameraImage { | |
string filename; | |
ofImage image; | |
cv::Mat grayscale; | |
shared_ptr<ofxCvGui::Panels::Image> panel; | |
vector<aruco::Marker> markers; | |
map<size_t, vector<cv::Point2f>> markerImagePoints; | |
}; | |
struct CornerResult { | |
cv::Point2f mean; | |
float deviation; | |
vector<cv::Point2f> positions; | |
}; | |
class ofApp : public ofBaseApp{ | |
public: | |
void setup(); | |
void init(); | |
void update(); | |
void draw(); | |
void keyPressed(int key); | |
void keyReleased(int key); | |
void mouseMoved(int x, int y ); | |
void mouseDragged(int x, int y, int button); | |
void mousePressed(int x, int y, int button); | |
void mouseReleased(int x, int y, int button); | |
void mouseEntered(int x, int y); | |
void mouseExited(int x, int y); | |
void windowResized(int w, int h); | |
void dragEvent(ofDragInfo dragInfo); | |
void gotMessage(ofMessage msg); | |
ofxCvGui::Builder gui; | |
vector<unique_ptr<CameraImage>> cameraImages; | |
ofImage previewAverage; | |
map<int, vector<CornerResult>> cornerResults; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment