カメラキャリブレーションのための自動撮影機能を背景差分を使って作る
動体検知をして、動いたものを見つけた後、一定時間画面に変化がないとシャッターを切ります。
単純な閾値処理しかしていませんが、結構ちゃんと動きます。
ただし、閾値がカメラ解像度に依存する上に、閾値の設定次第で使いやすさがまるで変ってしまいますが。
OpenFrameworks9.3とOpenCV3.1を使っています。
ofxGuiアドオンとofxHistoryPlot( GitHub - armadillu/ofxHistoryPlot: Visualize value history on a configurable graph )も使います。
ofApp.h
#pragma once #include "ofMain.h" #include "ofxGui.h" #include "ofxHistoryPlot.h" #include "opencv2/opencv.hpp" class ofApp : public ofBaseApp{ public: void setup(); 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); cv::Mat frame; cv::Mat fgMask; cv::Mat bgMask; cv::Ptr<cv::BackgroundSubtractorMOG2> pMOG2; cv::VideoCapture cap; ofxPanel gui; ofxFloatSlider background_ratio; ofxFloatSlider complexity_reduction_threshold; ofxToggle shadow_detection_flag; ofxIntSlider history; // the number of last frames that affect the background model ofxIntSlider nMixtures; // the number of gaussian components in the background model ofxFloatSlider shadow_threshold; ofxIntSlider shadow_value; ofxFloatSlider varInit; // the initial variance of each gaussian component ofxFloatSlider varMax; ofxFloatSlider varMin; ofxFloatSlider varThreshold; // the variance threshold for the pixel - model match ofxFloatSlider varThresholdGen; // the variance threshold for the pixel - model match used for new mixture component generation. void BackgroundRatioChanged(float& value) { pMOG2->setBackgroundRatio(value); } void ComplexityReductionThresholdChanged(float& value) { pMOG2->setComplexityReductionThreshold(value); } void ShadowDetectionFlagChanged(bool& value) { pMOG2->setDetectShadows(value); } void HistoryChanged(int & value) { pMOG2->setHistory(value); } void nMixturesChanged(int & value) { pMOG2->setNMixtures(value); } void ShadowThresholdChanged(float & value) { pMOG2->setShadowThreshold(value); } void ShadowValueChanged(int & value) { pMOG2->setShadowValue(value); } void VarInitChanged(float & value) { pMOG2->setVarInit(value); } void VarMaxChanged(float & value) { pMOG2->setVarMax(value); varMin.setMax(value); } void VarMinChanged(float & value) { pMOG2->setVarMin(value); varMax.setMin(value); } void VarThresholdChanged(float & value) { pMOG2->setVarThreshold(value); } void VarThresholdGenChanged(float & value) { pMOG2->setVarThresholdGen(value); } ofxHistoryPlot * plot; ofxPanel auto_shutter; ofxFloatSlider high_threshold; ofxFloatSlider low_threshold; int detection_state; void HighThresholdChanged(float & value) { low_threshold.setMax(value); } void LowThresholdChanged(float & value) { high_threshold.setMin(value); } vector<cv::Mat> calib; };
ofApp.cpp
#include "ofApp.h" #include "opencv2/opencv.hpp" #pragma comment(lib, "opencv_calib3d310.lib") #pragma comment(lib, "opencv_core310.lib") #pragma comment(lib, "opencv_features2d310.lib") #pragma comment(lib, "opencv_flann310.lib") #pragma comment(lib, "opencv_highgui310.lib") #pragma comment(lib, "opencv_imgcodecs310.lib") #pragma comment(lib, "opencv_imgproc310.lib") #pragma comment(lib, "opencv_ml310.lib") #pragma comment(lib, "opencv_objdetect310.lib") #pragma comment(lib, "opencv_photo310.lib") #pragma comment(lib, "opencv_shape310.lib") #pragma comment(lib, "opencv_stitching310.lib") #pragma comment(lib, "opencv_superres310.lib") #pragma comment(lib, "opencv_ts310.lib") #pragma comment(lib, "opencv_video310.lib") #pragma comment(lib, "opencv_videoio310.lib") #pragma comment(lib, "opencv_videostab310.lib") //-------------------------------------------------------------- void ofApp::setup() { cap.open(1); cap.set(cv::CAP_PROP_FRAME_WIDTH, 1280); cap.set(cv::CAP_PROP_FRAME_HEIGHT, 720); if (!cap.isOpened()) { cout << "Can't open the camera" << endl; ofExit(); } cap >> frame; int initHistory = 200; float initVarThreshold = 16; bool bShadowDetection = true; pMOG2 = cv::createBackgroundSubtractorMOG2(initHistory, initVarThreshold, bShadowDetection); pMOG2->setVarThreshold(10); gui.setup("settings", "settings.xml", 10, 10); gui.add(background_ratio.setup("background ratio", pMOG2->getBackgroundRatio(), 0.0, 1.0)); gui.add(complexity_reduction_threshold.setup("complexity reduction", pMOG2->getComplexityReductionThreshold(), 0.0, 1.0)); gui.add(shadow_detection_flag.setup("shadow detection flag", pMOG2->getDetectShadows())); gui.add(history.setup("hisotry", pMOG2->getHistory(), 1, 1000)); gui.add(nMixtures.setup("nMixtures", pMOG2->getNMixtures(), 1, 100)); gui.add(shadow_threshold.setup("shadow threshold", pMOG2->getShadowThreshold(), -1.0, 1.0)); gui.add(varInit.setup("varInit", pMOG2->getVarInit(), 0.0, 100.0)); gui.add(varMax.setup("varMax", pMOG2->getVarMax(), pMOG2->getVarMin(), 10000.0)); gui.add(varMin.setup("varMin", pMOG2->getVarMin(), 0.0, pMOG2->getVarMax())); gui.add(varThreshold.setup("varThreshold", pMOG2->getVarThreshold(), 0.0, 10000.0)); gui.add(varThresholdGen.setup("varThresholdGen", pMOG2->getVarThresholdGen(), 0.0, 10000.0)); background_ratio.addListener(this, &ofApp::BackgroundRatioChanged); complexity_reduction_threshold.addListener(this, &ofApp::ComplexityReductionThresholdChanged); shadow_detection_flag.addListener(this, &ofApp::ShadowDetectionFlagChanged); history.addListener(this, &ofApp::HistoryChanged); nMixtures.addListener(this, &ofApp::nMixturesChanged); shadow_threshold.addListener(this, &ofApp::ShadowThresholdChanged); varInit.addListener(this, &ofApp::VarInitChanged); varMax.addListener(this, &ofApp::VarMaxChanged); varMin.addListener(this, &ofApp::VarMinChanged); varThreshold.addListener(this, &ofApp::VarThresholdChanged); varThresholdGen.addListener(this, &ofApp::VarThresholdGenChanged); // History Plot int numSamples = 350; plot = new ofxHistoryPlot(NULL, "Ratio", numSamples, false); //NULL cos we don't want it to auto-update. confirmed by "true" plot->setRange(0, 10000.f / sqrt(frame.cols * frame.rows) ); //hard range, will not adapt to values off-scale plot->addHorizontalGuide(ofGetHeight() / 2, ofColor(255, 0, 0)); //add custom reference guides plot->setColor(ofColor(0, 255, 0)); //color of the plot line plot->setShowNumericalInfo(true); //show the current value and the scale in the plot plot->setRespectBorders(true); //dont let the plot draw on top of text plot->setLineWidth(1); //plot line width plot->setBackgroundColor(ofColor(0, 220)); //custom bg color //custom grid setup plot->setDrawGrid(true); plot->setGridColor(ofColor(30)); //grid lines color plot->setGridUnit(14); plot->setCropToRect(true); normalize_ratio = 1.0f / sqrt(frame.cols*frame.rows); auto_shutter.setup("shutter"); auto_shutter.add(high_threshold.setup("high", 3200.f * normalize_ratio, 1800.f * normalize_ratio, 10000.f * normalize_ratio)); auto_shutter.add(low_threshold.setup("low", 1800.f * normalize_ratio, 0, high_threshold)); high_threshold.addListener(this, &ofApp::HighThresholdChanged); low_threshold.addListener(this, &ofApp::LowThresholdChanged); detection_state = 0; } //-------------------------------------------------------------- void ofApp::update() { if (cap.grab()) { cap >> frame; pMOG2->apply(frame, fgMask); pMOG2->getBackgroundImage(bgMask); cv::Scalar v = cv::sum(fgMask); plot->update(sqrt(v[0]) * normalize_ratio); if (detection_state == 0) { if (sqrt(v[0]) * normalize_ratio > high_threshold) { detection_state = 1; } } else if (detection_state == 1) { if (sqrt(v[0]) * normalize_ratio < low_threshold) { detection_state = 0; calib.push_back(cv::Mat()); frame.copyTo(calib.back()); cv::imshow("calib", calib.back()); cout << "calib" << std::to_string(calib.size()) << endl; } } } } //-------------------------------------------------------------- void ofApp::draw() { if (!fgMask.empty()) { cv::imshow("FG", fgMask); } if (!bgMask.empty()) { cv::imshow("BG", bgMask); } gui.draw(); plot->draw(220, 10, 640, 240); } //-------------------------------------------------------------- void ofApp::keyPressed(int key) { if (key == 's') { int cameraIdx = 0; for (int i = 0; i < calib.size(); i++) { stringstream ss; ss << to_string(cameraIdx) << '-' << /* std::setw(3) << std::setfill('0') <<*/ i << ".png"; imwrite(ss.str(), calib[i]); cout << "wrote " << ss.str() << endl; } } } //-------------------------------------------------------------- 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) { }
本日の防災
乾電池はある程度手元にないと不安ですね。
- 出版社/メーカー: AmazonBasics
- メディア: ヘルスケア&ケア用品
- この商品を含むブログを見る
- 出版社/メーカー: AmazonBasics
- メディア: ヘルスケア&ケア用品
- この商品を含むブログを見る