#include "linalg.hpp" #ifdef USE_LAPACK typedef int integer; #include #include using namespace cv; bool cvfork::solve(InputArray _src, const InputArray _src2arg, OutputArray _dst, int method ) { bool result = true; Mat src = _src.getMat(), _src2 = _src2arg.getMat(); int type = src.type(); bool is_normal = (method & DECOMP_NORMAL) != 0; CV_Assert( type == _src2.type() && (type == CV_32F || type == CV_64F) ); method &= ~DECOMP_NORMAL; CV_Assert( (method != DECOMP_LU && method != DECOMP_CHOLESKY) || is_normal || src.rows == src.cols ); double rcond=-1, s1=0, work1=0, *work=0, *s=0; float frcond=-1, fs1=0, fwork1=0, *fwork=0, *fs=0; integer m = src.rows, m_ = m, n = src.cols, mn = std::max(m,n), nm = std::min(m, n), nb = _src2.cols, lwork=-1, liwork=0, iwork1=0, lda = m, ldx = mn, info=0, rank=0, *iwork=0; int elem_size = CV_ELEM_SIZE(type); bool copy_rhs=false; int buf_size=0; AutoBuffer buffer; uchar* ptr; char N[] = {'N', '\0'}, L[] = {'L', '\0'}; Mat src2 = _src2; _dst.create( src.cols, src2.cols, src.type() ); Mat dst = _dst.getMat(); if( m <= n ) is_normal = false; else if( is_normal ) m_ = n; buf_size += (is_normal ? n*n : m*n)*elem_size; if( m_ != n || nb > 1 || !dst.isContinuous() ) { copy_rhs = true; if( is_normal ) buf_size += n*nb*elem_size; else buf_size += mn*nb*elem_size; } if( method == DECOMP_SVD || method == DECOMP_EIG ) { integer nlvl = cvRound(std::log(std::max(std::min(m_,n)/25., 1.))/CV_LOG2) + 1; liwork = std::min(m_,n)*(3*std::max(nlvl,(integer)0) + 11); if( type == CV_32F ) sgelsd_(&m_, &n, &nb, (float*)src.data, &lda, (float*)dst.data, &ldx, &fs1, &frcond, &rank, &fwork1, &lwork, &iwork1, &info); else dgelsd_(&m_, &n, &nb, (double*)src.data, &lda, (double*)dst.data, &ldx, &s1, &rcond, &rank, &work1, &lwork, &iwork1, &info ); buf_size += nm*elem_size + (liwork + 1)*sizeof(integer); } else if( method == DECOMP_QR ) { if( type == CV_32F ) sgels_(N, &m_, &n, &nb, (float*)src.data, &lda, (float*)dst.data, &ldx, &fwork1, &lwork, &info ); else dgels_(N, &m_, &n, &nb, (double*)src.data, &lda, (double*)dst.data, &ldx, &work1, &lwork, &info ); } else if( method == DECOMP_LU ) { buf_size += (n+1)*sizeof(integer); } else if( method == DECOMP_CHOLESKY ) ; else CV_Error( Error::StsBadArg, "Unknown method" ); assert(info == 0); lwork = cvRound(type == CV_32F ? (double)fwork1 : work1); buf_size += lwork*elem_size; buffer.allocate(buf_size); ptr = (uchar*)buffer; Mat at(n, m_, type, ptr); ptr += n*m_*elem_size; if( method == DECOMP_CHOLESKY || method == DECOMP_EIG ) src.copyTo(at); else if( !is_normal ) transpose(src, at); else mulTransposed(src, at, true); Mat xt; if( !is_normal ) { if( copy_rhs ) { Mat temp(nb, mn, type, ptr); ptr += nb*mn*elem_size; Mat bt = temp.colRange(0, m); xt = temp.colRange(0, n); transpose(src2, bt); } else { src2.copyTo(dst); xt = Mat(1, n, type, dst.data); } } else { if( copy_rhs ) { xt = Mat(nb, n, type, ptr); ptr += nb*n*elem_size; } else xt = Mat(1, n, type, dst.data); // (a'*b)' = b'*a gemm( src2, src, 1, Mat(), 0, xt, GEMM_1_T ); } lda = (int)(at.step ? at.step/elem_size : at.cols); ldx = (int)(xt.step ? xt.step/elem_size : (!is_normal && copy_rhs ? mn : n)); if( method == DECOMP_SVD || method == DECOMP_EIG ) { if( type == CV_32F ) { fs = (float*)ptr; ptr += nm*elem_size; fwork = (float*)ptr; ptr += lwork*elem_size; iwork = (integer*)alignPtr(ptr, sizeof(integer)); sgelsd_(&m_, &n, &nb, (float*)at.data, &lda, (float*)xt.data, &ldx, fs, &frcond, &rank, fwork, &lwork, iwork, &info); } else { s = (double*)ptr; ptr += nm*elem_size; work = (double*)ptr; ptr += lwork*elem_size; iwork = (integer*)alignPtr(ptr, sizeof(integer)); dgelsd_(&m_, &n, &nb, (double*)at.data, &lda, (double*)xt.data, &ldx, s, &rcond, &rank, work, &lwork, iwork, &info); } } else if( method == DECOMP_QR ) { if( type == CV_32F ) { fwork = (float*)ptr; sgels_(N, &m_, &n, &nb, (float*)at.data, &lda, (float*)xt.data, &ldx, fwork, &lwork, &info); } else { work = (double*)ptr; dgels_(N, &m_, &n, &nb, (double*)at.data, &lda, (double*)xt.data, &ldx, work, &lwork, &info); } } else if( method == DECOMP_CHOLESKY || (method == DECOMP_LU && is_normal) ) { if( type == CV_32F ) { spotrf_(L, &n, (float*)at.data, &lda, &info); if(info==0) spotrs_(L, &n, &nb, (float*)at.data, &lda, (float*)xt.data, &ldx, &info); } else { dpotrf_(L, &n, (double*)at.data, &lda, &info); if(info==0) dpotrs_(L, &n, &nb, (double*)at.data, &lda, (double*)xt.data, &ldx, &info); } } else if( method == DECOMP_LU ) { iwork = (integer*)alignPtr(ptr, sizeof(integer)); if( type == CV_32F ) sgesv_(&n, &nb, (float*)at.data, &lda, iwork, (float*)xt.data, &ldx, &info ); else dgesv_(&n, &nb, (double*)at.data, &lda, iwork, (double*)xt.data, &ldx, &info ); } else assert(0); result = info == 0; if( !result ) dst = Scalar(0); else if( xt.data != dst.data ) transpose( xt, dst ); return result; } static void _SVDcompute( const InputArray _aarr, OutputArray _w, OutputArray _u, OutputArray _vt, int flags = 0) { Mat a = _aarr.getMat(), u, vt; integer m = a.rows, n = a.cols, mn = std::max(m, n), nm = std::min(m, n); int type = a.type(), elem_size = (int)a.elemSize(); bool compute_uv = _u.needed() || _vt.needed(); if( flags & SVD::NO_UV ) { _u.release(); _vt.release(); compute_uv = false; } if( compute_uv ) { _u.create( (int)m, (int)((flags & SVD::FULL_UV) ? m : nm), type ); _vt.create( (int)((flags & SVD::FULL_UV) ? n : nm), n, type ); u = _u.getMat(); vt = _vt.getMat(); } _w.create(nm, 1, type, -1, true); Mat _a = a, w = _w.getMat(); CV_Assert( w.isContinuous() ); int work_ofs=0, iwork_ofs=0, buf_size = 0; bool temp_a = false; double u1=0, v1=0, work1=0; float uf1=0, vf1=0, workf1=0; integer lda, ldu, ldv, lwork=-1, iwork1=0, info=0; char mode[] = {compute_uv ? 'S' : 'N', '\0'}; if( m != n && compute_uv && (flags & SVD::FULL_UV) ) mode[0] = 'A'; if( !(flags & SVD::MODIFY_A) ) { if( mode[0] == 'N' || mode[0] == 'A' ) temp_a = true; else if( compute_uv && (a.size() == vt.size() || a.size() == u.size()) && mode[0] == 'S' ) mode[0] = 'O'; } lda = a.cols; ldv = ldu = mn; if( type == CV_32F ) { sgesdd_(mode, &n, &m, (float*)a.data, &lda, (float*)w.data, &vf1, &ldv, &uf1, &ldu, &workf1, &lwork, &iwork1, &info ); lwork = cvRound(workf1); } else { dgesdd_(mode, &n, &m, (double*)a.data, &lda, (double*)w.data, &v1, &ldv, &u1, &ldu, &work1, &lwork, &iwork1, &info ); lwork = cvRound(work1); } assert(info == 0); if( temp_a ) { buf_size += n*m*elem_size; } work_ofs = buf_size; buf_size += lwork*elem_size; buf_size = alignSize(buf_size, sizeof(integer)); iwork_ofs = buf_size; buf_size += 8*nm*sizeof(integer); AutoBuffer buf(buf_size); uchar* buffer = (uchar*)buf; if( temp_a ) { _a = Mat(a.rows, a.cols, type, buffer ); a.copyTo(_a); } if( !(flags & SVD::MODIFY_A) && !temp_a ) { if( compute_uv && a.size() == vt.size() ) { a.copyTo(vt); _a = vt; } else if( compute_uv && a.size() == u.size() ) { a.copyTo(u); _a = u; } } if( compute_uv ) { ldv = (int)(vt.step ? vt.step/elem_size : vt.cols); ldu = (int)(u.step ? u.step/elem_size : u.cols); } lda = (int)(_a.step ? _a.step/elem_size : _a.cols); if( type == CV_32F ) { sgesdd_(mode, &n, &m, _a.ptr(), &lda, w.ptr(), vt.data ? vt.ptr() : (float*)&v1, &ldv, u.data ? u.ptr() : (float*)&u1, &ldu, (float*)(buffer + work_ofs), &lwork, (integer*)(buffer + iwork_ofs), &info ); } else { dgesdd_(mode, &n, &m, _a.ptr(), &lda, w.ptr(), vt.data ? vt.ptr() : &v1, &ldv, u.data ? u.ptr() : &u1, &ldu, (double*)(buffer + work_ofs), &lwork, (integer*)(buffer + iwork_ofs), &info ); } CV_Assert(info >= 0); if(info != 0) { if( u.data ) u = Scalar(0.); if( vt.data ) vt = Scalar(0.); w = Scalar(0.); } } ////////////////////////////////////////////////////////// template static void MatrAXPY( int m, int n, const T1* x, int dx, const T2* a, int inca, T3* y, int dy ) { int i, j; for( i = 0; i < m; i++, x += dx, y += dy ) { T2 s = a[i*inca]; for( j = 0; j <= n - 4; j += 4 ) { T3 t0 = (T3)(y[j] + s*x[j]); T3 t1 = (T3)(y[j+1] + s*x[j+1]); y[j] = t0; y[j+1] = t1; t0 = (T3)(y[j+2] + s*x[j+2]); t1 = (T3)(y[j+3] + s*x[j+3]); y[j+2] = t0; y[j+3] = t1; } for( ; j < n; j++ ) y[j] = (T3)(y[j] + s*x[j]); } } template static void SVBkSb( int m, int n, const T* w, int incw, const T* u, int ldu, int uT, const T* v, int ldv, int vT, const T* b, int ldb, int nb, T* x, int ldx, double* buffer, T eps ) { double threshold = 0; int udelta0 = uT ? ldu : 1, udelta1 = uT ? 1 : ldu; int vdelta0 = vT ? ldv : 1, vdelta1 = vT ? 1 : ldv; int i, j, nm = std::min(m, n); if( !b ) nb = m; for( i = 0; i < n; i++ ) for( j = 0; j < nb; j++ ) x[i*ldx + j] = 0; for( i = 0; i < nm; i++ ) threshold += w[i*incw]; threshold *= eps; // v * inv(w) * uT * b for( i = 0; i < nm; i++, u += udelta0, v += vdelta0 ) { double wi = w[i*incw]; if( wi <= threshold ) continue; wi = 1/wi; if( nb == 1 ) { double s = 0; if( b ) for( j = 0; j < m; j++ ) s += u[j*udelta1]*b[j*ldb]; else s = u[0]; s *= wi; for( j = 0; j < n; j++ ) x[j*ldx] = (T)(x[j*ldx] + s*v[j*vdelta1]); } else { if( b ) { for( j = 0; j < nb; j++ ) buffer[j] = 0; MatrAXPY( m, nb, b, ldb, u, udelta1, buffer, 0 ); for( j = 0; j < nb; j++ ) buffer[j] *= wi; } else { for( j = 0; j < nb; j++ ) buffer[j] = u[j*udelta1]*wi; } MatrAXPY( n, nb, buffer, 0, v, vdelta1, x, ldx ); } } } static void _backSubst( const InputArray _w, const InputArray _u, const InputArray _vt, const InputArray _rhs, OutputArray _dst ) { Mat w = _w.getMat(), u = _u.getMat(), vt = _vt.getMat(), rhs = _rhs.getMat(); int type = w.type(), esz = (int)w.elemSize(); int m = u.rows, n = vt.cols, nb = rhs.data ? rhs.cols : m; AutoBuffer buffer(nb); CV_Assert( u.data && vt.data && w.data ); CV_Assert( rhs.data == 0 || (rhs.type() == type && rhs.rows == m) ); _dst.create( n, nb, type ); Mat dst = _dst.getMat(); if( type == CV_32F ) SVBkSb(m, n, (float*)w.data, 1, (float*)u.data, (int)(u.step/esz), false, (float*)vt.data, (int)(vt.step/esz), true, (float*)rhs.data, (int)(rhs.step/esz), nb, (float*)dst.data, (int)(dst.step/esz), buffer, 10*FLT_EPSILON ); else if( type == CV_64F ) SVBkSb(m, n, (double*)w.data, 1, (double*)u.data, (int)(u.step/esz), false, (double*)vt.data, (int)(vt.step/esz), true, (double*)rhs.data, (int)(rhs.step/esz), nb, (double*)dst.data, (int)(dst.step/esz), buffer, 2*DBL_EPSILON ); else CV_Error( Error::StsUnsupportedFormat, "" ); } /////////////////////////////////////////// #define Sf( y, x ) ((float*)(srcdata + y*srcstep))[x] #define Sd( y, x ) ((double*)(srcdata + y*srcstep))[x] #define Df( y, x ) ((float*)(dstdata + y*dststep))[x] #define Dd( y, x ) ((double*)(dstdata + y*dststep))[x] double cvfork::invert( InputArray _src, OutputArray _dst, int method ) { Mat src = _src.getMat(); int type = src.type(); CV_Assert(type == CV_32F || type == CV_64F); size_t esz = CV_ELEM_SIZE(type); int m = src.rows, n = src.cols; if( method == DECOMP_SVD ) { int nm = std::min(m, n); AutoBuffer _buf((m*nm + nm + nm*n)*esz + sizeof(double)); uchar* buf = alignPtr((uchar*)_buf, (int)esz); Mat u(m, nm, type, buf); Mat w(nm, 1, type, u.ptr() + m*nm*esz); Mat vt(nm, n, type, w.ptr() + nm*esz); _SVDcompute(src, w, u, vt); _backSubst(w, u, vt, Mat(), _dst); return type == CV_32F ? (w.ptr()[0] >= FLT_EPSILON ? w.ptr()[n-1]/w.ptr()[0] : 0) : (w.ptr()[0] >= DBL_EPSILON ? w.ptr()[n-1]/w.ptr()[0] : 0); } return 0; } #endif //USE_LAPACK