You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
919 lines
36 KiB
919 lines
36 KiB
/*M/////////////////////////////////////////////////////////////////////////////////////// |
|
// |
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. |
|
// |
|
// By downloading, copying, installing or using the software you agree to this license. |
|
// If you do not agree to this license, do not download, install, |
|
// copy or use the software. |
|
// |
|
// |
|
// License Agreement |
|
// For Open Source Computer Vision Library |
|
// |
|
// Copyright (C) 2015, OpenCV Foundation, all rights reserved. |
|
// Third party copyrights are property of their respective owners. |
|
// |
|
// Redistribution and use in source and binary forms, with or without modification, |
|
// are permitted provided that the following conditions are met: |
|
// |
|
// * Redistribution's of source code must retain the above copyright notice, |
|
// this list of conditions and the following disclaimer. |
|
// |
|
// * Redistribution's in binary form must reproduce the above copyright notice, |
|
// this list of conditions and the following disclaimer in the documentation |
|
// and/or other materials provided with the distribution. |
|
// |
|
// * The name of the copyright holders may not be used to endorse or promote products |
|
// derived from this software without specific prior written permission. |
|
// |
|
// This software is provided by the copyright holders and contributors "as is" and |
|
// any express or implied warranties, including, but not limited to, the implied |
|
// warranties of merchantability and fitness for a particular purpose are disclaimed. |
|
// In no event shall the Intel Corporation or contributors be liable for any direct, |
|
// indirect, incidental, special, exemplary, or consequential damages |
|
// (including, but not limited to, procurement of substitute goods or services; |
|
// loss of use, data, or profits; or business interruption) however caused |
|
// and on any theory of liability, whether in contract, strict liability, |
|
// or tort (including negligence or otherwise) arising in any way out of |
|
// the use of this software, even if advised of the possibility of such damage. |
|
// |
|
//M*/ |
|
|
|
|
|
#include "precomp.hpp" |
|
|
|
namespace cv { |
|
namespace structured_light { |
|
class CV_EXPORTS_W SinusoidalPatternProfilometry_Impl CV_FINAL : public SinusoidalPattern |
|
{ |
|
public: |
|
// Constructor |
|
explicit SinusoidalPatternProfilometry_Impl( const SinusoidalPattern::Params ¶meters = |
|
SinusoidalPattern::Params() ); |
|
// Destructor |
|
virtual ~SinusoidalPatternProfilometry_Impl() CV_OVERRIDE {}; |
|
|
|
// Generate sinusoidal patterns |
|
bool generate( OutputArrayOfArrays patternImages ) CV_OVERRIDE; |
|
|
|
bool decode( const std::vector< std::vector<Mat> >& patternImages, OutputArray disparityMap, |
|
InputArrayOfArrays blackImages = noArray(), InputArrayOfArrays whiteImages = |
|
noArray(), int flags = 0 ) const CV_OVERRIDE; |
|
|
|
// Compute a wrapped phase map from the sinusoidal patterns |
|
void computePhaseMap( InputArrayOfArrays patternImages, OutputArray wrappedPhaseMap, |
|
OutputArray shadowMask = noArray(), InputArray fundamental = noArray()) CV_OVERRIDE; |
|
// Unwrap the wrapped phase map to retrieve correspondences |
|
void unwrapPhaseMap( InputArray wrappedPhaseMap, |
|
OutputArray unwrappedPhaseMap, |
|
cv::Size camSize, |
|
InputArray shadowMask = noArray() ) CV_OVERRIDE; |
|
// Find correspondences between the devices |
|
void findProCamMatches( InputArray projUnwrappedPhaseMap, InputArray camUnwrappedPhaseMap, |
|
OutputArrayOfArrays matches ) CV_OVERRIDE; |
|
|
|
void computeDataModulationTerm( InputArrayOfArrays patternImages, |
|
OutputArray dataModulationTerm, |
|
InputArray shadowMask ) CV_OVERRIDE; |
|
|
|
private: |
|
// Compute The Fourier transform of a pattern. Output is complex. Taken from the DFT example in OpenCV |
|
void computeDft( InputArray patternImage, OutputArray FourierTransform ); |
|
// Compute the inverse Fourier transform. Output can be complex or real |
|
void computeInverseDft( InputArray FourierTransform, OutputArray inverseFourierTransform, |
|
bool realOutput ); |
|
// Compute the DFT magnitude which is used to find maxima in the spectrum |
|
void computeDftMagnitude( InputArray FourierTransform, OutputArray FourierTransformMagnitude ); |
|
// Compute phase map from the complex signal given by non-symmetrical filtering of DFT |
|
void computeFtPhaseMap( InputArray inverseFourierTransform, |
|
InputArray shadowMask, |
|
OutputArray wrappedPhaseMap ); |
|
// Swap DFT quadrants. Come from opencv example |
|
void swapQuadrants( InputOutputArray image, int centerX, int centerY ); |
|
// Filter (non)-symmetrically the DFT. |
|
void frequencyFiltering( InputOutputArray FourierTransform, int centerX1, int centerY1, |
|
int halfRegionWidth, int halfRegionHeight, bool keepInsideRegion, |
|
int centerX2 = -1, int centerY2 = -1 ); |
|
// Find maxima in the spectrum so that we know how it should be filtered |
|
bool findMaxInHalvesTransform( InputArray FourierTransformMag, Point &maxPosition1, |
|
Point &maxPosition2 ); |
|
// Compute phase map from the three sinusoidal patterns |
|
void computePsPhaseMap( InputArrayOfArrays patternImages, |
|
InputArray shadowMask, |
|
OutputArray wrappedPhaseMap ); |
|
|
|
void computeFapsPhaseMap( InputArray a, InputArray b, InputArray theta1, InputArray theta2, |
|
InputArray shadowMask, OutputArray wrappedPhaseMap ); |
|
// Compute a shadow mask to discard shadow regions |
|
void computeShadowMask( InputArrayOfArrays patternImages, OutputArray shadowMask ); |
|
// Data modulation term is used to isolate cross markers |
|
|
|
void extractMarkersLocation( InputArray dataModulationTerm, |
|
std::vector<Point> &markersLocation ); |
|
|
|
void convertToAbsolutePhaseMap( InputArrayOfArrays camPatterns, |
|
InputArray unwrappedProjPhaseMap, |
|
InputArray unwrappedCamPhaseMap, |
|
InputArray shadowMask, |
|
InputArray fundamentalMatrix ); |
|
|
|
Params params; |
|
phase_unwrapping::HistogramPhaseUnwrapping::Params unwrappingParams; |
|
// Class describing markers that are added to the patterns |
|
class Marker{ |
|
private: |
|
Point center, up, right, left, down; |
|
public: |
|
Marker(); |
|
Marker( Point c ); |
|
void drawMarker( OutputArray pattern ); |
|
}; |
|
}; |
|
// Default parameters value |
|
SinusoidalPattern::Params::Params() |
|
{ |
|
width = 800; |
|
height = 600; |
|
nbrOfPeriods = 20; |
|
shiftValue = (float)(2 * CV_PI / 3); |
|
methodId = FAPS; |
|
nbrOfPixelsBetweenMarkers = 56; |
|
horizontal = false; |
|
setMarkers = false; |
|
} |
|
SinusoidalPatternProfilometry_Impl::Marker::Marker(){}; |
|
|
|
SinusoidalPatternProfilometry_Impl::Marker::Marker( Point c ) |
|
{ |
|
center = c; |
|
up.x = c.x; |
|
up.y = c.y - 1; |
|
left.x = c.x - 1; |
|
left.y = c.y; |
|
|
|
down.x = c.x; |
|
down.y = c.y + 1; |
|
right.x = c.x + 1; |
|
right.y = c.y; |
|
} |
|
// Draw marker on a pattern |
|
void SinusoidalPatternProfilometry_Impl::Marker::drawMarker( OutputArray pattern ) |
|
{ |
|
Mat &pattern_ = *(Mat*) pattern.getObj(); |
|
|
|
pattern_.at<uchar>(center.x, center.y) = 255; |
|
pattern_.at<uchar>(up.x, up.y) = 255; |
|
pattern_.at<uchar>(right.x, right.y) = 255; |
|
pattern_.at<uchar>(left.x, left.y) = 255; |
|
pattern_.at<uchar>(down.x, down.y) = 255; |
|
} |
|
|
|
SinusoidalPatternProfilometry_Impl::SinusoidalPatternProfilometry_Impl( |
|
const SinusoidalPattern::Params ¶meters ) : params(parameters) |
|
{ |
|
|
|
} |
|
// Generate sinusoidal patterns. Markers are optional |
|
bool SinusoidalPatternProfilometry_Impl::generate( OutputArrayOfArrays pattern ) |
|
{ |
|
// Three patterns are used in the reference paper. |
|
int nbrOfPatterns = 3; |
|
float meanAmpl = 127.5; |
|
float sinAmpl = 127.5; |
|
// Period in number of pixels |
|
int period; |
|
float frequency; |
|
// m and n are parameters described in the reference paper |
|
int m = params.nbrOfPixelsBetweenMarkers; |
|
int n; |
|
// Offset for the first marker of the first row. |
|
int firstMarkerOffset = 10; |
|
int mnRatio; |
|
int nbrOfMarkersOnOneRow; |
|
std::vector<Mat> &pattern_ = *(std::vector<Mat>*) pattern.getObj(); |
|
|
|
n = params.nbrOfPeriods / nbrOfPatterns; |
|
mnRatio = m / n; |
|
|
|
pattern_.resize(nbrOfPatterns); |
|
|
|
if( params.horizontal ) |
|
{ |
|
period = params.height / params.nbrOfPeriods; |
|
nbrOfMarkersOnOneRow = (int)floor(static_cast<float>((params.width - firstMarkerOffset) / m)); |
|
} |
|
else |
|
{ |
|
period = params.width / params.nbrOfPeriods; |
|
nbrOfMarkersOnOneRow = (int)floor(static_cast<float>((params.height - firstMarkerOffset) / m)); |
|
} |
|
frequency = (float) 1 / period; |
|
|
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
pattern_[i] = Mat(params.height, params.width, CV_8UC1); |
|
|
|
if( params.horizontal ) |
|
pattern_[i] = pattern_[i].t(); |
|
} |
|
// Patterns vary along one direction only so, a row Mat can be created and copied to the pattern's rows |
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
Mat rowValues(1, pattern_[i].cols, CV_8UC1); |
|
|
|
for( int j = 0; j < pattern_[i].cols; ++j ) |
|
{ |
|
rowValues.at<uchar>(0, j) = saturate_cast<uchar>( |
|
meanAmpl + sinAmpl * sin(2 * CV_PI * frequency * j + i * params.shiftValue)); |
|
} |
|
|
|
for( int j = 0; j < pattern_[i].rows; ++j ) |
|
{ |
|
rowValues.row(0).copyTo(pattern_[i].row(j)); |
|
} |
|
} |
|
// Add cross markers to the patterns. |
|
if( params.setMarkers ) |
|
{ |
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
for( int j = 0; j < n; ++j ) |
|
{ |
|
for( int k = 0; k < nbrOfMarkersOnOneRow; ++k ) |
|
{ |
|
Marker mark(Point(firstMarkerOffset + k * m + j * mnRatio, |
|
3 * period / 4 + j * period + i * period * n - i * period / 3)); |
|
mark.drawMarker(pattern_[i]); |
|
params.markersLocation.push_back(Point2f((float)(firstMarkerOffset + k * m + j * mnRatio), |
|
(float) (3 * period / 4 + j * period + i * period * n - i * period / 3))); |
|
} |
|
} |
|
} |
|
} |
|
if( params.horizontal ) |
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
pattern_[i] = pattern_[i].t(); |
|
} |
|
return true; |
|
} |
|
|
|
bool SinusoidalPatternProfilometry_Impl::decode(const std::vector< std::vector<Mat> >& patternImages, |
|
OutputArray disparityMap, |
|
InputArrayOfArrays blackImages, |
|
InputArrayOfArrays whiteImages, int flags ) const |
|
{ |
|
CV_UNUSED(patternImages); |
|
CV_UNUSED(disparityMap); |
|
CV_UNUSED(blackImages); |
|
CV_UNUSED(whiteImages); |
|
CV_UNUSED(flags); |
|
return true; |
|
} |
|
// Most of the steps described in the paper to get the wrapped phase map take place here |
|
void SinusoidalPatternProfilometry_Impl::computePhaseMap( InputArrayOfArrays patternImages, |
|
OutputArray wrappedPhaseMap, |
|
OutputArray shadowMask, |
|
InputArray fundamental ) |
|
{ |
|
std::vector<Mat> &pattern_ = *(std::vector<Mat>*) patternImages.getObj(); |
|
Mat &wrappedPhaseMap_ = *(Mat*) wrappedPhaseMap.getObj(); |
|
int rows = pattern_[0].rows; |
|
int cols = pattern_[0].cols; |
|
int dcWidth = 5; |
|
int dcHeight = 5; |
|
int bpWidth = 21; |
|
int bpHeight = 21; |
|
// Compute wrapped phase map for FTP |
|
if( params.methodId == FTP ) |
|
{ |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
Mat dftImage, complexInverseDft; |
|
Mat dftMag; |
|
int halfWidth = cols/2; |
|
int halfHeight = rows/2; |
|
Point m1, m2; |
|
computeShadowMask(pattern_, shadowMask_); |
|
|
|
computeDft(pattern_[0], dftImage); //compute the complex pattern DFT |
|
swapQuadrants(dftImage, halfWidth, halfHeight); //swap quadrants to get 0 frequency in (halfWidth, halfHeight) |
|
frequencyFiltering(dftImage, halfHeight, halfWidth, dcHeight, dcWidth, false); //get rid of 0 frequency |
|
computeDftMagnitude(dftImage, dftMag); //compute magnitude to find maxima |
|
findMaxInHalvesTransform(dftMag, m1, m2); //look for maxima in the magnitude. Useful information is located around maxima |
|
frequencyFiltering(dftImage, m2.y, m2.x, bpHeight, bpWidth, true); //keep useful information only |
|
swapQuadrants(dftImage,halfWidth, halfHeight); //swap quadrants again to compute inverse dft |
|
computeInverseDft(dftImage, complexInverseDft, false); //compute inverse dft. Result is complex since we only keep half of the spectrum |
|
computeFtPhaseMap(complexInverseDft, shadowMask_, wrappedPhaseMap_); //compute phaseMap from the complex image. |
|
} |
|
// Compute wrapped pahse map for PSP |
|
else if( params.methodId == PSP ) |
|
{ |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
//Mat &fundamental_ = *(Mat*) fundamental.getObj(); |
|
CV_UNUSED(fundamental); |
|
Mat dmt; |
|
int nbrOfPatterns = static_cast<int>(pattern_.size()); |
|
std::vector<Mat> filteredPatterns(nbrOfPatterns); |
|
std::vector<Mat> dftImages(nbrOfPatterns); |
|
std::vector<Mat> dftMags(nbrOfPatterns); |
|
int halfWidth = cols/2; |
|
int halfHeight = rows/2; |
|
Point m1, m2; |
|
|
|
computeShadowMask(pattern_, shadowMask_); |
|
|
|
//this loop symmetrically filters pattern to remove cross markers. |
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
computeDft(pattern_[i], dftImages[i]); |
|
swapQuadrants(dftImages[i], halfWidth, halfHeight); |
|
frequencyFiltering(dftImages[i], halfHeight, halfWidth, dcHeight, dcWidth, false); |
|
computeDftMagnitude(dftImages[i], dftMags[i]); |
|
findMaxInHalvesTransform(dftMags[i], m1, m2); |
|
frequencyFiltering(dftImages[i], m1.y, m1.x, bpHeight, bpWidth, true, m2.y, m2.x);//symmetrical filtering |
|
swapQuadrants(dftImages[i], halfWidth, halfHeight); |
|
computeInverseDft(dftImages[i], filteredPatterns[i], true); |
|
|
|
} |
|
computePsPhaseMap(filteredPatterns, shadowMask_, wrappedPhaseMap_); |
|
} |
|
else if( params.methodId == FAPS ) |
|
{ |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
int nbrOfPatterns = static_cast<int>(pattern_.size()); |
|
std::vector<Mat> unwrappedFTPhaseMaps; |
|
std::vector<Mat> filteredPatterns(nbrOfPatterns); |
|
Mat dmt; |
|
Mat theta1, theta2, a, b; |
|
std::vector<Point> markersLoc; |
|
cv::Size camSize; |
|
camSize.height = pattern_[0].rows; |
|
camSize.width = pattern_[0].cols; |
|
computeShadowMask(pattern_, shadowMask_); |
|
|
|
for( int i = 0; i < nbrOfPatterns; ++i ) |
|
{ |
|
Mat dftImage, complexInverseDft; |
|
Mat dftMag; |
|
Mat tempWrappedPhaseMap; |
|
Mat tempUnwrappedPhaseMap; |
|
int halfWidth = cols/2; |
|
int halfHeight = rows/2; |
|
Point m1, m2; |
|
|
|
computeDft(pattern_[i], dftImage); //compute the complex pattern DFT |
|
swapQuadrants(dftImage, halfWidth, halfHeight); //swap quadrants to get 0 frequency in (halfWidth, halfHeight) |
|
frequencyFiltering(dftImage, halfHeight, halfWidth, dcHeight, dcWidth, false); //get rid of 0 frequency |
|
computeDftMagnitude(dftImage, dftMag); //compute magnitude to find maxima |
|
findMaxInHalvesTransform(dftMag, m1, m2); //look for maxima in the magnitude. Useful information is located around maxima |
|
frequencyFiltering(dftImage, m2.y, m2.x, bpHeight, bpWidth, true); //keep useful information only |
|
swapQuadrants(dftImage,halfWidth, halfHeight); //swap quadrants again to compute inverse dft |
|
computeInverseDft(dftImage, complexInverseDft, false); //compute inverse dft. Result is complex since we only keep half of the spectrum |
|
computeFtPhaseMap(complexInverseDft, shadowMask_, tempWrappedPhaseMap); //compute phaseMap from the complex image. |
|
unwrapPhaseMap(tempWrappedPhaseMap, tempUnwrappedPhaseMap, camSize, shadowMask); |
|
unwrappedFTPhaseMaps.push_back(tempUnwrappedPhaseMap); |
|
computeInverseDft(dftImage, filteredPatterns[i], true); |
|
} |
|
|
|
theta1.create(camSize.height, camSize.width, unwrappedFTPhaseMaps[0].type()); |
|
theta2.create(camSize.height, camSize.width, unwrappedFTPhaseMaps[0].type()); |
|
a.create(camSize.height, camSize.width, CV_32FC1); |
|
b.create(camSize.height, camSize.width, CV_32FC1); |
|
|
|
a = filteredPatterns[0] - filteredPatterns[1]; |
|
b = filteredPatterns[1] - filteredPatterns[2]; |
|
|
|
theta1 = unwrappedFTPhaseMaps[1] - unwrappedFTPhaseMaps[0]; |
|
theta2 = unwrappedFTPhaseMaps[2] - unwrappedFTPhaseMaps[1]; |
|
|
|
computeFapsPhaseMap(a, b, theta1, theta2, shadowMask_, wrappedPhaseMap_); |
|
} |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::unwrapPhaseMap( InputArray wrappedPhaseMap, |
|
OutputArray unwrappedPhaseMap, |
|
cv::Size camSize, |
|
InputArray shadowMask ) |
|
{ |
|
int rows = params.height; |
|
int cols = params.width; |
|
unwrappingParams.width = camSize.width; |
|
unwrappingParams.height = camSize.height; |
|
|
|
Mat &wPhaseMap = *(Mat*) wrappedPhaseMap.getObj(); |
|
Mat &uPhaseMap = *(Mat*) unwrappedPhaseMap.getObj(); |
|
Mat mask; |
|
|
|
if( shadowMask.empty() ) |
|
{ |
|
mask.create(rows, cols, CV_8UC1); |
|
mask = Scalar::all(255); |
|
} |
|
else |
|
{ |
|
Mat &temp = *(Mat*) shadowMask.getObj(); |
|
temp.copyTo(mask); |
|
} |
|
|
|
Ptr<phase_unwrapping::HistogramPhaseUnwrapping> phaseUnwrapping = |
|
phase_unwrapping::HistogramPhaseUnwrapping::create(unwrappingParams); |
|
|
|
phaseUnwrapping->unwrapPhaseMap(wPhaseMap, uPhaseMap, mask); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::findProCamMatches( InputArray projUnwrappedPhaseMap, |
|
InputArray camUnwrappedPhaseMap, |
|
OutputArrayOfArrays matches ) |
|
{ |
|
CV_UNUSED(projUnwrappedPhaseMap); |
|
CV_UNUSED(camUnwrappedPhaseMap); |
|
CV_UNUSED(matches); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computeDft( InputArray patternImage, |
|
OutputArray FourierTransform ) |
|
{ |
|
Mat &pattern_ = *(Mat*) patternImage.getObj(); |
|
Mat &FourierTransform_ = *(Mat*) FourierTransform.getObj(); |
|
Mat padded; |
|
int m = getOptimalDFTSize(pattern_.rows); |
|
int n = getOptimalDFTSize(pattern_.cols); |
|
copyMakeBorder(pattern_, padded, 0, m - pattern_.rows, 0, n - pattern_.cols, BORDER_CONSTANT, |
|
Scalar::all(0)); |
|
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)}; |
|
merge(planes, 2, FourierTransform_); |
|
dft(FourierTransform_, FourierTransform_); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computeInverseDft( InputArray FourierTransform, |
|
OutputArray inverseFourierTransform, |
|
bool realOutput ) |
|
{ |
|
Mat &FourierTransform_ = *(Mat*) FourierTransform.getObj(); |
|
Mat &inverseFourierTransform_ = *(Mat*) inverseFourierTransform.getObj(); |
|
if( realOutput ) |
|
idft(FourierTransform_, inverseFourierTransform_, DFT_SCALE | DFT_REAL_OUTPUT); |
|
else |
|
idft(FourierTransform_, inverseFourierTransform_, DFT_SCALE); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computeDftMagnitude( InputArray FourierTransform, |
|
OutputArray FourierTransformMagnitude ) |
|
{ |
|
Mat &FourierTransform_ = *(Mat*) FourierTransform.getObj(); |
|
Mat &FourierTransformMagnitude_ = *(Mat*) FourierTransformMagnitude.getObj(); |
|
Mat planes[2]; |
|
split(FourierTransform_, planes); |
|
magnitude(planes[0], planes[1], planes[0]); |
|
FourierTransformMagnitude_ = planes[0]; |
|
FourierTransformMagnitude_ += Scalar::all(1); |
|
log(FourierTransformMagnitude_, FourierTransformMagnitude_); |
|
FourierTransformMagnitude_ = FourierTransformMagnitude_( |
|
Rect(0, 0, FourierTransformMagnitude_.cols & -2, FourierTransformMagnitude_.rows & - 2)); |
|
normalize(FourierTransformMagnitude_, FourierTransformMagnitude_, 0, 1, NORM_MINMAX); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computeFtPhaseMap( InputArray inverseFourierTransform, |
|
InputArray shadowMask, |
|
OutputArray wrappedPhaseMap ) |
|
{ |
|
|
|
Mat &inverseFourierTransform_ = *(Mat*) inverseFourierTransform.getObj(); |
|
Mat &wrappedPhaseMap_ = *(Mat*) wrappedPhaseMap.getObj(); |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
Mat planes[2]; |
|
|
|
int rows = inverseFourierTransform_.rows; |
|
int cols = inverseFourierTransform_.cols; |
|
|
|
if( wrappedPhaseMap_.empty () ) |
|
wrappedPhaseMap_.create(rows, cols, CV_32FC1); |
|
|
|
split(inverseFourierTransform_, planes); |
|
|
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
if( shadowMask_.at<uchar>(i, j) != 0 ) |
|
{ |
|
float im = planes[1].at<float>(i, j); |
|
float re = planes[0].at<float>(i, j); |
|
wrappedPhaseMap_.at<float>(i, j) = atan2(re, im); |
|
} |
|
else |
|
{ |
|
wrappedPhaseMap_.at<float>(i, j) = 0; |
|
} |
|
} |
|
} |
|
} |
|
void SinusoidalPatternProfilometry_Impl::swapQuadrants( InputOutputArray image, |
|
int centerX, int centerY ) |
|
{ |
|
Mat &image_ = *(Mat*) image.getObj(); |
|
Mat q0(image_, Rect(0, 0, centerX, centerY)); |
|
Mat q1(image_, Rect(centerX, 0, centerX, centerY)); |
|
Mat q2(image_, Rect(0, centerY, centerX, centerY)); |
|
Mat q3(image_, Rect(centerX, centerY, centerX, centerY)); |
|
Mat tmp; |
|
|
|
q0.copyTo(tmp); |
|
q3.copyTo(q0); |
|
tmp.copyTo(q3); |
|
|
|
q1.copyTo(tmp); |
|
q2.copyTo(q1); |
|
tmp.copyTo(q2); |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::frequencyFiltering( InputOutputArray FourierTransform, |
|
int centerX1, int centerY1, |
|
int halfRegionWidth, int halfRegionHeight, |
|
bool keepInsideRegion, int centerX2, |
|
int centerY2 ) |
|
{ |
|
Mat &FourierTransform_ = *(Mat*) FourierTransform.getObj(); |
|
int rows = FourierTransform_.rows; |
|
int cols = FourierTransform_.cols; |
|
int type = FourierTransform_.type(); |
|
if( keepInsideRegion ) |
|
{ |
|
Mat maskedTransform(rows, cols, type); |
|
maskedTransform = Scalar::all(0); |
|
Mat roi1 = FourierTransform_( |
|
Rect(centerY1 - halfRegionHeight, centerX1 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
Mat dstRoi1 = maskedTransform( |
|
Rect(centerY1 - halfRegionHeight, centerX1 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
roi1.copyTo(dstRoi1); |
|
|
|
if( centerY2 != -1 || centerX2 != -1 ) |
|
{ |
|
Mat roi2 = FourierTransform_( |
|
Rect(centerY2 - halfRegionHeight, centerX2 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
Mat dstRoi2 = maskedTransform( |
|
Rect(centerY2 - halfRegionHeight, centerX2 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
roi2.copyTo(dstRoi2); |
|
} |
|
FourierTransform_ = maskedTransform; |
|
} |
|
else |
|
{ |
|
Mat roi(2 * halfRegionHeight, 2 * halfRegionWidth, type); |
|
roi = Scalar::all(0); |
|
|
|
Mat dstRoi1 = FourierTransform_( |
|
Rect(centerY1 - halfRegionHeight, centerX1 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
roi.copyTo(dstRoi1); |
|
|
|
if( centerY2 != -1 || centerX2 != -1 ) |
|
{ |
|
Mat dstRoi2 = FourierTransform_( |
|
Rect(centerY2 - halfRegionHeight, centerX2 - halfRegionWidth, |
|
2 * halfRegionHeight, 2 * halfRegionWidth)); |
|
roi.copyTo(dstRoi2); |
|
} |
|
} |
|
} |
|
bool SinusoidalPatternProfilometry_Impl::findMaxInHalvesTransform( InputArray FourierTransformMag, |
|
Point &maxPosition1, |
|
Point &maxPosition2 ) |
|
{ |
|
Mat &FourierTransformMag_ = *(Mat*) FourierTransformMag.getObj(); |
|
|
|
int centerX = FourierTransformMag_.cols / 2; |
|
int centerY = FourierTransformMag_.rows / 2; |
|
Mat h0, h1; |
|
double maxV1 = -1; |
|
double maxV2 = -1; |
|
int margin = 5; |
|
|
|
if( params.horizontal ) |
|
{ |
|
h0 = FourierTransformMag_(Rect(0, 0, FourierTransformMag_.cols, centerY - margin)); |
|
h1 = FourierTransformMag_( |
|
Rect(0, centerY + margin, FourierTransformMag_.cols, centerY - margin)); |
|
} |
|
else |
|
{ |
|
h0 = FourierTransformMag_(Rect(0, 0, centerX - margin, FourierTransformMag_.rows)); |
|
h1 = FourierTransformMag_( |
|
Rect(centerX + margin, 0, centerX - margin, FourierTransformMag_.rows)); |
|
} |
|
|
|
minMaxLoc(h0, NULL, &maxV1, NULL, &maxPosition1); |
|
minMaxLoc(h1, NULL, &maxV2, NULL, &maxPosition2); |
|
|
|
if( params.horizontal ) |
|
{ |
|
maxPosition2.y = maxPosition2.y + centerY + margin; |
|
} |
|
else |
|
{ |
|
maxPosition2.x = maxPosition2.x + centerX + margin; |
|
} |
|
|
|
if( maxV1 == -1 || maxV2 == -1 ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computePsPhaseMap( InputArrayOfArrays patternImages, |
|
InputArray shadowMask, |
|
OutputArray wrappedPhaseMap ) |
|
{ |
|
std::vector<Mat> &pattern_ = *(std::vector<Mat>*) patternImages.getObj(); |
|
Mat &wrappedPhaseMap_ = *(Mat*) wrappedPhaseMap.getObj(); |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
|
|
int rows = pattern_[0].rows; |
|
int cols = pattern_[0].cols; |
|
|
|
float i1 = 0; |
|
float i2 = 0; |
|
float i3 = 0; |
|
|
|
if( wrappedPhaseMap_.empty() ) |
|
wrappedPhaseMap_.create(rows, cols, CV_32FC1); |
|
|
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
if( shadowMask_.at<uchar>(i, j) != 0 ) |
|
{ |
|
if( pattern_[0].type() == CV_8UC1 ) |
|
{ |
|
i1 = pattern_[0].at<uchar>(i, j); |
|
i2 = pattern_[1].at<uchar>(i, j); |
|
i3 = pattern_[2].at<uchar>(i, j); |
|
} |
|
else if( pattern_[0].type() == CV_32FC1 ) |
|
{ |
|
i1 = pattern_[0].at<float>(i, j); |
|
i2 = pattern_[1].at<float>(i, j); |
|
i3 = pattern_[2].at<float>(i, j); |
|
} |
|
float num = (1- cos(params.shiftValue)) * (i3 - i2); |
|
float den = sin(params.shiftValue) * (2 * i1 - i2 - i3); |
|
wrappedPhaseMap_.at<float>(i,j) = atan2(num, den); |
|
} |
|
else |
|
{ |
|
wrappedPhaseMap_.at<float>(i,j) = 0; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void SinusoidalPatternProfilometry_Impl::computeFapsPhaseMap( InputArray a, |
|
InputArray b, |
|
InputArray theta1, |
|
InputArray theta2, |
|
InputArray shadowMask, |
|
OutputArray wrappedPhaseMap ) |
|
{ |
|
Mat &a_ = *(Mat*) a.getObj(); |
|
Mat &b_ = *(Mat*) b.getObj(); |
|
Mat &theta1_ = *(Mat*) theta1.getObj(); |
|
Mat &theta2_ = *(Mat*) theta2.getObj(); |
|
Mat &wrappedPhaseMap_ = *(Mat*) wrappedPhaseMap.getObj(); |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
|
|
int rows = a_.rows; |
|
int cols = a_.cols; |
|
|
|
if( wrappedPhaseMap_.empty() ) |
|
wrappedPhaseMap_.create(rows, cols, CV_32FC1); |
|
|
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
if( shadowMask_.at<uchar>(i, j ) != 0 ) |
|
{ |
|
float num = (1 - cos(theta2_.at<float>(i, j))) * a_.at<float>(i, j) + |
|
(1 - cos(theta1_.at<float>(i, j))) * b_.at<float>(i, j); |
|
|
|
float den = sin(theta1_.at<float>(i, j)) * b_.at<float>(i, j) - |
|
sin(theta2_.at<float>(i, j)) * a_.at<float>(i, j); |
|
|
|
wrappedPhaseMap_.at<float>(i, j) = atan2(num, den); |
|
} |
|
else |
|
{ |
|
wrappedPhaseMap_.at<float>(i, j) = 0; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//compute shadow mask from three patterns. Valid pixels are lit at least by one pattern |
|
void SinusoidalPatternProfilometry_Impl::computeShadowMask( InputArrayOfArrays patternImages, |
|
OutputArray shadowMask ) |
|
{ |
|
std::vector<Mat> &patternImages_ = *(std::vector<Mat>*) patternImages.getObj(); |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
Mat mean; |
|
int rows = patternImages_[0].rows; |
|
int cols = patternImages_[0].cols; |
|
float i1, i2, i3; |
|
|
|
mean.create(rows, cols, CV_32FC1); |
|
|
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
i1 = (float) patternImages_[0].at<uchar>(i, j); |
|
i2 = (float) patternImages_[1].at<uchar>(i, j); |
|
i3 = (float) patternImages_[2].at<uchar>(i, j); |
|
mean.at<float>(i, j) = (i1 + i2 + i3) / 3; |
|
} |
|
} |
|
mean.convertTo(mean, CV_8UC1); |
|
threshold(mean, shadowMask_, 10, 255, 0); |
|
|
|
} |
|
// Compute the data modulation term according to the formula given in the reference paper |
|
void SinusoidalPatternProfilometry_Impl::computeDataModulationTerm( InputArrayOfArrays patternImages, |
|
OutputArray dataModulationTerm, |
|
InputArray shadowMask ) |
|
{ |
|
std::vector<Mat> &patternImages_ = *(std::vector<Mat>*) patternImages.getObj(); |
|
Mat &dataModulationTerm_ = *(Mat*) dataModulationTerm.getObj(); |
|
Mat &shadowMask_ = *(Mat*) shadowMask.getObj(); |
|
int rows = patternImages_[0].rows; |
|
int cols = patternImages_[0].cols; |
|
float num = 0; |
|
float den = 0; |
|
float i1 = 0; |
|
float i2 = 0; |
|
float i3 = 0; |
|
|
|
int iOffset, jOffset; |
|
Mat dmt(rows, cols, CV_32FC1); |
|
Mat threshedDmt; |
|
|
|
if( dataModulationTerm_.empty() ) |
|
{ |
|
dataModulationTerm_.create(rows, cols, CV_8UC1); |
|
} |
|
if( shadowMask_.empty() ) |
|
{ |
|
shadowMask_.create(rows, cols, CV_8U); |
|
shadowMask_ = Scalar::all(255); |
|
} |
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
if( shadowMask_.at<uchar>(i, j) != 0 ){ |
|
if( i - 2 == - 2 ) |
|
{ |
|
iOffset = 0; |
|
} |
|
else if( i - 2 == - 1 ) |
|
{ |
|
iOffset = -1; |
|
} |
|
else if( i - 2 + 4 == rows + 1 ) |
|
{ |
|
iOffset = -3; |
|
} |
|
else |
|
{ |
|
iOffset = -2; |
|
} |
|
if( j - 2 == -2 ) |
|
{ |
|
jOffset = 0; |
|
} |
|
else if( j - 2 == -1 ) |
|
{ |
|
jOffset = -1; |
|
} |
|
else if( j - 2 + 4 == cols + 1 ) |
|
{ |
|
jOffset = -3; |
|
} |
|
else |
|
{ |
|
jOffset = -2; |
|
} |
|
Mat roi = shadowMask_(Rect(j + jOffset, i + iOffset, 4, 4)); |
|
Scalar nbrOfValidPixels = sum(roi); |
|
if( nbrOfValidPixels[0] < 14*255 ) |
|
{ |
|
dmt.at<float>(i, j) = 0; |
|
} |
|
else |
|
{ |
|
i1 = patternImages_[0].at<uchar>(i, j); |
|
i2 = patternImages_[1].at<uchar>(i, j); |
|
i3 = patternImages_[2].at<uchar>(i, j); |
|
|
|
num = sqrt(3 * ( i1 - i3 ) * ( i1 - i3 ) + ( 2 * i2 - i1 - i3 ) * ( 2 * i2 - i1 - i3 )); |
|
den = i1 + i2 + i3; |
|
dmt.at<float>(i, j) = 1 - num / den; |
|
} |
|
} |
|
else |
|
{ |
|
dmt.at<float>(i, j) = 0; |
|
} |
|
} |
|
} |
|
Mat kernel(3, 3, CV_32F); |
|
kernel.at<float>(0, 0) = 1.f/16.f; |
|
kernel.at<float>(1, 0) = 2.f/16.f; |
|
kernel.at<float>(2, 0) = 1.f/16.f; |
|
|
|
kernel.at<float>(0, 1) = 2.f/16.f; |
|
kernel.at<float>(1, 1) = 4.f/16.f; |
|
kernel.at<float>(2, 1) = 2.f/16.f; |
|
|
|
kernel.at<float>(0, 2) = 1.f/16.f; |
|
kernel.at<float>(1, 2) = 2.f/16.f; |
|
kernel.at<float>(2, 2) = 1.f/16.f; |
|
|
|
Point anchor = Point(-1, -1); |
|
double delta = 0; |
|
int ddepth = -1; |
|
|
|
filter2D(dmt, dmt, ddepth, kernel, anchor, delta, BORDER_DEFAULT); |
|
|
|
threshold(dmt, threshedDmt, 0.4, 1, THRESH_BINARY); |
|
threshedDmt.convertTo(dataModulationTerm_, CV_8UC1, 255, 0); |
|
} |
|
|
|
//Extract marker location on the DMT. Duplicates are removed |
|
void SinusoidalPatternProfilometry_Impl::extractMarkersLocation( InputArray dataModulationTerm, |
|
std::vector<Point> &markersLocation ) |
|
{ |
|
Mat &dmt = *(Mat*) dataModulationTerm.getObj(); |
|
int rows = dmt.rows; |
|
int cols = dmt.cols; |
|
int halfRegionSize = 6; |
|
|
|
for( int i = 0; i < rows; ++i ) |
|
{ |
|
for( int j = 0; j < cols; ++j ) |
|
{ |
|
if( dmt.at<uchar>(i,j) != 0 ) |
|
{ |
|
bool addToVector = true; |
|
for(int k = 0; k < (int)markersLocation.size(); ++k) |
|
{ |
|
if( markersLocation[k].x - halfRegionSize < i && |
|
markersLocation[k].x + halfRegionSize > i && |
|
markersLocation[k].y - halfRegionSize < j && |
|
markersLocation[k].y + halfRegionSize > j ){ |
|
addToVector = false; |
|
} |
|
} |
|
if(addToVector) |
|
{ |
|
Point temp(i,j); |
|
markersLocation.push_back(temp); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
void SinusoidalPatternProfilometry_Impl::convertToAbsolutePhaseMap( InputArrayOfArrays camPatterns, |
|
InputArray unwrappedProjPhaseMap, |
|
InputArray unwrappedCamPhaseMap, |
|
InputArray shadowMask, |
|
InputArray fundamentalMatrix ) |
|
{ |
|
std::vector<Mat> &camPatterns_ = *(std::vector<Mat>*) camPatterns.getObj(); |
|
CV_UNUSED(unwrappedCamPhaseMap); |
|
CV_UNUSED(unwrappedProjPhaseMap); |
|
|
|
Mat &fundamental = *(Mat*) fundamentalMatrix.getObj(); |
|
|
|
Mat camDmt; |
|
|
|
std::vector<Point> markersLocation; |
|
|
|
computeDataModulationTerm(camPatterns_, camDmt, shadowMask); |
|
|
|
std::vector<Vec3f> epilines; |
|
computeCorrespondEpilines(params.markersLocation, 2, fundamental, epilines); |
|
|
|
} |
|
Ptr<SinusoidalPattern> SinusoidalPattern::create( Ptr<SinusoidalPattern::Params> params ) |
|
{ |
|
return makePtr<SinusoidalPatternProfilometry_Impl>(*params); |
|
} |
|
} |
|
}
|
|
|