#include "opencv_process.h" #include "hmd_opencv_sandbox.h" #include #include #include #include "common_hello.h" #include #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define NUM_DISP 96 //Max disparity. inline double * CoPTr( const std::initializer_list& 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( 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, ¢er ); 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; }