Rev 232 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
//
// Gray Code Structured Light with horizontal and vertical encoding
//
// This implementation of Gray encoding uses horizontal and vertial stripes, which adds some encoding redundancy, but avoids interpolation effects from rectifying homographies.
//
#include "AlgorithmGrayCodeHorzVert.h"
#include <cmath>
#include <assert.h>
#include "cvtools.h"
#include "algorithmtools.h"
// Algorithm
AlgorithmGrayCodeHorzVert::AlgorithmGrayCodeHorzVert(unsigned int _screenCols, unsigned int _screenRows) : Algorithm(_screenCols, _screenRows){
NbitsHorz = ceilf(log2f((float)screenCols));
NbitsVert = ceilf(log2f((float)screenRows));
N = 2 + (NbitsHorz+NbitsVert)*2;
// all on pattern
cv::Mat allOn(1, screenCols, CV_8UC3, cv::Scalar::all(255));
patterns.push_back(allOn);
// all off pattern
cv::Mat allOff(1, screenCols, CV_8UC3, cv::Scalar::all(0));
patterns.push_back(allOff);
// horizontally encoding patterns
for(unsigned int p=0; p<NbitsHorz; p++){
cv::Mat pattern(1, screenCols, CV_8UC3);
cv::Mat patternInv(1, screenCols, CV_8UC3);
for(unsigned int j=0; j<screenCols; j++){
unsigned int jGray = binaryToGray(j);
// Amplitude of channels
int bit = (int)getBit(jGray, NbitsHorz-p);
pattern.at<cv::Vec3b>(0,j) = cv::Vec3b(255.0*bit,255.0*bit,255.0*bit);
int invBit = bit^1;
patternInv.at<cv::Vec3b>(0,j) = cv::Vec3b(255.0*invBit,255.0*invBit,255.0*invBit);
}
patterns.push_back(pattern);
patterns.push_back(patternInv);
}
// vertical encoding patterns
for(unsigned int p=0; p<NbitsVert; p++){
cv::Mat pattern(screenRows, 1, CV_8UC3);
cv::Mat patternInv(screenRows, 1, CV_8UC3);
for(unsigned int j=0; j<screenRows; j++){
unsigned int jGray = binaryToGray(j);
// Amplitude of channels
int bit = (int)getBit(jGray, NbitsVert-p);
pattern.at<cv::Vec3b>(j,0) = cv::Vec3b(255.0*bit,255.0*bit,255.0*bit);
int invBit = bit^1;
patternInv.at<cv::Vec3b>(j,0) = cv::Vec3b(255.0*invBit,255.0*invBit,255.0*invBit);
}
patterns.push_back(pattern);
patterns.push_back(patternInv);
}
}
cv::Mat AlgorithmGrayCodeHorzVert::getEncodingPattern(unsigned int depth){
return patterns[depth];
}
typedef struct intersection{
unsigned int row;
unsigned int col;
// 64 bit integer (long long)
unsigned long id;
intersection() : row(0), col(0), id(0){}
intersection(unsigned int _row, unsigned int _col, unsigned long long _id) :
row(_row), col(_col), id(_id){}
} intersection;
static bool sortingLarger(intersection i,intersection j){ return (i.id<j.id);}
static bool sortingEqual(intersection i,intersection j){ return (i.id==j.id);}
static void getIntersectionLabels(const cv::Mat& codeHorz, const cv::Mat& codeVert, const int NbitsHorz, const int NbitsVert, std::vector<intersection>& intersections){
int nRows = codeHorz.rows;
int nCols = codeHorz.cols;
int labelHorz;
int labelVert;
int labelHorzRight;
int labelVertBelow;
// collect intersections
for(int row=0; row<nRows-1; row++){
for(int col=0; col<nCols-1; col++){
labelHorz = codeHorz.at<int>(row, col);
labelHorzRight = codeHorz.at<int>(row, col+1);
labelVert = codeVert.at<int>(row, col);
labelVertBelow = codeVert.at<int>(row+1, col);
// labels need to be non-background, and differ in exactly one bit
if(labelHorz != -1 && labelHorzRight != -1 &&
(grayToBinary(labelHorzRight) == grayToBinary(labelHorz)+1) &&
labelVert != -1 && labelVertBelow != -1 &&
(grayToBinary(labelVertBelow) == grayToBinary(labelVert)+1)){
// shift together labels to form unique intersection id
unsigned long id = ((ulong)labelHorz << (NbitsHorz+2*NbitsVert)) + ((ulong)labelHorzRight << (2*NbitsVert)) +
((ulong)labelVert << NbitsVert) + (ulong)labelVertBelow;
// store intersection
intersections.push_back(intersection(row, col, id));
}
}
}
// sort
std::sort(intersections.begin(), intersections.end(), sortingLarger);
// remove duplicates
std::vector<intersection>::iterator it;
it = std::unique(intersections.begin(), intersections.end(), sortingEqual);
intersections.resize(std::distance(intersections.begin(),it));
}
static void getSubpixelCoordinates(const std::vector<intersection>& matches, const std::vector<cv::Mat>& frames, const int NbitsHorz, const int NbitsVert, std::vector<cv::Point2f> &q){
int nMatches = matches.size();
q.resize(nMatches);
// subpixel refinement finds the intersection of linear interpolants in the positive/negative pattern
for(int i=0; i<nMatches; i++){
// shift the labels back out from id
int labelHorz = (matches[i].id >> (NbitsHorz+2*NbitsVert)) & ((1 << NbitsHorz) - 1);
int labelHorzRight = (matches[i].id >> (2*NbitsVert)) & ((1 << NbitsHorz) - 1);
int labelVert = (matches[i].id >> NbitsVert) & ((1 << NbitsVert) - 1);
int labelVertBelow = matches[i].id & ((1 << NbitsVert) - 1);
// std::cout << "id: " << matches[i].id << std::endl;
// std::cout << "labelHorz: " << labelHorz << std::endl;
// std::cout << "labelHorzRight: " << labelHorzRight << std::endl;
// std::cout << "labelVert: " << labelVert << std::endl;
// std::cout << "labelVertBelow: " << labelVertBelow << std::endl;
// determine the levels at which the edges exists
int levelHorz = NbitsHorz - leastSignificantBitSet(labelHorz^labelHorzRight);
int levelVert = NbitsVert - leastSignificantBitSet(labelVert^labelVertBelow);
// interpolate horizontal coordinate
float row = matches[i].row;
float col = matches[i].col;
float colRight = col+1;
float posHorz = frames[2*levelHorz+2].at<unsigned char>(row, col);
float negHorz = frames[2*levelHorz+3].at<unsigned char>(row, col);
float posHorzRight = frames[2*levelHorz+2].at<unsigned char>(row, colRight);
float negHorzRight = frames[2*levelHorz+3].at<unsigned char>(row, colRight);
float x = col + (posHorz - negHorz)/(negHorzRight - negHorz - posHorzRight + posHorz);
// interpolate vertical coordinate
float rowBelow = row+1;
float posVert = frames[2*NbitsHorz+2*levelVert+2].at<unsigned char>(row, col);
float negVert = frames[2*NbitsHorz+2*levelVert+3].at<unsigned char>(row, col);
float posVertBelow = frames[2*NbitsHorz+2*levelVert+2].at<unsigned char>(rowBelow, col);
float negVertBelow = frames[2*NbitsHorz+2*levelVert+3].at<unsigned char>(rowBelow, col);
float y = row + (posVert - negVert)/(negVertBelow - negVert - posVertBelow + posVert);
// write into return vector
q[i] = cv::Point2f(x, y);
}
}
void AlgorithmGrayCodeHorzVert::get3DPoints(const SMCalibrationParameters & calibration, const std::vector<cv::Mat>& frames0, const std::vector<cv::Mat>& frames1, std::vector<cv::Point3f>& Q, std::vector<cv::Vec3b>& color){
assert(frames0.size() == N);
assert(frames1.size() == N);
int frameRows = frames0[0].rows;
int frameCols = frames0[0].cols;
// gray-scale
std::vector<cv::Mat> frames0Gray(N);
std::vector<cv::Mat> frames1Gray(N);
for(unsigned int i=0; i<N; i++){
cv::cvtColor(frames0[i], frames0Gray[i], CV_BayerBG2GRAY);
cv::cvtColor(frames1[i], frames1Gray[i], CV_BayerBG2GRAY);
}
// colors
cv::Mat color0;
// frames0[0].convertTo(color0, CV_8UC3, 1.0/256.0);
cv::cvtColor(frames0[0], color0, CV_BayerBG2RGB);
cv::Mat color1;
// frames1[0].convertTo(color1, CV_8UC3, 1.0/256.0);
cv::cvtColor(frames1[0], color1, CV_BayerBG2RGB);
// occlusion masks
cv::Mat occlusion0, occlusion1;
cv::subtract(frames0[0], frames0[1], occlusion0);
occlusion0 = (occlusion0 > 20) & (occlusion0 < 250);
cv::subtract(frames1[0], frames1[1], occlusion1);
occlusion1 = (occlusion1 > 20) & (occlusion1 < 250);
// // erode occlusion masks
// cv::Mat strel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3,3));
// cv::erode(occlusion0, occlusion0, strel);
// cv::erode(occlusion1, occlusion1, strel);
//cvtools::writeMat(occlusion0, "occlusion0.mat", "occlusion0");
//cvtools::writeMat(occlusion1, "occlusion1.mat", "occlusion1");
// decode patterns
cv::Mat code0Horz(frameRows, frameCols, CV_32S, cv::Scalar(0));
cv::Mat code1Horz(frameRows, frameCols, CV_32S, cv::Scalar(0));
cv::Mat code0Vert(frameRows, frameCols, CV_32S, cv::Scalar(0));
cv::Mat code1Vert(frameRows, frameCols, CV_32S, cv::Scalar(0));
// horizontal codes into gray code
for(unsigned int i=0; i<NbitsHorz; i++){
cv::Mat bit0;
cv::compare(frames0Gray[i*2+2], frames0Gray[i*2+3], bit0, cv::CMP_GT);
bit0.convertTo(bit0, CV_32S, 1.0/255.0);
cv::add(code0Horz, bit0*twopowi(NbitsHorz-i-1), code0Horz, cv::Mat(), CV_32S);
cv::Mat bit1;
cv::compare(frames1Gray[i*2+2], frames1Gray[i*2+3], bit1, cv::CMP_GT);
bit1.convertTo(bit1, CV_32S, 1.0/255.0);
cv::add(code1Horz, bit1*twopowi(NbitsHorz-i-1), code1Horz, cv::Mat(), CV_32S);
}
// vertical codes into gray code
for(unsigned int i=0; i<NbitsVert; i++){
cv::Mat bit0;
cv::compare(frames0Gray[i*2+2*NbitsHorz+2], frames0Gray[i*2+2*NbitsHorz+3], bit0, cv::CMP_GT);
bit0.convertTo(bit0, CV_32S, 1.0/255.0);
cv::add(code0Vert, bit0*twopowi(NbitsVert-i-1), code0Vert, cv::Mat(), CV_32S);
cv::Mat bit1;
cv::compare(frames1Gray[i*2+2*NbitsHorz+2], frames1Gray[i*2+2*NbitsHorz+3], bit1, cv::CMP_GT);
bit1.convertTo(bit1, CV_32S, 1.0/255.0);
cv::add(code1Vert, bit1*twopowi(NbitsVert-i-1), code1Vert, cv::Mat(), CV_32S);
}
// set occluded pixels to -1
for(int r=0; r<frameRows; r++){
for(int c=0; c<frameCols; c++){
if(occlusion0.at<char>(r,c) == 0){
code0Horz.at<int>(r,c) = -1;
code0Vert.at<int>(r,c) = -1;
}
if(occlusion1.at<char>(r,c) == 0){
code1Horz.at<int>(r,c) = -1;
code1Vert.at<int>(r,c) = -1;
}
}
}
// cvtools::writeMat(code0Horz, "code0Horz.mat", "code0Horz");
// cvtools::writeMat(code1Horz, "code1Horz.mat", "code1Horz");
// cvtools::writeMat(code0Vert, "code0Vert.mat", "code0Vert");
// cvtools::writeMat(code1Vert, "code1Vert.mat", "code1Vert");
// get intersections
std::vector<intersection> intersections0, intersections1;
getIntersectionLabels(code0Horz, code0Vert, NbitsHorz, NbitsVert, intersections0);
getIntersectionLabels(code1Horz, code1Vert, NbitsHorz, NbitsVert, intersections1);
// match intersections
std::vector<intersection> matches0, matches1;
unsigned int i=0, j=0;
while(i<intersections0.size() && j<intersections1.size()){
if(intersections0[i].id == intersections1[j].id){
matches0.push_back(intersections0[i]);
matches1.push_back(intersections1[j]);
i += 1;
j += 1;
} else if(intersections0[i].id < intersections1[j].id){
i += 1;
} else if(intersections0[i].id > intersections1[j].id){
j += 1;
}
}
int nMatches = matches0.size();
if(nMatches < 1){
Q.resize(0);
color.resize(0);
return;
}
std::vector<cv::Point2f> q0(nMatches), q1(nMatches);
// for(int i=0; i<nMatches; i++){
// q0[i] = cv::Point2f(matches0[i].col, matches0[i].row);
// q1[i] = cv::Point2f(matches1[i].col, matches1[i].row);
// }
// subpixel refinement
getSubpixelCoordinates(matches0, frames0Gray, NbitsHorz, NbitsVert, q0);
getSubpixelCoordinates(matches1, frames1Gray, NbitsHorz, NbitsVert, q1);
// retrieve color information (at subpixel coordinates)
color.resize(nMatches);
for(int i=0; i<nMatches; i++){
cv::Vec3b c0 = color0.at<cv::Vec3b>(std::floor(q0[i].y), std::floor(q0[i].x));
cv::Vec3b c1 = color1.at<cv::Vec3b>(std::floor(q1[i].y), std::floor(q1[i].x));
color[i] = 0.5*c0 + 0.5*c1;
}
//cv::correctMatches(calibration.F, q0, q1, q0, q1);
// correct for lens distortion
cv::undistortPoints(q0, q0, calibration.K0, calibration.k0, cv::noArray(), calibration.K0);
cv::undistortPoints(q1, q1, calibration.K1, calibration.k1, cv::noArray(), calibration.K1);
cv::correctMatches(calibration.F, q0, q1, q0, q1);
// Triangulate points
cv::Mat P0(3, 4, CV_32F, cv::Scalar(0.0));
cv::Mat(calibration.K0).copyTo(P0.colRange(0, 3));
cv::Mat P1(3, 4, CV_32F), temp(3,4,CV_32F);
cv::Mat(calibration.R1).copyTo(temp(cv::Range(0,3), cv::Range(0,3)));
cv::Mat(calibration.T1).copyTo(temp(cv::Range(0,3), cv::Range(3,4)));
P1 = cv::Mat(calibration.K1) * temp;
cv::Mat QMatHomogenous, QMat;
cv::triangulatePoints(P0, P1, q0, q1, QMatHomogenous);
cvtools::convertMatFromHomogeneous(QMatHomogenous, QMat);
cvtools::matToPoints3f(QMat, Q);
}