I plan to post about my research in computer vision, photography and birding.
Using Converter class to use cv::undistort in Matlab
OpenCV function
void undistort(const Mat& src, Mat& dst, const Mat& cameraMatrix, const Mat& distCoeffs, const Mat& newCameraMatrix=Mat())
Matlab
dst=mexUndistort(src,cameraMatrix,distCoeffs)
MEX function source
#include "mex.h" #include "matcv.h" void mexFunction(int nlhs, mxArray *plhs[],int nrhs, mxArray *prhs[]) { if(nrhs!=3) { mexErrMsgTxt("Need exactly 3 inputs src, cameraMatrix, and distCoeffs.n"); } cv::Mat Inputs[3]; cv::Mat Output; for(int i=0;i<nrhs;i++) { Inputs[i]=Converter(prhs[i]); } cv::undistort(Inputs[0],Output,Inputs[1],Inputs[2]); plhs[0]=Converter(Output); return; }
To compile you need opencv and boost. Mex command I used with Matlab R2011a and VC9 goes like this:
mex mexUndistort.cpp matcv.cpp -IC:OpenCV2.3buildinclude -ID:NinadToolsboost_1_47_0 -LC:OpenCV2.3buildx64vc9lib -lopencv_core230 -lopencv_imgproc230 -v COMPFLAGS=”/c /Zp8 /GR /W3 /EHs /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE /DMATLAB_MEX_FILE /nologo /MD”
Using OpenCV functions in Matlab
I wrote Converter class few days back to simplify creating MEX files for OpenCV functions. Example of using the class follows in the next post.
Source for matcv.h
#ifndef __MATCV__H__ #define __MATCV__H__ #include "mex.h" #include "matrix.h" #include <cstring> #include "opencvcv.h" #include "opencvhighgui.h" #include "boostbimap.hpp" class Converter{ public: Converter(mxArray* src, bool Interleve=true); Converter(cv::Mat& src); operator cv::Mat(); operator mxArray*(); private: enum{MATLAB_MXARRAY,OPENCV_MAT} _id; bool _interleve; cv::Mat opencv; mxArray* matlab; typedef boost::bimap<mxClassID,unsigned char> bmtype; bmtype _idmap; void _initidmap(); void _matlab2opencv(); void _opencv2matlab(); }; #endif //__MATCV__H__
Source for matcv.cpp
#include "matcv.h" Converter::Converter(mxArray* src,bool Interleve):matlab(src),_id(MATLAB_MXARRAY),_interleve(Interleve){ _initidmap(); } Converter::Converter(cv::Mat& src):opencv(src),_id(OPENCV_MAT){ _initidmap(); } Converter::operator cv::Mat() { if(_id==OPENCV_MAT) return(opencv); _matlab2opencv(); return(opencv); } Converter::operator mxArray*() { if(_id==MATLAB_MXARRAY) return(matlab); _opencv2matlab(); return(matlab); } void Converter::_initidmap() { _idmap.insert(bmtype::value_type(mxINT8_CLASS,CV_8S)); _idmap.insert(bmtype::value_type(mxUINT8_CLASS,CV_8U)); _idmap.insert(bmtype::value_type(mxINT16_CLASS,CV_16S)); _idmap.insert(bmtype::value_type(mxUINT16_CLASS,CV_16U)); _idmap.insert(bmtype::value_type(mxINT32_CLASS,CV_32S)); _idmap.insert(bmtype::value_type(mxSINGLE_CLASS,CV_32F)); _idmap.insert(bmtype::value_type(mxDOUBLE_CLASS,CV_64F)); } void Converter::_opencv2matlab() { //Is the data type supported? bmtype::right_map::const_iterator itr=_idmap.right.find(opencv.depth()); //if not then if(itr==_idmap.right.end()) { mexErrMsgTxt("OpenCV2Matlab:Unsupported data type."); } //Find the matlab data type mxClassID type=itr->second; //We support max 3 dimensions mwSignedIndex dims[3]; dims[0]=opencv.rows; dims[1]=opencv.cols; dims[2]=opencv.channels(); //if number of channels is 1, its a 2D array if(dims[0]>0 && dims[1]>0 && dims[2]==1) { //Create the array to be returned matlab=mxCreateNumericArray(2,dims,type,mxREAL); //Create opencv header for the matlab data cv::Mat tmp=cv::Mat(dims[1],dims[0],CV_MAKETYPE(opencv.depth(),1),mxGetData(matlab),0); //Transpose the opencv data to get row major data for matlab tmp=opencv.t(); const mwSize* size=mxGetDimensions(matlab); mxAssert((opencv.rows==size[0])&(opencv.cols==size[1]),"OpenCV2Matlab:Conversion mismatch"); } else { //Create the array to be returned matlab=mxCreateNumericArray(3,dims,type,mxREAL); //Seperate the channels std::vector<cv::Mat> chans(dims[2]); //cv::split(opencv,&chans[0]); cv::split(opencv,chans); //Create opencv header as a "flat" image for the matlab data cv::Mat tmp=cv::Mat(dims[1]*dims[2],dims[0],CV_MAKETYPE(opencv.depth(),1),mxGetData(matlab),0); for(int i=0;i<dims[2];i++) { //transpose the opencv channels image to row major matlab data cv::Mat tmp2=chans[i].t(); //Copy the data to the flat matlab data tmp2.copyTo(tmp.rowRange(i*dims[1],(i+1)*dims[1])); } const mwSize* size=mxGetDimensions(matlab); mxAssert((opencv.rows==size[0])&(opencv.cols==size[1])&(opencv.channels()==size[2]),"OpenCV2Matlab:Conversion mismatch"); } } void Converter::_matlab2opencv() { //find the corresponding corresponding opencv data type bmtype::left_map::const_iterator itr=_idmap.left.find(mxGetClassID(matlab)); //No corresponding type? if(itr==_idmap.left.end()| mxIsComplex(matlab) | mxIsSparse(matlab)) { std::string msg="Matlab2OpenCV:Unsupported data type:"+(itr==_idmap.left.end()?std::string(mxGetClassName(matlab)):"") +(mxIsComplex(matlab)?" Complex":"")+(mxIsSparse(matlab)?" Sparse":"."); mexErrMsgTxt(msg.c_str()); } unsigned char type=itr->second; //Get number of dimensions const mwSize dims=mxGetNumberOfDimensions(matlab); //Cannot handle more that 3 dimensions if(dims>3) { std::ostringstream o; o<<"Matlab2OpenCV:Supports upto 3 dimensions. You supplied "<<dims<<"."; mexErrMsgTxt(o.str().c_str()); } //Get actual dimensions const mwSize* size=mxGetDimensions(matlab); //Check if 2 or 3 dimentions if(dims==2) { //Create header for row major matlab data const cv::Mat donotmodify=cv::Mat(size[1],size[0],CV_MAKETYPE(type,1),mxGetData(matlab),0); //Transpose the data so that it is column major for opencv. opencv=donotmodify.t(); mxAssert((opencv.rows==size[0])&(opencv.cols==size[1]),"Matlab2OpenCV:Conversion mismatch"); } else { //Create header for the "flat" matlab data const cv::Mat donotmodify=cv::Mat(size[1]*size[2],size[0],CV_MAKETYPE(type,1),mxGetData(matlab),0); //Transpose the flat data cv::Mat flat=donotmodify.t(); //Create vector of channels to pass to opencv merge operataion std::vector<cv::Mat> chans(size[2]); for(int i=0;i<size[2];i++) { chans[i]=flat.colRange(i*size[1],(i+1)*size[1]); } cv::merge(chans,opencv); mxAssert((opencv.rows==size[0])&(opencv.cols==size[1])&(opencv.channels()==size[2]),"Matlab2OpenCV:Conversion mismatch"); } }
Writing 3D range grid data to PLY file
I have been working on fixing 3D scanner capable of scanning entire human body. I needed to export the scanner data to Scanalyze to figure out what was wrong with the scanner. I could not find something easy and quick which I could use to export the range grid data to the PLY file format that Scanalyze understands. Final result of few hours of web search, reverse engineering and trial and error follows:
function cloud2ply(X,Y,Z,outfile) % Writes range grid data to a PLY file % X,Y,Z: 2D matrix describing x,y and z depths %outfile: path/name of the output ply flie %Ignores all zeros M=(X(:)==0)&(Y(:)==0)&(Z(:)==0); fid=fopen(outfile,'w'); fprintf(fid,'plyn'); fprintf(fid,'format ascii 1.0n'); fprintf(fid,'obj_info num_cols %dn',size(X,1)); fprintf(fid,'obj_info num_rows %dn',size(X,2)); fprintf(fid,'element vertex %dn',sum(~M)); fprintf(fid,'property float xn'); fprintf(fid,'property float yn'); fprintf(fid,'property float zn'); fprintf(fid,'element range_grid %dn',numel(X)); fprintf(fid,'property list uchar int vertex_indicesn'); fprintf(fid,'end_headern'); fprintf(fid,'%f %f %fn',[X(~M) Y(~M) Z(~M)]'); fprintf(fid,'%d %dn',[~M (~M).*cumsum(~M)]'); fclose(fid);
Running Japanese application on windows 7
I have English(US) as my locale on my computer. I have this program which fails to run if locale is anything but Japanese. So only solution I thought I had was switching locale of the system. But that is inconvenient. After a little search, I found Applocale utility.
http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=13209
But it wont install on Windows 7 as is. You need to start the installation from elevated command prompt.
http://www.emreakkas.com/windows-tips/how-to-install-applocale-on-windows-7
Super-Resolution Tools
Runtime enum-to-type mapping
Both Matlab array mxArray and OpenCV’s Mat can hold data any basic type. I want to write code which essentially can handle any data type. My current solution is to write template based code for data processing. I call this code with a switch/case statement. I wanted to avoid rewriting this switch/case for each function that I write. I was hoping that there was a better way of doing this. My friend posted this for me on stackoverflow.
mxClassID category = mxGetClassID(prhs[0]); switch (category) { case mxINT8_CLASS: computecost<signed char>(T,offT,Offset,CostMatrix); break; case mxUINT8_CLASS: computecost<unsigned char>(T,offT,Offset,CostMatrix); break; case mxINT16_CLASS: computecost<signed short>(T,offT,Offset,CostMatrix); break; case mxUINT16_CLASS: computecost<unsigned short>(T,offT,Offset,CostMatrix); break; case mxINT32_CLASS: computecost<signed int>(T,offT,Offset,CostMatrix); break; case mxSINGLE_CLASS: computecost<float>(T,offT,Offset,CostMatrix); break; case mxDOUBLE_CLASS: computecost<double>(T,offT,Offset,CostMatrix); break; default: }
http://stackoverflow.com/q/7356740
The details I missed on the stackoverflow question were I plan to call the this function template only at once. However, I have multiple functions which will need this switch case structure. This makes the function table based methods useless and I don’t really want to use macros. So, it seems like there is now way of getting rid of the switch/case.
Using cv::Mat at()
How does one access (i,j) th location and k th channels of a 2D cv::Mat. Here k is not constant at runtime. I was thinking cv::Mat at<T>(i,j,k) will do it. But it does not. So how do I do this?
double x=Data.at<double>(i,j*Data.channels()+k);
Fixing the problem
- Check what Visual studio Matlab was compiled with. Browsed to C:Program FilesMATLABR2011abinwin64, I see boost dlls with vc90 in their names, so I am guessing Visual studio 2008 AKA VC9 was used. Just to be absolutely sure, I used Dependency Walker and opened libmex.dll in it. It shows VC9 runtimes (msvc*90.dll) in dependency as well.
- Get OpenCV compiled with same Visual Studio Version. I already have this located at C:OpenCV2.3buildx64vc9.
- Configured Matlab mex compiler to use VC9. Done.
- Compile test.cpp with mex and run it.
- Debug mode first: mex -g test.cpp -IC:OpenCV2.3buildinclude -lopencv_core230d -LC:OpenCV2.3buildx64vc9lib
- The compiling/linking is is ok.
- Tried running the “test”.
-
??? Unexpected Standard exception from MEX file. What() is:c:Usersvpworkocvopencvmodulescoresrcconvert.cpp:265: error: (-215) mv && n > 0
- Debug mode with SECURE_SCL=1: Leads to crash of Matlab!
- Release mode: mex test.cpp -IC:OpenCV2.3buildinclude -lopencv_core230 -LC:OpenCV2.3buildx64vc9lib
- Leads to crash of Matlab!
- Release mode with SECURE_SCL=1: mex test.cpp -IC:OpenCV2.3buildinclude -lopencv_core230 -LC:OpenCV2.3buildx64vc9lib -v COMPFLAGS=”/c /Zp8 /GR /W3 /EHs /D_CRT_SECURE_NO_DEPRECATE /D_SCL_SECURE_NO_DEPRECATE /DMATLAB_MEX_FILE /nologo /MD”
- No crash. Now to more thorough testing!
The problem!
I tried compiling following piece of code with “mex”.
Matlab version: R2011a
C++ Compiler: Visual Studio 2010
//test.cpp #include "mex.h" #include <vector> #include <opencvcv.h> void mexFunction(int nlhs, mxArray *plhs[],int nrhs, mxArray *prhs[]) { int size[]={100,100,3}; const std::vector<cv::Mat> chans(size[2],cv::Mat(size[0],size[1],CV_64F)); cv::Mat opencv; cv::merge(chans,opencv); return ; }
This compiles fine. But crashes Matlab, if you run “test” in Matlab. In debug mode (“mex” with -g), leads to an assertions failure !