Files
Happy-Reconstruction/Classes/structured_light.cpp
2020-05-05 23:30:52 +08:00

459 lines
14 KiB
C++

#include "structured_light.h"
#include <iostream>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
namespace sl
{
const float PIXEL_UNCERTAIN = std::numeric_limits<float>::quiet_NaN();
const unsigned short BIT_UNCERTAIN = 0xffff;
}
bool sl::decode_pattern(const std::vector<cv::Mat> & images, cv::Mat & pattern_image, cv::Mat & min_max_image, cv::Size const& projector_size, unsigned flags, const cv::Mat & direct_light, unsigned m)
{
// for(int i = 0; i < images.size(); i++) {
// std::cout<<images[i].cols<<" "<<images[i].rows<<std::endl;
// }
bool binary = (flags & GrayPatternDecode)!=GrayPatternDecode;
bool robust = (flags & RobustDecode)==RobustDecode;
std::cout << " --- decode_pattern START ---\n";
//delete previous data
pattern_image = cv::Mat();
min_max_image = cv::Mat();
bool init = true;
std::cout << "Decode: " << (binary?"Binary ":"Gray ")
<< (robust?"Robust ":"")
<< std::endl;
unsigned int total_images = static_cast<unsigned int>(images.size());
int v_bits = 1;
int h_bits = 1;
for (int i=(1<<v_bits); i<projector_size.height; i=(1<<v_bits)) { v_bits++; }
for (int i=(1<<h_bits); i<projector_size.width; i=(1<<h_bits)) { h_bits++; }
// std::cout<<2+4*total_bits<<" "<<total_images<<std::endl;
if (2+2*v_bits+2*h_bits!=total_images)
{ //error
std::cout << "[sl::decode_pattern] ERROR: cannot detect pattern and bit count from image set.\n";
return false;
}
const unsigned bit_count[] = {0, h_bits, v_bits}; //pattern bits
const unsigned set_size[] = {1, h_bits, v_bits}; //number of image pairs
const unsigned COUNT = 2*(set_size[0]+set_size[1]+set_size[2]); //total image count
//??????
const int pattern_offset[2] = {((1<<h_bits)-projector_size.width)/2, ((1<<v_bits)-projector_size.height)/2};
if (images.size()<COUNT)
{ //error
std::cout << "Image list size does not match set size, please supply exactly " << COUNT << " image names.\n";
return false;
}
//load every image pair and compute the maximum, minimum, and bit code
unsigned set = 0;
unsigned current = 0;
for (unsigned t=0; t<COUNT; t+=2, current++)
{
if (current==set_size[set])
{
set++;
current = 0;
}
if (set==0)
{ //skip
continue;
}
unsigned bit = bit_count[set] - current - 1; //current bit: from 0 to (bit_count[set]-1)
unsigned channel = set - 1;
//load images
const cv::Mat & gray_image1 = get_gray_image(images[t+0]);
// std::cout<<t<<" "<<images[t].cols<<" "<<images[t].rows<<" "<<images[t].channels()<<std::endl;
if (gray_image1.rows<1)
{
std::cout << "Failed to load " << t+0 <<"th image"<< std::endl;
return false;
}
const cv::Mat & gray_image2 = get_gray_image(images[t+1]);
if (gray_image2.rows<1)
{
std::cout << "Failed to load " << t+1 << "th image" << std::endl;
return false;
}
//initialize data structures
if (init)
{
//sanity check
if (gray_image1.size()!=gray_image2.size())
{ //different size
std::cout << " --> Initial images have different size: \n";
return false;
}
if (robust && gray_image1.size()!=direct_light.size())
{ //different size
std::cout << " --> Direct Component image has different size: \n";
return false;
}
pattern_image = cv::Mat(gray_image1.size(), CV_32FC2);
min_max_image = cv::Mat(gray_image1.size(), CV_8UC2);
}
//sanity check
if (gray_image1.size()!=pattern_image.size())
{ //different size
std::cout << " --> Image 1 has different size, image pair " << t << " (skipped!)\n";
continue;
}
if (gray_image2.size()!=pattern_image.size())
{ //different size
std::cout << " --> Image 2 has different size, image pair " << t << " (skipped!)\n";
continue;
}
//compare
for (int h=0; h<pattern_image.rows; h++)
{
const unsigned char * row1 = gray_image1.ptr<unsigned char>(h);
const unsigned char * row2 = gray_image2.ptr<unsigned char>(h);
const cv::Vec2b * row_light = (robust ? direct_light.ptr<cv::Vec2b>(h) : NULL);
cv::Vec2f * pattern_row = pattern_image.ptr<cv::Vec2f>(h);
cv::Vec2b * min_max_row = min_max_image.ptr<cv::Vec2b>(h);
for (int w=0; w<pattern_image.cols; w++)
{
cv::Vec2f & pattern = pattern_row[w];
cv::Vec2b & min_max = min_max_row[w];
unsigned char value1 = row1[w];
unsigned char value2 = row2[w];
if (init)
{
pattern[0] = 0.f; //vertical
pattern[1] = 0.f; //horizontal
}
//min/max
if (init || value1<min_max[0] || value2<min_max[0])
{
min_max[0] = (value1<value2?value1:value2);
}
if (init || value1>min_max[1] || value2>min_max[1])
{
min_max[1] = (value1>value2?value1:value2);
}
if (!robust)
{ // [simple] pattern bit assignment
if (value1>value2)
{ //set bit n to 1
pattern[channel] += (1<<bit);
}
}
else
{ // [robust] pattern bit assignment
if (row_light && (init || pattern[channel]!=PIXEL_UNCERTAIN))
{
const cv::Vec2b & L = row_light[w];
unsigned short p = get_robust_bit(value1, value2, L[0], L[1], m);
if (p==BIT_UNCERTAIN)
{
pattern[channel] = PIXEL_UNCERTAIN;
}
else
{
pattern[channel] += (p<<bit);
}
}
}
} //for each column
} //for each row
init = false;
} //for all image pairs
if (!binary)
{ //not binary... it must be gray code
convert_pattern(pattern_image, projector_size, pattern_offset, binary);
}
std::cout << " --- decode_pattern END ---"<<std::endl;
return true;
}
unsigned short sl::get_robust_bit(unsigned value1, unsigned value2, unsigned Ld, unsigned Lg, unsigned m)
{
if (Ld < m)
{
return BIT_UNCERTAIN;
}
if (Ld>Lg)
{
return (value1>value2 ? 1 : 0);
}
if (value1<=Ld && value2>=Lg)
{
return 0;
}
if (value1>=Lg && value2<=Ld)
{
return 1;
}
return BIT_UNCERTAIN;
}
void sl::convert_pattern(cv::Mat & pattern_image, cv::Size const& projector_size, const int offset[2], bool binary)
{
if (pattern_image.rows==0)
{ //no pattern image
return;
}
if (pattern_image.type()!=CV_32FC2)
{
return;
}
if (binary)
{
std::cout << "Converting binary code to gray\n";
}
else
{
std::cout << "Converting gray code to binary\n";
}
for (int h=0; h<pattern_image.rows; h++)
{
cv::Vec2f * pattern_row = pattern_image.ptr<cv::Vec2f>(h);
for (int w=0; w<pattern_image.cols; w++)
{
cv::Vec2f & pattern = pattern_row[w];
if (binary)
{
if (!INVALID(pattern[0]))
{
int p = static_cast<int>(pattern[0]);
pattern[0] = binaryToGray(p, offset[0]) + (pattern[0] - p);
}
if (!INVALID(pattern[1]))
{
int p = static_cast<int>(pattern[1]);
pattern[1] = binaryToGray(p, offset[1]) + (pattern[1] - p);
}
}
else
{
if (!INVALID(pattern[0]))
{
int p = static_cast<int>(pattern[0]);
int code = grayToBinary(p, offset[0]);
if (code<0) {code = 0;}
else if (code>=projector_size.width) {code = projector_size.width - 1;}
pattern[0] = code + (pattern[0] - p);
}
if (!INVALID(pattern[1]))
{
int p = static_cast<int>(pattern[1]);
int code = grayToBinary(p, offset[1]);
if (code<0) {code = 0;}
else if (code>=projector_size.height) {code = projector_size.height - 1;}
pattern[1] = code + (pattern[1] - p);
}
}
}
}
}
cv::Mat sl::estimate_direct_light(const std::vector<cv::Mat> & images, float b)
{
static const unsigned COUNT = 10; // max number of images
unsigned count = static_cast<int>(images.size());
if (count<1)
{ //no images
return cv::Mat();
}
std::cout << " --- estimate_direct_light START ---\n";
if (count>COUNT)
{
count = COUNT;
std::cout << "WARNING: Using only " << COUNT << " of " << count << std::endl;
}
for (unsigned i=0; i<count; i++)
{
if (images.at(i).type()!=CV_8UC1)
{ //error
std::cout << "Gray images required\n";
return cv::Mat();
}
}
cv::Size size = images.at(0).size();
//initialize direct light image
cv::Mat direct_light(size, CV_8UC2);
double b1 = 1.0/(1.0 - b);
double b2 = 2.0/(1.0 - b*1.0*b);
for (unsigned h=0; static_cast<int>(h)<size.height; h++)
{
unsigned char const* row[COUNT];
for (unsigned i=0; i<count; i++)
{
row[i] = images.at(i).ptr<unsigned char>(h);
}
cv::Vec2b * row_light = direct_light.ptr<cv::Vec2b>(h);
for (unsigned w=0; static_cast<int>(w)<size.width; w++)
{
unsigned Lmax = row[0][w];
unsigned Lmin = row[0][w];
for (unsigned i=0; i<count; i++)
{
if (Lmax<row[i][w]) Lmax = row[i][w];
if (Lmin>row[i][w]) Lmin = row[i][w];
}
int Ld = static_cast<int>(b1*(Lmax - Lmin) + 0.5);
int Lg = static_cast<int>(b2*(Lmin - b*Lmax) + 0.5);
row_light[w][0] = (Lg>0 ? static_cast<unsigned>(Ld) : Lmax);
row_light[w][1] = (Lg>0 ? static_cast<unsigned>(Lg) : 0);
//std::cout << "Ld=" << (int)row_light[w][0] << " iTotal=" <<(int) row_light[w][1] << std::endl;
}
}
std::cout << " --- estimate_direct_light END ---\n";
return direct_light;
}
cv::Mat sl::get_gray_image(cv::Mat img)
{
if (img.channels() > 1)
{
//gray scale
cv::Mat gray_image;
cvtColor(img, gray_image, cv::COLOR_BGR2GRAY);
return gray_image;
}
cv::Mat res = img.clone();
return res;
}
/* From Wikipedia: http://en.wikipedia.org/wiki/Gray_code
The purpose of this function is to convert an unsigned
binary number to reflected binary Gray code.
*/
static unsigned util_binaryToGray(unsigned num)
{
return (num>>1) ^ num;
}
/* From Wikipedia: http://en.wikipedia.org/wiki/Gray_code
The purpose of this function is to convert a reflected binary
Gray code number to a binary number.
*/
static unsigned util_grayToBinary(unsigned num, unsigned numBits)
{
for (unsigned shift = 1; shift < numBits; shift <<= 1)
{
num ^= num >> shift;
}
return num;
}
int sl::binaryToGray(int value) {return util_binaryToGray(value);}
inline int sl::binaryToGray(int value, unsigned offset) {return util_binaryToGray(value + offset);}
inline int sl::grayToBinary(int value, unsigned offset) {return (util_grayToBinary(value, 32) - offset);}
cv::Mat sl::colorize_pattern(const cv::Mat & pattern_image, unsigned set, float max_value)
{
if (pattern_image.rows==0)
{ //empty image
return cv::Mat();
}
if (pattern_image.type()!=CV_32FC2)
{ //invalid image type
return cv::Mat();
}
if (set!=0 && set!=1)
{
return cv::Mat();
}
cv::Mat image(pattern_image.size(), CV_8UC3);
float max_t = max_value;
float n = 4.f;
float dt = 255.f/n;
for (int h=0; h<pattern_image.rows; h++)
{
const cv::Vec2f * row1 = pattern_image.ptr<cv::Vec2f>(h);
cv::Vec3b * row2 = image.ptr<cv::Vec3b>(h);
for (int w=0; w<pattern_image.cols; w++)
{
if (row1[w][set]>max_value || INVALID(row1[w][set]))
{ //invalid value: use grey
row2[w] = cv::Vec3b(128, 128, 128);
continue;
}
//display
float t = row1[w][set]*255.f/max_t;
float c1 = 0.f, c2 = 0.f, c3 = 0.f;
if (t<=1.f*dt)
{ //black -> red
float c = n*(t-0.f*dt);
c1 = c; //0-255
c2 = 0.f; //0
c3 = 0.f; //0
}
else if (t<=2.f*dt)
{ //red -> red,green
float c = n*(t-1.f*dt);
c1 = 255.f; //255
c2 = c; //0-255
c3 = 0.f; //0
}
else if (t<=3.f*dt)
{ //red,green -> green
float c = n*(t-2.f*dt);
c1 = 255.f-c; //255-0
c2 = 255.f; //255
c3 = 0.f; //0
}
else if (t<=4.f*dt)
{ //green -> blue
float c = n*(t-3.f*dt);
c1 = 0.f; //0
c2 = 255.f-c; //255-0
c3 = c; //0-255
}
row2[w] = cv::Vec3b(static_cast<uchar>(c3), static_cast<uchar>(c2), static_cast<uchar>(c1));
}
}
return image;
}