Skip to content

Instantly share code, notes, and snippets.

@denkiwakame
Last active January 4, 2016 19:19
Show Gist options
  • Save denkiwakame/8667025 to your computer and use it in GitHub Desktop.
Save denkiwakame/8667025 to your computer and use it in GitHub Desktop.
グレイコードデコーダ
#include <highgui.h>
#include <cv.h>
#include <cxcore.h>
#include <assert.h>
#include <iostream>
#include <sstream>
#include <math.h>
#include "fileio_util.hpp"
class GrayCodeDecoder {
public:
// constructor
GrayCodeDecoder(int _graycode_partition_num_x, int _graycode_partitoin_num_y);
~GrayCodeDecoder();
void endecode(const std::string& dirpath, const std::string& file_ext, int first_idx);
// getter for data
cv::Mat getDecodedDataX() const;
cv::Mat getDecodedDataY() const;
cv::Mat getMaskImg() const;
std::string toBinaryString(int num) const;
// confirms result
void showDecodedResult() const;
// utils
void showImg(const cv::Mat& img, const int waiting_time) const;
private:
// called in void endecode()
void setMaskImg(const cv::Mat& img1, const cv::Mat& img2);
void setMaskContour(const cv::Mat& img);
void encodeImgX(const cv::Mat& img);
void encodeImgY(const cv::Mat& img);
// called in encodeImg(X|Y)
void _encodeImg(const cv::Mat& img, cv::Mat& decoded_data);
const int graycode_partition_num_x;
const int graycode_partition_num_y;
int img_cols;
int img_rows;
// background: I(x,y) = 0 projected area: I(x,y) = 255
cv::Mat mask_img;
std::vector< cv::Point > masked_points;
cv::Mat decoded_dataX;
cv::Mat decoded_dataY;
};
GrayCodeDecoder::GrayCodeDecoder(int _graycode_partition_num_x, int _graycode_partition_num_y)
: graycode_partition_num_x(_graycode_partition_num_x),
graycode_partition_num_y(_graycode_partition_num_y)
{
};
GrayCodeDecoder::~GrayCodeDecoder(){};
void GrayCodeDecoder::showImg(const cv::Mat& img, const int waiting_time) const
{
cv::imshow("window", img);
cv::waitKey(waiting_time);
}
void GrayCodeDecoder::endecode(const std::string& dirpath, const std::string& file_ext, int first_idx) {
// TODO
// detects projected area
cv::Mat base_img1 = cv::imread(fileio::create_filepath(0, dirpath, file_ext), 1);
assert(!base_img1.empty() && "cannot load base img");
//cv::Mat base_img2 = cv::imread(fileio::create_filepath(1, dirpath, file_ext), 1);
// this->setMaskImg(base_img1, base_img2);
this->setMaskContour(base_img1);
assert(!this->mask_img.empty() && "mask img hasn't yet been set");
// set input img size
this->img_cols = base_img1.cols;
this->img_rows = base_img1.rows;
base_img1.release();
//base_img2.release();
this->decoded_dataX = cv::Mat(this->img_rows, this->img_cols, CV_16UC1, cv::Scalar(0));
this->decoded_dataY = cv::Mat(this->img_rows, this->img_cols, CV_16UC1, cv::Scalar(0));
int ximg_idx_first = first_idx;
int ximg_idx_last = this->graycode_partition_num_x + ximg_idx_first - 1;
int yimg_idx_first = ximg_idx_last + 1;
int yimg_idx_last = this->graycode_partition_num_y + yimg_idx_first - 1;
for (int i=ximg_idx_first; i<= ximg_idx_last; i++){
cv::Mat img = cv::imread(fileio::create_filepath(i, dirpath, file_ext), 0);
assert(!img.empty() && "cannot load img");
this->showImg(img, 0);
this->encodeImgX(img);
}
for (int i=yimg_idx_first; i<= yimg_idx_last; i++){
cv::Mat img = cv::imread(fileio::create_filepath(i, dirpath, file_ext), 0);
assert(!img.empty() && "cannot load img");
this->showImg(img, 0);
this->encodeImgY(img);
}
cv::destroyAllWindows();
}
// TODO 手でやったほうがいい
void GrayCodeDecoder::setMaskImg(const cv::Mat& img1, const cv::Mat& img2)
{
assert((img1.channels()==3 && img2.channels()==3) && "input img should be grayscale");
cv::Mat diff_img;
cv::absdiff(img1, img2, diff_img);
cv::Mat diff_img_gray;
cv::cvtColor(diff_img, diff_img_gray, CV_RGB2GRAY);
diff_img.release();
for (int rows=0; rows < diff_img_gray.rows; rows++){
for (int cols=0; cols < diff_img_gray.cols; cols++){
diff_img_gray.at<unsigned char>(rows,cols) = diff_img_gray.at<unsigned char>(rows,cols) > 30 ? 255 : 0;
}
}
this->mask_img = diff_img_gray;
this->showImg(diff_img_gray, 0);
};
void GrayCodeDecoder::encodeImgX(const cv::Mat& img) {
this->_encodeImg(img, this->decoded_dataX);
}
void GrayCodeDecoder::encodeImgY(const cv::Mat& img) {
this->_encodeImg(img, this->decoded_dataY);
}
// TODO thresh
void GrayCodeDecoder::_encodeImg(const cv::Mat& img, cv::Mat& decoded_data) {
/**********************************************
* mask_img: projected pattern area 1 else 0
* img: picture
* decoded_data: graycode img 001000....
* ********************************************/
unsigned char thresh = 125;
cv::Mat dst;
cv::threshold(img,dst,thresh,255,cv::THRESH_BINARY|cv::THRESH_OTSU);
this->showImg(dst,0);
dst.release();
assert((img.channels() == this->mask_img.channels()) && "img and mask channel not equal");
for (int cols=0; cols < img.cols; cols++) {
for (int rows=0; rows < img.rows; rows++) {
// target area
if (this->mask_img.at<unsigned char>(rows,cols) == 0) {
continue;
}
int code = img.at<unsigned char>(rows, cols) > thresh ? 1 : 0;
decoded_data.at<unsigned short>(rows,cols) = (decoded_data.at<unsigned short>(rows,cols) << 1) + code;
}
}
};
cv::Mat GrayCodeDecoder::getDecodedDataX() const {
return this->decoded_dataX;
}
cv::Mat GrayCodeDecoder::getDecodedDataY() const {
return this->decoded_dataY;
}
cv::Mat GrayCodeDecoder::getMaskImg() const {
assert(!this->mask_img.empty() && "mask img empty");
return this->mask_img;
}
// util
std::string GrayCodeDecoder::toBinaryString(int num) const {
if (num == 0) return "0";
std::string binary_num = "";
while(num){
// num 000100 1-> (+1)00001 String
binary_num = util::numToString(num & 1) + binary_num;
num >>= 1;
}
return binary_num;
}
// おまけ
// mouse event handler for showdecodedresult
void _mouseEvtHandler (int evt, int x, int y, int flag, void* _data){
struct Data { int x; int y; bool clicked;};
Data* data = (Data*)_data;
switch(evt){
case cv::EVENT_LBUTTONDOWN:
data->x = x;
data->y = y;
data->clicked = true;
std::cout << x << ",\t" << y << std::endl;
break;
default:
break;
}
}
void GrayCodeDecoder::showDecodedResult() const {
int graycode_rows = pow(2, this->graycode_partition_num_y);
int graycode_cols = pow(2, this->graycode_partition_num_x);
cv::Mat graycode_campus = cv::Mat(graycode_rows, graycode_cols, CV_8UC1, cv::Scalar(0));
cv::Mat mask_campus = this->mask_img.clone();
cv::imshow("PRESS ESC TO EXIT", mask_campus);
struct Data { int x; int y; bool clicked; };
Data data = { 0, 0, false };
cv::setMouseCallback("PRESS ESC TO EXIT", _mouseEvtHandler, &data);
while(1){
if(data.clicked){
int pos_x = this->getDecodedDataX().at<unsigned short>(data.y, data.x);
int pos_y = this->getDecodedDataY().at<unsigned short>(data.y, data.x);
std::cout << pos_x << "\t" << this->toBinaryString(pos_x) << "\t" << std::endl;
std::cout << pos_y << "\t" << this->toBinaryString(pos_y) << "\t" << std::endl;
// draw
cv::circle(mask_campus, cv::Point(data.x, data.y), 2, cv::Scalar(0), -1);
cv::circle(mask_campus, cv::Point(data.x, data.y), 8, cv::Scalar(0));
cv::circle(graycode_campus, cv::Point(pos_x, pos_y), 2, cv::Scalar(255), -1);
cv::circle(graycode_campus, cv::Point(pos_x, pos_y), 8, cv::Scalar(255));
cv::imshow("GRAYCODE_PATTERN", graycode_campus);
cv::imshow("PRESS ESC TO EXIT", mask_campus);
data.clicked = false;
}
int key = cv::waitKey(10);
if (key==27) break;
}
cv::destroyAllWindows();
}
void _mouseEvtHandlerForSetMaskContour (int evt, int x, int y, int flags, void* _data){
struct Data {
int vertex_num;
std::vector< cv::Point > contour;
};
Data* data = (Data*)_data;
switch(evt){
case cv::EVENT_LBUTTONDOWN:
data->contour.push_back(cv::Point(x,y));
data->vertex_num += 1;
break;
default:
break;
}
}
void GrayCodeDecoder::setMaskContour(const cv::Mat& img) {
struct Data {
int vertex_num;
std::vector< cv::Point > contour;
};
Data *data = new Data;
data->vertex_num = 0;
this->mask_img = cv::Mat(img.rows, img.cols, CV_8UC1, cv::Scalar(0));
assert(mask_img.channels()==1 && "mask img should be grayscale");
cv::imshow("CLICK 4 VERTICES", img);
cv::setMouseCallback("CLICK 4 VERTICES", _mouseEvtHandlerForSetMaskContour, data);
while(1){
if(data->vertex_num >= 4) {
assert(!data->contour.empty() && "contour empty");
std::vector< std::vector< cv::Point > > contours;
contours.push_back(data->contour);
cv::drawContours(this->mask_img, contours, -1, cv::Scalar(255), -1);
cv::imshow("MASK IMG", this->mask_img);
cv::waitKey(0);
cv::destroyAllWindows();
break;
};
int key = cv::waitKey(10);
if (key==27) break;
}
}
#include <highgui.h>
#include <cv.h>
#include <cxcore.h>
#include <assert.h>
#include <iostream>
#include <math.h>
// filepath // 000_1~000_10.PNG : blink
// 001_1~5 : graycode with exposure
class GrayCode {
public:
GrayCode(int _x_partition_num, int _y_partition_num, const std::string& _window_name);
~GrayCode();
void splitX(int depth_limit, int time);
void splitY(int depth_limit, int time);
bool _split(const cv::Point& tl, const cv::Point& br, const cv::Scalar& cvcolor, int depth, int depth_limit, bool split_x);
const cv::Mat getImg() const;
void showFilledImg(const cv::Scalar& cvcolor, int time) const;
void blink(int times) const;
private:
const int x_partition_num;
const int y_partition_num;
const std::string window_name;
cv::Mat img;
};
GrayCode::GrayCode(int _x_partition_num, int _y_partition_num, const std::string& _window_name)
: x_partition_num(_x_partition_num), y_partition_num(_y_partition_num), window_name(_window_name){
int rows = pow(2, this->y_partition_num);
int cols = pow(2, this->x_partition_num);
this->img = cv::Mat(rows, cols, CV_8UC1, cv::Scalar(0));
assert(img.channels() == 1 && "img channel is not equal to 1");
// WINDOW SETTINGS
cv::namedWindow(this->window_name, CV_WINDOW_AUTOSIZE);
// TODO 拡大されて危ない
// cv::namedWindow(this->window_name, CV_WINDOW_NORMAL);
// cv::setWindowProperty(this->window_name, CV_WND_PROP_FULLSCREEN, CV_WINDOW_FULLSCREEN);
};
GrayCode::~GrayCode(){};
const cv::Mat GrayCode::getImg() const {
assert(!this->img.empty() && "img is empty");
return this->img;
}
void GrayCode::showFilledImg(const cv::Scalar& cvcolor, int time) const {
cv::Mat filled_img = cv::Mat(this->img.rows, this->img.cols, CV_8UC1, cvcolor);
cv::imshow(this->window_name, filled_img);
cv::waitKey(time);
filled_img.release();
}
void GrayCode::blink(int times) const {
for(int i=0; i<times; i++){
this->showFilledImg(cv::Scalar(255), 0);
this->showFilledImg(cv::Scalar(0), 0);
}
}
void GrayCode::splitX(int depth_limit, int time){
this->_split(cv::Point(0,0), cv::Point(pow(2,x_partition_num), pow(2,y_partition_num)), cv::Scalar(0), 0, depth_limit, true);
cv::imshow(this->window_name, this->img);
cv::waitKey(time);
};
void GrayCode::splitY(int depth_limit, int time){
this->_split(cv::Point(0,0), cv::Point(pow(2,x_partition_num), pow(2,y_partition_num)), cv::Scalar(0), 0, depth_limit, false);
cv::imshow(this->window_name, this->img);
cv::waitKey(time);
};
bool GrayCode::_split(const cv::Point& tl, const cv::Point& br, const cv::Scalar& cvcolor, int depth, int depth_limit, bool split_x) {
if (depth == depth_limit) {
cv::rectangle(this->img, cv::Point(tl.x, tl.y), cv::Point(br.x, br.y), cvcolor, -1);
return true;
};
int mid_x = (tl.x + br.x)/2;
int mid_y = (tl.y + br.y)/2;
if (split_x){
this->_split(cv::Point(tl.x, tl.y), cv::Point(mid_x, br.y), cv::Scalar(0), depth+1, depth_limit, split_x);
this->_split(cv::Point(mid_x+1, tl.y), cv::Point(br.x, br.y), cv::Scalar(255), depth+1, depth_limit, split_x);
} else {
this->_split(cv::Point(tl.x, tl.y), cv::Point(br.x, mid_y), cv::Scalar(0), depth+1, depth_limit, split_x);
this->_split(cv::Point(tl.x, mid_y+1), cv::Point(br.x, br.y), cv::Scalar(255), depth+1, depth_limit, split_x);
}
return true;
};
@denkiwakame
Copy link
Author

cv::Mat img
img.copyTo(dst, mask_img)

トリミング

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment