読者です 読者をやめる 読者になる 読者になる

cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

カメラキャリブレーションのための自動撮影機能を背景差分を使って作る

動体検知をして、動いたものを見つけた後、一定時間画面に変化がないとシャッターを切ります。
単純な閾値処理しかしていませんが、結構ちゃんと動きます。
ただし、閾値がカメラ解像度に依存する上に、閾値の設定次第で使いやすさがまるで変ってしまいますが。
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) {

}

f:id:cvl-robot:20160419223815p:plain

本日の防災

乾電池はある程度手元にないと不安ですね。

Amazonベーシック アルカリ乾電池 単3形20個パック

Amazonベーシック アルカリ乾電池 単3形20個パック

Amazonベーシック アルカリ乾電池 単4形20個パック

Amazonベーシック アルカリ乾電池 単4形20個パック