blob: e96426847d311b6b6337c67d1100ce6ec6bcf06b [file]
#include "opencv_process.h"
#include "hmd_opencv_sandbox.h"
#include <opencv2/imgproc/types_c.h>
#include <opencv2/calib3d.hpp>
#include <opencv2/imgproc.hpp>
#include "common_hello.h"
#include <time.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
#define NUM_DISP 96 //Max disparity.
inline double * CoPTr( const std::initializer_list<double>& d ) { return (double*)d.begin(); }
#define DO_FISHEYE 1
#define DO_PROFILE 1
#if DO_PROFILE
#define PROFILE( x ) { double Now = OGGetAbsoluteTime(); dprintf( 1, "\x1b[2K%27s: %.3fms\n", x, (Now-Start)*1000.0); Start = Now; }
#else
#define PROFILE( x )
#endif
Matrix4 Matrix4FromCVMatrix( cv::Mat matin )
{
Matrix4 out;
out.identity();
for ( int y = 0; y < matin.rows; y++ )
{
for ( int x = 0; x < matin.cols; x++ )
{
out[x + y * 4] = (float)matin.at<double>( y, x );
}
}
return out;
}
OpenCVProcess::OpenCVProcess( CameraApp * parent ) :
m_iFrameBufferLength( 0 )
, m_pFrameBuffer( 0 )
, m_pthread( 0 )
, m_parent( parent )
, m_iCurrentStereoAlgorithm( -1 )
, m_bScreenshotNext( 0 )
, m_iHasFrameForUpdate( 0 )
, m_iDoneFrameOutput( 0 )
, m_iProcFrames( 0 )
, m_iFramesSinceFPS( 0 )
, m_dTimeOfLastFPS( 0 )
, m_bQuitThread( false )
{
fNAN = nanf( "" );
}
OpenCVProcess::~OpenCVProcess()
{
m_bQuitThread = true;
if ( m_pthread )
{
m_pthread->join();
}
if ( m_iPBOids )
{
glDeleteBuffers( 2, m_iPBOids );
glDeleteFramebuffers( 1, &m_iGLfrback );
glDeleteTextures( 1, &m_iGLimback );
}
}
bool OpenCVProcess::OpenCVAppStart()
{
#define MOGRIFY_X 4
#define MOGRIFY_Y 4
#define IGNORE_EDGE_DATA_PIXELS 4
#define LAGFRAMES 4
#define DENOISE_PASSES 2
vr::EVRTrackedCameraError ce = vr::VRTrackedCamera()->AcquireVideoStreamingService( vr::k_unTrackedDeviceIndex_Hmd, &m_pCamera );
if ( ce )
{
dprintf( 0, "Error getting video streaming service. Exiting. Error: %d\n", ce );
return false;
}
vr::ETrackedPropertyError err;
m_parent->m_parent->m_pIVRSystem->GetArrayTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_CameraToHeadTransforms_Matrix34_Array, vr::k_unHmdMatrix34PropertyTag, (void *)m_headFromCamera, sizeof( vr::HmdMatrix34_t ) * 2, &err );
if ( err != vr::TrackedProp_Success )
{
dprintf( 0, "ERROR: Could not get camera to head transforms.\n" );
return false;
}
for ( int nEye = vr::Eye_Left; nEye <= vr::Eye_Right; nEye++ )
{
vr::HmdVector2_t focalLength;
vr::HmdVector2_t center;
//vr::EVRDistortionFunctionType eDistortionType;
//double fTempDistCoeffs[vr::k_unMaxDistortionFunctionParameters];
vr::EVRTrackedCameraError vrTrackedCameraError = vr::VRTrackedCamera()->GetCameraIntrinsics( vr::k_unTrackedDeviceIndex_Hmd, nEye, vr::VRTrackedCameraFrameType_Undistorted, &focalLength, &center );
if ( vrTrackedCameraError != vr::VRTrackedCameraError_None )
dprintf( 0, "error on GetCameraIntrinsics: %d\n", vrTrackedCameraError );
uint32_t nUndistortedWidth, nUndistortedHeight;
vrTrackedCameraError = vr::VRTrackedCamera()->GetCameraFrameSize( vr::k_unTrackedDeviceIndex_Hmd, vr::VRTrackedCameraFrameType_Undistorted, &nUndistortedWidth, &nUndistortedHeight, nullptr );
dprintf( 0, "undisorted frame size: %d %d\n", nUndistortedWidth, nUndistortedHeight );
// Currently can't get intrinsics (focal length and center) with "Distorted" type. If I get them with "Undistorted" type,
// and the undistorted size is different from the distorted size, the center will need to be corrected to account for the
// difference. Something like the below...
//
//center.v[ 0 ] -= ( ( nUndistortedWidth - m_nCameraFrameWidth ) / 2 );
//center.v[ 1 ] -= ( ( nUndistortedHeight - m_nCameraFrameHeight ) / 2 );
//
m_cameraIntrinsics[nEye].fx = focalLength.v[0]; //414, 416
m_cameraIntrinsics[nEye].cx = center.v[0]; // center.v[0]; //479, 486
m_cameraIntrinsics[nEye].fy = focalLength.v[1]; //414, 416
m_cameraIntrinsics[nEye].cy = center.v[1]; // center.v[1]; //502, 500
}
//Create Rectification Maps
double fx, cx, fy, cy;
double tmp[3][3] = { 0 };
tmp[2][2] = 1.0;
fx = m_cameraIntrinsics[0].fx;
cx = m_cameraIntrinsics[0].cx;
fy = m_cameraIntrinsics[0].fy;
cy = m_cameraIntrinsics[0].cy;
tmp[0][0] = fx;
tmp[0][2] = cx;
tmp[1][1] = fy;
tmp[1][2] = cy;
//Get coefficients... This is problematic. So, we use "undistorted" imagery from the camera.
double distortion_coefficients[vr::k_unMaxDistortionFunctionParameters * 2] = { 0 };
//double distortion_coefficients_from_steamvr[vr::k_unMaxDistortionFunctionParameters * 2];
if ( DO_FISHEYE )
{
m_parent->m_parent->m_pIVRSystem->GetArrayTrackedDeviceProperty( vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_CameraDistortionCoefficients_Float_Array, vr::k_unFloatPropertyTag,
(void*)distortion_coefficients, vr::k_unMaxDistortionFunctionParameters * 2 * sizeof( double ), 0 );
}
cv::Mat K1 = cv::Mat( cv::Size( 3, 3 ), CV_64F, &(tmp[0][0]) ).clone();
cv::Mat D1( DO_FISHEYE ? 4 : 8, 1, CV_64F, &distortion_coefficients[vr::k_unMaxDistortionFunctionParameters * 0] );
fx = m_cameraIntrinsics[1].fx;
cx = m_cameraIntrinsics[1].cx;
fy = m_cameraIntrinsics[1].fy;
cy = m_cameraIntrinsics[1].cy;
/// ROW COL
tmp[0][0] = fx;
tmp[0][2] = cx;
tmp[1][1] = fy;
tmp[1][2] = cy;
cv::Mat K2 = cv::Mat( cv::Size( 3, 3 ), CV_64F, &(tmp[0][0]) ).clone();
cv::Mat D2( DO_FISHEYE ? 4 : 8, 1, CV_64F, &distortion_coefficients[vr::k_unMaxDistortionFunctionParameters * 1] );
Matrix4 headFromLeftCamera_steamvr = ConvertSteamVRMatrixToMatrix4( m_headFromCamera[0] );
Matrix4 headFromRightCamera_steamvr = ConvertSteamVRMatrixToMatrix4( m_headFromCamera[1] );
Matrix4 RightCamerafromHead_steamvr = headFromRightCamera_steamvr.invert();
Matrix4 rotate180AroundX;
rotate180AroundX.identity();
rotate180AroundX.rotateX( 180 );
Matrix4 rightCameraFromLeftCamera_steamvr = RightCamerafromHead_steamvr * headFromLeftCamera_steamvr;
Matrix4 rightCameraFromLeftCamera_opencv = rotate180AroundX * rightCameraFromLeftCamera_steamvr * rotate180AroundX;
Vector4 RightEyeFromLeftEye = (rightCameraFromLeftCamera_steamvr * Vector4( 0, 0, 0, 1 ));
m_centerFromLeftEye = RightEyeFromLeftEye / 2;
m_CameraDistanceMeters = ( RightEyeFromLeftEye * Vector4( 1, 1, 1, 0 ) ).length();
m_centerFromLeftEye.w = 0;
double posetrans[3] = { rightCameraFromLeftCamera_opencv[12], rightCameraFromLeftCamera_opencv[13], rightCameraFromLeftCamera_opencv[14] };
rightCameraFromLeftCamera_opencv.transpose();
double posemat[9] = {
rightCameraFromLeftCamera_opencv[0], rightCameraFromLeftCamera_opencv[1], rightCameraFromLeftCamera_opencv[2],
rightCameraFromLeftCamera_opencv[4], rightCameraFromLeftCamera_opencv[5], rightCameraFromLeftCamera_opencv[6],
rightCameraFromLeftCamera_opencv[8], rightCameraFromLeftCamera_opencv[9], rightCameraFromLeftCamera_opencv[10] };
cv::Mat R( cv::Size( 3, 3 ), CV_64F, posemat /* Boy I hope the major is right */ );
cv::Mat T( 3, 1, CV_64F, posetrans );
cv::Mat R1, R2, P1, P2;
if ( DO_FISHEYE )
{
cv::fisheye::stereoRectify( K1, D1, K2, D2, cv::Size( 960, 960 ), R, T, R1, R2, P1, P2, m_cvQ, cv::CALIB_ZERO_DISPARITY, cv::Size( 960, 960 ), 0.0, 0.7 );
cv::fisheye::initUndistortRectifyMap( K1, D1, R1, P1, cv::Size( 960, 960 ), CV_16SC2, m_leftMap1, m_leftMap2 );
cv::fisheye::initUndistortRectifyMap( K2, D2, R2, P2, cv::Size( 960, 960 ), CV_16SC2, m_rightMap1, m_rightMap2 );
}
else
{
cv::stereoRectify( K1, D1, K2, D2, cv::Size( 960, 960 ), R, T, R1, R2, P1, P2, m_cvQ, cv::CALIB_ZERO_DISPARITY );
cv::initUndistortRectifyMap( K1, D1, R1, P1, cv::Size( 960, 960 ), CV_16SC2, m_leftMap1, m_leftMap2 );
cv::initUndistortRectifyMap( K2, D2, R2, P2, cv::Size( 960, 960 ), CV_16SC2, m_rightMap1, m_rightMap2 );
}
m_R1 = Matrix4FromCVMatrix( R1 );
m_R1inv = m_R1;
m_R1inv = m_R1inv.invert();
m_Q = Matrix4FromCVMatrix( m_cvQ );
uint32_t width, height;
vr::VRTextureBounds_t vtb;
dprintf( 0, "Get Cam: %lld\n", m_pCamera );
ce = vr::VRTrackedCamera()->GetVideoStreamTextureSize( vr::k_unTrackedDeviceIndex_Hmd, DO_FISHEYE ? vr::VRTrackedCameraFrameType_Distorted : vr::VRTrackedCameraFrameType_Undistorted, &vtb, &width, &height );
if ( ce )
{
dprintf( 0, "Error getting frame size (%d)\n", ce );
return false;
}
m_iFrameBufferLength = width * height * 4;
m_iFBSideWidth = width / 2;
m_iFBSideHeight = height;
m_iFBAlgoWidth = m_iFBSideWidth / MOGRIFY_X;
m_iFBAlgoHeight = m_iFBSideHeight / MOGRIFY_Y;
m_pDisparity = (uint16_t*)malloc( m_iFBAlgoWidth * m_iFBAlgoHeight * 2 );
for ( int side = 0; side < 2; side++ )
{
m_pFBSides[side] = (uint8_t*)malloc( (m_iFBAlgoWidth+ NUM_DISP)* m_iFBAlgoHeight * 1 );
m_pFBSidesColor[side] = (uint32_t*)malloc( m_iFBAlgoWidth * m_iFBAlgoHeight * 4 );
}
m_pColorOut = (uint32_t*) calloc( m_iFBAlgoWidth * m_iFBAlgoHeight, sizeof( uint32_t ) );
m_pColorOut2 = (uint32_t*) calloc( m_iFBSideWidth * m_iFBSideHeight, sizeof( uint32_t ) );
glBindTexture( GL_TEXTURE_2D, m_parent->m_iTexture );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0 ); //Always set the base and max mipmap levels of a texture.
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0 );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, m_iFBSideWidth, m_iFBSideHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pColorOut2 );
glBindTexture( GL_TEXTURE_2D, 0 );
m_valids.resize( m_iFBAlgoHeight * m_iFBAlgoWidth );
m_valids.assign( m_valids.size(), 1 );
m_depths.resize( m_iFBAlgoHeight * m_iFBAlgoWidth );
m_pthread = new std::thread( &OpenCVProcess::Thread, this );
//Readback Buffers
//Create PBOs to allow streaming of data.
glGenBuffers( 2, m_iPBOids );
glBindBuffer( GL_PIXEL_PACK_BUFFER, m_iPBOids[0] );
glBufferData( GL_PIXEL_PACK_BUFFER, 960 * 960 * 2 * 4, 0, GL_STREAM_READ );
glBindBuffer( GL_PIXEL_PACK_BUFFER, m_iPBOids[1] );
glBufferData( GL_PIXEL_PACK_BUFFER, 960 * 960 * 2 * 4, 0, GL_STREAM_READ );
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
//Create target framebuffer and texture to stream camera data into.
glGenFramebuffers( 1, &m_iGLfrback );
glGenTextures( 1, &m_iGLimback );
glBindTexture( GL_TEXTURE_2D, m_iGLimback );
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, m_iFBSideWidth, m_iFBSideHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
glBindTexture( GL_TEXTURE_2D, 0 );
//Set up what matrices we can to prevent dynamic memory allocation.
mdisparity_expanded = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth + NUM_DISP, CV_16S );
return true;
}
void OpenCVProcess::Thread()
{
while ( !m_bQuitThread )
{
if ( m_iHasFrameForUpdate == 2 )
{
if( m_pFrameBuffer )
OpenCVAppUpdate();
m_iHasFrameForUpdate = 0;
}
OGUSleep( 1000 );
}
}
void OpenCVProcess::Prerender()
{
if ( m_iDoneFrameOutput )
{
m_iProcFrames++;
m_iFramesSinceFPS++;
double Start = OGGetAbsoluteTime();
if ( Start >= m_dTimeOfLastFPS + 1 )
{
if ( Start - m_dTimeOfLastFPS < 4 )
m_dTimeOfLastFPS++;
else
m_dTimeOfLastFPS = Start;
m_iFPS = m_iFramesSinceFPS;
m_iFramesSinceFPS = 0;
}
glBindTexture( GL_TEXTURE_2D, m_parent->m_iTexture );
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, m_iFBSideWidth, m_iFBSideHeight, GL_RGBA, GL_UNSIGNED_BYTE, m_pColorOut2 ); //If you want to debug m_pColorOut, you can select that here.
glBindTexture( GL_TEXTURE_2D, 0 );
PROFILE( "[GL] Updating output texture" )
m_parent->m_geoDepthMap.TaintVerts( 0 );
m_parent->m_geoDepthMap.Check();
PROFILE( "[GL] Updating output verts" )
m_iDoneFrameOutput = 0;
}
if ( m_iHasFrameForUpdate == 1 )
{
double Start = OGGetAbsoluteTime();
glBindBuffer( GL_PIXEL_PACK_BUFFER, m_iPBOids[0] );
m_pFrameBuffer = (GLubyte*)glMapBuffer( GL_PIXEL_PACK_BUFFER, GL_READ_ONLY );
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
m_iHasFrameForUpdate = 2;
PROFILE( "[GL] Readback" )
}
if ( m_iHasFrameForUpdate == 0 )
{
double Start = OGGetAbsoluteTime();
#if DO_PROFILE
dprintf( 1, "\x1b[1;1f" );
dprintf( 1, "\x1b[2K\x1b[34mFrames: %5d; %3d FPS\x1b[0m\n", m_iProcFrames, m_iFPS );
dprintf( 1, "\x1b[32mGreen FG Test\x1b[0m\n" );
dprintf( 1, "\x1b[31mRed FG Test\x1b[0m\n" );
dprintf( 1, "\x1b[0m" );
#endif
//This uses OpenGL to read back the pixels. It seems to be MUCH faster than the DX alternative inside SteamVR.
vr::EVRTrackedCameraError ce = vr::VRTrackedCamera()->GetVideoStreamTextureGL( m_pCamera, DO_FISHEYE ? vr::VRTrackedCameraFrameType_Distorted : vr::VRTrackedCameraFrameType_Undistorted, &m_iGLimback, &m_lastFrameHeader, sizeof( m_lastFrameHeader ) );
m_lastFrameHeaderMatrix = ConvertSteamVRMatrixToMatrix4( m_lastFrameHeader.trackedDevicePose.mDeviceToAbsoluteTracking );
PROFILE( "[GL] GetVideoStreamTexture" )
glFinish();
PROFILE( "[GL] Flush" )
glBindFramebuffer( GL_FRAMEBUFFER, m_iGLfrback );
glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_iGLimback, 0 );
glBindBuffer( GL_PIXEL_PACK_BUFFER, m_iPBOids[0] );
if ( m_pFrameBuffer ) glUnmapBuffer( GL_PIXEL_PACK_BUFFER );
glReadPixels( 0, 0, m_lastFrameHeader.nWidth, m_lastFrameHeader.nHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0 );
glBindBuffer( GL_PIXEL_PACK_BUFFER, 0 );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
PROFILE( "[GL] PBO read is setup." )
if ( ce )
{
dprintf( 0, "Error getting frame (%d)\n", ce );
}
else
{
m_iHasFrameForUpdate = 1;
}
}
}
void OpenCVProcess::ConvertToGray( cv::InputArray src, cv::OutputArray dst )
{
//You can do this the OpenCV way, but my alternative transform seems more successful.
if ( 0 )
{
cv::cvtColor( src, dst, cv::COLOR_BGR2GRAY );
}
else
{
cv::Mat msrc( src.getMat() );
cv::Mat mdst( dst.getMat() );
int wd = mdst.cols;
int w = msrc.cols;
int h = msrc.rows;
uint32_t * indata = (uint32_t*)msrc.data;
uint8_t * outdata;
int x, y;
for ( y = 0; y < h; y++ )
{
outdata = ((uint8_t*)mdst.data) + y * wd + NUM_DISP;
for ( x = 0; x < w; x++ )
{
uint32_t inx = *(indata++);
int r = (inx >> 0) & 0xff;
int g = (inx >> 8) & 0xff;
int b = (inx >> 16) & 0xff;
//*(outdata++) = inx >> (chan * 8);
*(outdata++) = (uint8_t)((r + g + b) / 3);
}
}
}
}
Vector4 OpenCVProcess::TransformToLocalSpace( float x, float y, int disp )
{
float fDisp = ( float ) disp / 16.f; // 16-bit fixed-point disparity map (where each disparity value has 4 fractional bits)
float lz = m_Q[11] * m_CameraDistanceMeters / ( fDisp * MOGRIFY_X );
float ly = -(y * MOGRIFY_Y + m_Q[7]) / m_Q[11];
float lx = (x * MOGRIFY_X + m_Q[3]) / m_Q[11];
lx *= lz;
ly *= lz;
lz *= -1;
return m_R1inv * Vector4( lx, ly, lz, 1.0 );
}
Vector4 OpenCVProcess::TransformToWorldSpace( float x, float y, int disp )
{
Vector4 local = TransformToLocalSpace( x, y, disp );
Matrix4 mlefteye = m_lastFrameHeaderMatrix;
Vector4 placerelview( local );
Vector4 Worldview = mlefteye * (placerelview);
return Worldview;
}
void OpenCVProcess::BlurDepths()
{
//This does an actual blurring function.
#define RIGHT_EDGE_NO_TRUST 24
std::vector< float > validsback( m_iFBAlgoHeight * m_iFBAlgoWidth );
std::vector< float > depthsback( m_iFBAlgoHeight * m_iFBAlgoWidth );
//Initialize the data for this frame
for ( unsigned y = 0; y < m_iFBAlgoWidth; y++ )
{
for ( unsigned x = 0; x < m_iFBAlgoHeight; x++ )
{
int idx = y * m_iFBAlgoWidth + x;
uint16_t pxi = m_pDisparity[idx];
if ( pxi == 0 || pxi >= m_iFBAlgoWidth * 16 )
{
//If we don't know the depth, then we just discard it. We could handle that here. Additionally,
//if we wanted, we could emit fake dots where we believe the floor to be.
//Right now, we do nothing.
}
else
{
m_valids[idx] = 1.0;
m_depths[idx] = pxi;
}
//Never trust the right side of the screen.
if ( x >= m_iFBAlgoWidth - RIGHT_EDGE_NO_TRUST )
{
m_valids[idx] = 0;
m_depths[idx] = 0;
}
}
}
for ( int iter = 0; iter < 10; iter++ )
{
for ( unsigned y = 4; y < m_iFBAlgoWidth - 4; y++ )
{
for ( unsigned x = 4; x < m_iFBAlgoHeight - 4; x++ )
{
int idx = y * m_iFBAlgoWidth + x;
float tval = 0;
float tdepths = 0;
for ( int ly = -1; ly <= 1; ly++ )
for ( int lx = -1; lx <= 1; lx++ )
{
int idxx = (y+ly) * m_iFBAlgoWidth + x+lx;
tval += m_valids[idxx];
tdepths += m_depths[idxx];
}
validsback[idx] = tval / 9;
depthsback[idx] = tdepths / 9;
}
}
memcpy( &m_valids[0], &validsback[0], sizeof( float ) * validsback.size() );
memcpy( &m_depths[0], &depthsback[0], sizeof( float ) * depthsback.size() );
}
for ( unsigned y = 0; y < m_iFBAlgoHeight; y++ )
{
for ( unsigned x = 0; x < m_iFBAlgoWidth; x++ )
{
int idx = y * m_iFBAlgoWidth + x;
if ( x < 1 || x >= m_iFBAlgoWidth - 1 )
{
//Must throw out edges.
m_pDisparity[idx] = 0xfff0;
continue;
}
uint16_t pxi = m_pDisparity[idx];
if ( pxi == 0 || pxi >= m_iFBAlgoWidth * 16 )
{
if ( m_valids[idx] < .00005 )
{
m_valids[idx] = 0;
m_depths[idx] = 0;
m_pDisparity[idx] = 0xfff0;
}
else
{
m_pDisparity[idx] = (uint16_t)(m_depths[idx] / m_valids[idx]);
}
}
m_valids[idx] *= .9f;
m_depths[idx] *= .9f;
}
}
}
void OpenCVProcess::OpenCVAppUpdate()
{
double Start = OGGetAbsoluteTime();
if ( m_iCurrentStereoAlgorithm != m_parent->settings.iStereoAlg )
{
if ( m_parent->settings.iStereoAlg >= 3 ) m_parent->settings.iStereoAlg = 0;
m_iCurrentStereoAlgorithm = m_parent->settings.iStereoAlg;
if ( m_iCurrentStereoAlgorithm == 0 )
{
m_stereo = cv::StereoSGBM::create( 0, NUM_DISP, 7,
0, 0, 0,
4, 55,
25, 4,
cv::StereoSGBM::MODE_SGBM );
}
else if ( m_iCurrentStereoAlgorithm == 1 )
{
m_stereo = cv::StereoSGBM::create( 0, NUM_DISP, 2,
0, 0, 0,
4, 35,
10, 3,
cv::StereoSGBM::MODE_SGBM );
}
else if ( m_iCurrentStereoAlgorithm == 2 )
{
m_stereo = cv::StereoSGBM::create( 0, NUM_DISP, 15,
0, 0, 0,
4, 5,
200, 1,
cv::StereoSGBM::MODE_SGBM );
}
}
origStereoPair = cv::Mat( m_iFBSideHeight, m_iFBSideWidth * 2, CV_8UC4, m_pFrameBuffer );
origLeft = origStereoPair( cv::Rect( 0, 0, 960, 960 ) );
origRight = origStereoPair( cv::Rect( 960, 0, 960, 960 ) );
cv::remap( origLeft, rectLeft, m_leftMap1, m_leftMap2, CV_INTER_LINEAR, cv::BORDER_CONSTANT );
cv::remap( origRight, rectRight, m_rightMap1, m_rightMap2, CV_INTER_LINEAR, cv::BORDER_CONSTANT );
resizedLeft = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth, CV_8UC4, m_pFBSidesColor[0] );
resizedRight = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth, CV_8UC4, m_pFBSidesColor[1] );
cv::resize( rectLeft, resizedLeft, cv::Size( m_iFBAlgoWidth, m_iFBAlgoHeight ) );
cv::resize( rectRight, resizedRight, cv::Size( m_iFBAlgoWidth, m_iFBAlgoHeight ) );
resizedLeftGray = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth + NUM_DISP, CV_8U, m_pFBSides[0] );
resizedRightGray = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth + NUM_DISP, CV_8U, m_pFBSides[1] );
mdisparity = cv::Mat( m_iFBAlgoHeight, m_iFBAlgoWidth, CV_16S, m_pDisparity );
ConvertToGray( resizedLeft, resizedLeftGray );
ConvertToGray( resizedRight, resizedRightGray );
PROFILE( "[OP] Setup" )
{
m_stereo->compute( resizedLeftGray, resizedRightGray, mdisparity_expanded );
uint32_t x, y;
int wd = mdisparity.cols;
int w = mdisparity_expanded.cols;
//int h = mdisparity_expanded.rows;
for ( y = 0; y < m_iFBAlgoWidth; y++ )
{
uint16_t * indata = ((uint16_t*)mdisparity_expanded.data) + y * w + NUM_DISP;
uint16_t * outdata = ((uint16_t*)mdisparity.data) + y * wd;
for ( x = 0; x < m_iFBAlgoHeight; x++ )
{
*(outdata++) = *(indata++);
}
}
}
if ( m_bScreenshotNext )
{
TakeScreenshot();
m_bScreenshotNext = false;
}
PROFILE( "[OP] Stereo Computation")
static int rframe;
//For frame decimation
//rframe++; if ( rframe == 10 ) rframe = 0;
if ( rframe == 0 )
{
int x, y;
for ( y = 0; y < (int)m_iFBSideHeight; y++ )
{
uint32_t * pdsp = &((uint32_t*)rectLeft.data)[y*m_iFBSideWidth];
uint32_t * outlines = &m_pColorOut2[y*m_iFBSideWidth];
for ( x = 0; x < (int)m_iFBSideWidth; x++ )
{
outlines[x] = pdsp[x];// ((*(uint32_t*)(&pxdl[x * 4 + 0])) & 0xff) | ((*(uint32_t*)(&pxdr[x * 4 + 0])) & 0xff00);
}
}
}
PROFILE( "[OP] Outlines update" )
//Potentially emit dots.
if ( 1 )
{
unsigned x, y;
for ( y = 0; y < m_iFBAlgoHeight; y++ )
{
uint16_t * pxin = &m_pDisparity[y*m_iFBAlgoWidth];
for ( x = IGNORE_EDGE_DATA_PIXELS; x < m_iFBAlgoWidth - IGNORE_EDGE_DATA_PIXELS; x++ )
{
uint32_t pxc = pxin[x];
//Color
uint32_t pxo = m_pFBSidesColor[0][(x)+y * m_iFBAlgoWidth];
int pxr = ((pxo >> 0) & 0xff);
int pxg = ((pxo >> 8) & 0xff);
int pxb = ((pxo >> 16) & 0xff);
if ( pxc < 0xfff0 )
{
float frx = x + (rand() % 1000) / 1000.0f;
float fry = y + (rand() % 1000) / 1000.0f;
Vector4 Worldspace = TransformToWorldSpace( frx, fry, pxc );
if ( 1 ) //&& Worldspace.y >= 0 && Worldspace.y < 1.5 )
{
//Create debug map (this appears to the left of the window)
int dx = (int) ( -Worldspace.x * 50.0 + m_parent->m_iDebugTextureW/2 );
int dy = (int) ( Worldspace.z * 50.0 + m_parent->m_iDebugTextureH/2 );
if ( dx >= 0 && dy >= 0 && dx < m_parent->m_iDebugTextureW && dy < m_parent->m_iDebugTextureH )
{
m_parent->m_pDebugTextureData[dx + dy * m_parent->m_iDebugTextureH] = pxo | 0xff;
}
}
int emitevery = (int)(m_parent->settings.iAntEvery + m_parent->settings.fImportanceOfDist * 200 / pxc );
if ( emitevery < 1 ) emitevery = 1;
if ( 0 == (rand() % emitevery) )
m_parent->EmitDot( Worldspace.x, Worldspace.y, Worldspace.z, 1,
pxr / 255.0f, pxg / 255.0f, pxb / 255.0f, (float)((m_parent->m_frameno % m_parent->m_maxframeno) + rand()*10.0 / RAND_MAX) );
}
}
}
}
PROFILE( "[OP] Emit Dots")
if ( 1 )
{
BlurDepths( );
}
PROFILE( "[OP] Blur" )
std::vector< float > & depth_vc = m_parent->m_geoDepthMap.GetVertexArrayPtr( 0 );
if ( rframe == 0 && 1 ) //Process Output
{
uint32_t x, y;
for ( y = 0; y < m_iFBAlgoHeight; y++ )
{
uint16_t * pxin = &m_pDisparity[y*m_iFBAlgoWidth];
uint32_t * pxout = &m_pColorOut[y*m_iFBAlgoWidth];
for ( x = IGNORE_EDGE_DATA_PIXELS; x < m_iFBAlgoWidth - IGNORE_EDGE_DATA_PIXELS; x++ )
{
uint32_t pxo = pxin[x];
int idx = y * m_iFBAlgoWidth + x;
if ( pxo >= 0xfff0 )
{
pxo = 0x202020;// (x > m_iFBAlgoWidth / 2) ? 0 : 129;
depth_vc[idx * 4 + 0] = fNAN;
depth_vc[idx * 4 + 1] = fNAN;
depth_vc[idx * 4 + 2] = fNAN;
depth_vc[idx * 4 + 3] = 0;
}
else
{
//depths[x + y * m_iFBAlgoWidth] = pxin[x];
if ( 1 )
{
//Update depth geometry.
Vector4 Worldspace = TransformToWorldSpace( (float)x, (float)y, pxin[x] );
depth_vc[idx * 4 + 0] = Worldspace.x;
depth_vc[idx * 4 + 1] = Worldspace.y;
depth_vc[idx * 4 + 2] = Worldspace.z;
depth_vc[idx * 4 + 3] = m_valids[idx];
}
//OPTIONAL: Write the color buffer out.
if ( 1 )
{
pxout[x] = pxin[x];
}
}
}
}
}
m_iDoneFrameOutput = 1;
PROFILE( "[OP] Process" )
}
void OpenCVProcess::TakeScreenshot( )
{
struct tm timeinfo;
time_t rawtime;
time( &rawtime );
localtime_s( &timeinfo, &rawtime );
char timebuffer[128];
std::strftime( timebuffer, sizeof( timebuffer ), "%Y%m%d %H%M%S", &timeinfo );
std::string nowstr = timebuffer;
//Make the alpha channel of the RGB maps solid.
int sidepix = m_iFBSideWidth * m_iFBSideHeight;
for ( int i = 0; i < sidepix; i++ )
{
((uint32_t*)origStereoPair.data)[i * 2 + 1] |= 0xff000000;
((uint32_t*)origStereoPair.data)[i * 2 + 0] |= 0xff000000;
((uint32_t*)rectLeft.data)[i] |= 0xff000000;
((uint32_t*)rectRight.data)[i] |= 0xff000000;
}
stbi_write_png( (nowstr + "_Orig_RGB0.png").c_str(), m_iFBSideWidth, m_iFBSideHeight, 4, origLeft.data, m_iFBSideWidth * 8 );
stbi_write_png( (nowstr + "_Orig_RGB1.png").c_str(), m_iFBSideWidth, m_iFBSideHeight, 4, origRight.data, m_iFBSideWidth * 8 );
stbi_write_png( (nowstr + "_RGB0.png").c_str(), m_iFBSideWidth, m_iFBSideHeight, 4, rectLeft.data, m_iFBSideWidth * 4 );
stbi_write_png( (nowstr + "_RGB1.png").c_str(), m_iFBSideWidth, m_iFBSideHeight, 4, rectRight.data, m_iFBSideWidth * 4 );
stbi_write_png( (nowstr + "_Gray0.png").c_str(), m_iFBAlgoWidth, m_iFBAlgoHeight, 1, resizedLeftGray.data, m_iFBAlgoWidth );
stbi_write_png( (nowstr + "_Gray1.png").c_str(), m_iFBAlgoWidth, m_iFBAlgoHeight, 1, resizedRightGray.data, m_iFBAlgoWidth );
int pxl = m_iFBAlgoWidth * m_iFBAlgoHeight;
uint8_t * disp_px = new uint8_t[pxl];
for ( int i = 0; i < pxl; i++ )
{
disp_px[i] = (uint8_t)(((uint16_t*)(mdisparity.data))[i] / 16);
}
stbi_write_png( (nowstr + "_Disp.png").c_str(), m_iFBAlgoWidth, m_iFBAlgoHeight, 1, disp_px, m_iFBAlgoWidth );
delete[] disp_px;
}