// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html #include "precomp.hpp" #include "opencv2/core/mat.hpp" #include "opencv2/core/types_c.h" namespace cv { template void convertData_(const void* _from, void* _to, int cn) { const T1* from = (const T1*)_from; T2* to = (T2*)_to; if( cn == 1 ) *to = saturate_cast(*from); else for( int i = 0; i < cn; i++ ) to[i] = saturate_cast(from[i]); } template void convertScaleData_(const void* _from, void* _to, int cn, double alpha, double beta) { const T1* from = (const T1*)_from; T2* to = (T2*)_to; if( cn == 1 ) *to = saturate_cast(*from*alpha + beta); else for( int i = 0; i < cn; i++ ) to[i] = saturate_cast(from[i]*alpha + beta); } typedef void (*ConvertData)(const void* from, void* to, int cn); typedef void (*ConvertScaleData)(const void* from, void* to, int cn, double alpha, double beta); static ConvertData getConvertElem(int fromType, int toType) { static ConvertData tab[][8] = {{ convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, convertData_, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }}; ConvertData func = tab[CV_MAT_DEPTH(fromType)][CV_MAT_DEPTH(toType)]; CV_Assert( func != 0 ); return func; } static ConvertScaleData getConvertScaleElem(int fromType, int toType) { static ConvertScaleData tab[][8] = {{ convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, convertScaleData_, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }}; ConvertScaleData func = tab[CV_MAT_DEPTH(fromType)][CV_MAT_DEPTH(toType)]; CV_Assert( func != 0 ); return func; } enum { HASH_SIZE0 = 8 }; static inline void copyElem(const uchar* from, uchar* to, size_t elemSize) { size_t i; for( i = 0; i + sizeof(int) <= elemSize; i += sizeof(int) ) *(int*)(to + i) = *(const int*)(from + i); for( ; i < elemSize; i++ ) to[i] = from[i]; } static inline bool isZeroElem(const uchar* data, size_t elemSize) { size_t i; for( i = 0; i + sizeof(int) <= elemSize; i += sizeof(int) ) if( *(int*)(data + i) != 0 ) return false; for( ; i < elemSize; i++ ) if( data[i] != 0 ) return false; return true; } SparseMat::Hdr::Hdr( int _dims, const int* _sizes, int _type ) { refcount = 1; dims = _dims; valueOffset = (int)alignSize(sizeof(SparseMat::Node) - MAX_DIM*sizeof(int) + dims*sizeof(int), CV_ELEM_SIZE1(_type)); nodeSize = alignSize(valueOffset + CV_ELEM_SIZE(_type), (int)sizeof(size_t)); int i; for( i = 0; i < dims; i++ ) size[i] = _sizes[i]; for( ; i < CV_MAX_DIM; i++ ) size[i] = 0; clear(); } void SparseMat::Hdr::clear() { hashtab.clear(); hashtab.resize(HASH_SIZE0); pool.clear(); pool.resize(nodeSize); nodeCount = freeList = 0; } ///////////////////////////// SparseMat ///////////////////////////// SparseMat::SparseMat() : flags(MAGIC_VAL), hdr(0) {} SparseMat::SparseMat(int _dims, const int* _sizes, int _type) : flags(MAGIC_VAL), hdr(0) { create(_dims, _sizes, _type); } SparseMat::SparseMat(const SparseMat& m) : flags(m.flags), hdr(m.hdr) { addref(); } SparseMat::~SparseMat() { release(); } SparseMat& SparseMat::operator = (const SparseMat& m) { if( this != &m ) { if( m.hdr ) CV_XADD(&m.hdr->refcount, 1); release(); flags = m.flags; hdr = m.hdr; } return *this; } SparseMat& SparseMat::operator=(const Mat& m) { return (*this = SparseMat(m)); } void SparseMat::assignTo(SparseMat& m, int _type) const { if( _type < 0 ) m = *this; else convertTo(m, _type); } void SparseMat::addref() { if( hdr ) CV_XADD(&hdr->refcount, 1); } void SparseMat::release() { if( hdr && CV_XADD(&hdr->refcount, -1) == 1 ) delete hdr; hdr = 0; } size_t SparseMat::hash(int i0) const { return (size_t)i0; } size_t SparseMat::hash(int i0, int i1) const { return (size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1; } size_t SparseMat::hash(int i0, int i1, int i2) const { return ((size_t)(unsigned)i0 * HASH_SCALE + (unsigned)i1) * HASH_SCALE + (unsigned)i2; } size_t SparseMat::hash(const int* idx) const { size_t h = (unsigned)idx[0]; if( !hdr ) return 0; int d = hdr->dims; for(int i = 1; i < d; i++ ) h = h * HASH_SCALE + (unsigned)idx[i]; return h; } SparseMat::SparseMat(const Mat& m) : flags(MAGIC_VAL), hdr(0) { create( m.dims, m.size, m.type() ); int i, idx[CV_MAX_DIM] = {0}, d = m.dims, lastSize = m.size[d - 1]; size_t esz = m.elemSize(); const uchar* dptr = m.ptr(); for(;;) { for( i = 0; i < lastSize; i++, dptr += esz ) { if( isZeroElem(dptr, esz) ) continue; idx[d-1] = i; uchar* to = newNode(idx, hash(idx)); copyElem( dptr, to, esz ); } for( i = d - 2; i >= 0; i-- ) { dptr += m.step[i] - m.size[i+1]*m.step[i+1]; if( ++idx[i] < m.size[i] ) break; idx[i] = 0; } if( i < 0 ) break; } } void SparseMat::create(int d, const int* _sizes, int _type) { CV_Assert( _sizes && 0 < d && d <= CV_MAX_DIM ); for( int i = 0; i < d; i++ ) CV_Assert( _sizes[i] > 0 ); _type = CV_MAT_TYPE(_type); if( hdr && _type == type() && hdr->dims == d && hdr->refcount == 1 ) { int i; for( i = 0; i < d; i++ ) if( _sizes[i] != hdr->size[i] ) break; if( i == d ) { clear(); return; } } int _sizes_backup[CV_MAX_DIM]; // #5991 if (hdr && _sizes == hdr->size) { for(int i = 0; i < d; i++ ) _sizes_backup[i] = _sizes[i]; _sizes = _sizes_backup; } release(); flags = MAGIC_VAL | _type; hdr = new Hdr(d, _sizes, _type); } void SparseMat::copyTo( SparseMat& m ) const { if( hdr == m.hdr ) return; if( !hdr ) { m.release(); return; } m.create( hdr->dims, hdr->size, type() ); SparseMatConstIterator from = begin(); size_t N = nzcount(), esz = elemSize(); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); uchar* to = m.newNode(n->idx, n->hashval); copyElem( from.ptr, to, esz ); } } void SparseMat::copyTo( Mat& m ) const { CV_Assert( hdr ); int ndims = dims(); m.create( ndims, hdr->size, type() ); m = Scalar(0); SparseMatConstIterator from = begin(); size_t N = nzcount(), esz = elemSize(); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); copyElem( from.ptr, (ndims > 1 ? m.ptr(n->idx) : m.ptr(n->idx[0])), esz); } } void SparseMat::convertTo( SparseMat& m, int rtype, double alpha ) const { int cn = channels(); if( rtype < 0 ) rtype = type(); rtype = CV_MAKETYPE(rtype, cn); if( hdr == m.hdr && rtype != type() ) { SparseMat temp; convertTo(temp, rtype, alpha); m = temp; return; } CV_Assert(hdr != 0); if( hdr != m.hdr ) m.create( hdr->dims, hdr->size, rtype ); SparseMatConstIterator from = begin(); size_t N = nzcount(); if( alpha == 1 ) { ConvertData cvtfunc = getConvertElem(type(), rtype); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); uchar* to = hdr == m.hdr ? from.ptr : m.newNode(n->idx, n->hashval); cvtfunc( from.ptr, to, cn ); } } else { ConvertScaleData cvtfunc = getConvertScaleElem(type(), rtype); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); uchar* to = hdr == m.hdr ? from.ptr : m.newNode(n->idx, n->hashval); cvtfunc( from.ptr, to, cn, alpha, 0 ); } } } void SparseMat::convertTo( Mat& m, int rtype, double alpha, double beta ) const { int cn = channels(); if( rtype < 0 ) rtype = type(); rtype = CV_MAKETYPE(rtype, cn); CV_Assert( hdr ); m.create( dims(), hdr->size, rtype ); m = Scalar(beta); SparseMatConstIterator from = begin(); size_t N = nzcount(); if( alpha == 1 && beta == 0 ) { ConvertData cvtfunc = getConvertElem(type(), rtype); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); uchar* to = m.ptr(n->idx); cvtfunc( from.ptr, to, cn ); } } else { ConvertScaleData cvtfunc = getConvertScaleElem(type(), rtype); for( size_t i = 0; i < N; i++, ++from ) { const Node* n = from.node(); uchar* to = m.ptr(n->idx); cvtfunc( from.ptr, to, cn, alpha, beta ); } } } void SparseMat::clear() { if( hdr ) hdr->clear(); } uchar* SparseMat::ptr(int i0, bool createMissing, size_t* hashval) { CV_Assert( hdr && hdr->dims == 1 ); size_t h = hashval ? *hashval : hash(i0); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx]; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h && elem->idx[0] == i0 ) return &value(elem); nidx = elem->next; } if( createMissing ) { int idx[] = { i0 }; return newNode( idx, h ); } return NULL; } uchar* SparseMat::ptr(int i0, int i1, bool createMissing, size_t* hashval) { CV_Assert( hdr && hdr->dims == 2 ); size_t h = hashval ? *hashval : hash(i0, i1); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx]; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h && elem->idx[0] == i0 && elem->idx[1] == i1 ) return &value(elem); nidx = elem->next; } if( createMissing ) { int idx[] = { i0, i1 }; return newNode( idx, h ); } return NULL; } uchar* SparseMat::ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval) { CV_Assert( hdr && hdr->dims == 3 ); size_t h = hashval ? *hashval : hash(i0, i1, i2); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx]; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h && elem->idx[0] == i0 && elem->idx[1] == i1 && elem->idx[2] == i2 ) return &value(elem); nidx = elem->next; } if( createMissing ) { int idx[] = { i0, i1, i2 }; return newNode( idx, h ); } return NULL; } uchar* SparseMat::ptr(const int* idx, bool createMissing, size_t* hashval) { CV_Assert( hdr ); int i, d = hdr->dims; size_t h = hashval ? *hashval : hash(idx); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx]; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h ) { for( i = 0; i < d; i++ ) if( elem->idx[i] != idx[i] ) break; if( i == d ) return &value(elem); } nidx = elem->next; } return createMissing ? newNode(idx, h) : NULL; } void SparseMat::erase(int i0, int i1, size_t* hashval) { CV_Assert( hdr && hdr->dims == 2 ); size_t h = hashval ? *hashval : hash(i0, i1); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx], previdx=0; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h && elem->idx[0] == i0 && elem->idx[1] == i1 ) break; previdx = nidx; nidx = elem->next; } if( nidx ) removeNode(hidx, nidx, previdx); } void SparseMat::erase(int i0, int i1, int i2, size_t* hashval) { CV_Assert( hdr && hdr->dims == 3 ); size_t h = hashval ? *hashval : hash(i0, i1, i2); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx], previdx=0; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h && elem->idx[0] == i0 && elem->idx[1] == i1 && elem->idx[2] == i2 ) break; previdx = nidx; nidx = elem->next; } if( nidx ) removeNode(hidx, nidx, previdx); } void SparseMat::erase(const int* idx, size_t* hashval) { CV_Assert( hdr ); int i, d = hdr->dims; size_t h = hashval ? *hashval : hash(idx); size_t hidx = h & (hdr->hashtab.size() - 1), nidx = hdr->hashtab[hidx], previdx=0; uchar* pool = &hdr->pool[0]; while( nidx != 0 ) { Node* elem = (Node*)(pool + nidx); if( elem->hashval == h ) { for( i = 0; i < d; i++ ) if( elem->idx[i] != idx[i] ) break; if( i == d ) break; } previdx = nidx; nidx = elem->next; } if( nidx ) removeNode(hidx, nidx, previdx); } void SparseMat::resizeHashTab(size_t newsize) { newsize = std::max(newsize, (size_t)8); if((newsize & (newsize-1)) != 0) newsize = (size_t)1 << cvCeil(std::log((double)newsize)/CV_LOG2); size_t hsize = hdr->hashtab.size(); std::vector _newh(newsize); size_t* newh = &_newh[0]; for( size_t i = 0; i < newsize; i++ ) newh[i] = 0; uchar* pool = &hdr->pool[0]; for( size_t i = 0; i < hsize; i++ ) { size_t nidx = hdr->hashtab[i]; while( nidx ) { Node* elem = (Node*)(pool + nidx); size_t next = elem->next; size_t newhidx = elem->hashval & (newsize - 1); elem->next = newh[newhidx]; newh[newhidx] = nidx; nidx = next; } } hdr->hashtab = _newh; } uchar* SparseMat::newNode(const int* idx, size_t hashval) { const int HASH_MAX_FILL_FACTOR=3; assert(hdr); size_t hsize = hdr->hashtab.size(); if( ++hdr->nodeCount > hsize*HASH_MAX_FILL_FACTOR ) { resizeHashTab(std::max(hsize*2, (size_t)8)); hsize = hdr->hashtab.size(); } if( !hdr->freeList ) { size_t i, nsz = hdr->nodeSize, psize = hdr->pool.size(), newpsize = std::max(psize*3/2, 8*nsz); newpsize = (newpsize/nsz)*nsz; hdr->pool.resize(newpsize); uchar* pool = &hdr->pool[0]; hdr->freeList = std::max(psize, nsz); for( i = hdr->freeList; i < newpsize - nsz; i += nsz ) ((Node*)(pool + i))->next = i + nsz; ((Node*)(pool + i))->next = 0; } size_t nidx = hdr->freeList; Node* elem = (Node*)&hdr->pool[nidx]; hdr->freeList = elem->next; elem->hashval = hashval; size_t hidx = hashval & (hsize - 1); elem->next = hdr->hashtab[hidx]; hdr->hashtab[hidx] = nidx; int i, d = hdr->dims; for( i = 0; i < d; i++ ) elem->idx[i] = idx[i]; size_t esz = elemSize(); uchar* p = &value(elem); if( esz == sizeof(float) ) *((float*)p) = 0.f; else if( esz == sizeof(double) ) *((double*)p) = 0.; else memset(p, 0, esz); return p; } void SparseMat::removeNode(size_t hidx, size_t nidx, size_t previdx) { Node* n = node(nidx); if( previdx ) { Node* prev = node(previdx); prev->next = n->next; } else hdr->hashtab[hidx] = n->next; n->next = hdr->freeList; hdr->freeList = nidx; --hdr->nodeCount; } // // Operations // double norm( const SparseMat& src, int normType ) { CV_INSTRUMENT_REGION(); SparseMatConstIterator it = src.begin(); size_t i, N = src.nzcount(); normType &= NORM_TYPE_MASK; int type = src.type(); double result = 0; CV_Assert( normType == NORM_INF || normType == NORM_L1 || normType == NORM_L2 ); if( type == CV_32F ) { if( normType == NORM_INF ) for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); result = std::max(result, std::abs((double)it.value())); } else if( normType == NORM_L1 ) for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); result += std::abs(it.value()); } else for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); double v = it.value(); result += v*v; } } else if( type == CV_64F ) { if( normType == NORM_INF ) for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); result = std::max(result, std::abs(it.value())); } else if( normType == NORM_L1 ) for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); result += std::abs(it.value()); } else for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); double v = it.value(); result += v*v; } } else CV_Error( CV_StsUnsupportedFormat, "Only 32f and 64f are supported" ); if( normType == NORM_L2 ) result = std::sqrt(result); return result; } void minMaxLoc( const SparseMat& src, double* _minval, double* _maxval, int* _minidx, int* _maxidx ) { CV_INSTRUMENT_REGION(); SparseMatConstIterator it = src.begin(); size_t i, N = src.nzcount(), d = src.hdr ? src.hdr->dims : 0; int type = src.type(); const int *minidx = 0, *maxidx = 0; if( type == CV_32F ) { float minval = FLT_MAX, maxval = -FLT_MAX; for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); float v = it.value(); if( v < minval ) { minval = v; minidx = it.node()->idx; } if( v > maxval ) { maxval = v; maxidx = it.node()->idx; } } if( _minval ) *_minval = minval; if( _maxval ) *_maxval = maxval; } else if( type == CV_64F ) { double minval = DBL_MAX, maxval = -DBL_MAX; for( i = 0; i < N; i++, ++it ) { CV_Assert(it.ptr); double v = it.value(); if( v < minval ) { minval = v; minidx = it.node()->idx; } if( v > maxval ) { maxval = v; maxidx = it.node()->idx; } } if( _minval ) *_minval = minval; if( _maxval ) *_maxval = maxval; } else CV_Error( CV_StsUnsupportedFormat, "Only 32f and 64f are supported" ); if( _minidx && minidx ) for( i = 0; i < d; i++ ) _minidx[i] = minidx[i]; if( _maxidx && maxidx ) for( i = 0; i < d; i++ ) _maxidx[i] = maxidx[i]; } void normalize( const SparseMat& src, SparseMat& dst, double a, int norm_type ) { CV_INSTRUMENT_REGION(); double scale = 1; if( norm_type == CV_L2 || norm_type == CV_L1 || norm_type == CV_C ) { scale = norm( src, norm_type ); scale = scale > DBL_EPSILON ? a/scale : 0.; } else CV_Error( CV_StsBadArg, "Unknown/unsupported norm type" ); src.convertTo( dst, -1, scale ); } } // cv:: // // C-API glue // CvSparseMat* cvCreateSparseMat(const cv::SparseMat& sm) { if( !sm.hdr || sm.hdr->dims > (int)cv::SparseMat::MAX_DIM) return 0; CvSparseMat* m = cvCreateSparseMat(sm.hdr->dims, sm.hdr->size, sm.type()); cv::SparseMatConstIterator from = sm.begin(); size_t i, N = sm.nzcount(), esz = sm.elemSize(); for( i = 0; i < N; i++, ++from ) { const cv::SparseMat::Node* n = from.node(); uchar* to = cvPtrND(m, n->idx, 0, -2, 0); cv::copyElem(from.ptr, to, esz); } return m; } void CvSparseMat::copyToSparseMat(cv::SparseMat& m) const { m.create( dims, &size[0], type ); CvSparseMatIterator it; CvSparseNode* n = cvInitSparseMatIterator(this, &it); size_t esz = m.elemSize(); for( ; n != 0; n = cvGetNextSparseNode(&it) ) { const int* idx = CV_NODE_IDX(this, n); uchar* to = m.newNode(idx, m.hash(idx)); cv::copyElem((const uchar*)CV_NODE_VAL(this, n), to, esz); } }