1782 lines
55 KiB
1782 lines
55 KiB
/********************************************************************* |
|
* Software License Agreement (BSD License) |
|
* |
|
* Copyright (c) 2014, 2015 |
|
* Zhengqin Li <li-zq12 at mails dot tsinghua dot edu dot cn> |
|
* Jiansheng Chen <jschenthu at mail dot tsinghua dot edu dot cn> |
|
* Tsinghua University |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions |
|
* are met: |
|
* |
|
* * Redistributions of source code must retain the above copyright |
|
* notice, this list of conditions and the following disclaimer. |
|
* * Redistributions in binary form must reproduce the above |
|
* copyright notice, this list of conditions and the following |
|
* disclaimer in the documentation and/or other materials provided |
|
* with the distribution. |
|
* * Neither the name of the copyright holders nor the names of its |
|
* contributors may be used to endorse or promote products derived |
|
* from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
|
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
* POSSIBILITY OF SUCH DAMAGE. |
|
*********************************************************************/ |
|
|
|
/* |
|
|
|
"Superpixel Segmentation using Linear Spectral Clustering" |
|
Zhengqin Li, Jiansheng Chen, IEEE Conference on Computer Vision and Pattern |
|
Recognition (CVPR), Jun. 2015 |
|
|
|
OpenCV port by: Cristian Balint <cristian dot balint at gmail dot com> |
|
*/ |
|
|
|
#include <map> |
|
#include <queue> |
|
#include "precomp.hpp" |
|
|
|
using namespace std; |
|
|
|
|
|
namespace cv { |
|
namespace ximgproc { |
|
|
|
class SuperpixelLSCImpl : public SuperpixelLSC |
|
{ |
|
public: |
|
|
|
SuperpixelLSCImpl( InputArray image, int region_size, float ratio ); |
|
|
|
virtual ~SuperpixelLSCImpl(); |
|
|
|
// perform amount of iteration |
|
virtual void iterate( int num_iterations = 10 ); |
|
|
|
// get amount of superpixels |
|
virtual int getNumberOfSuperpixels() const; |
|
|
|
// get image with labels |
|
virtual void getLabels( OutputArray labels_out ) const; |
|
|
|
// get mask image with contour |
|
virtual void getLabelContourMask( OutputArray image, bool thick_line = true ) const; |
|
|
|
// enforce connectivity over labels |
|
virtual void enforceLabelConnectivity( int min_element_size ); |
|
|
|
|
|
protected: |
|
|
|
// image width |
|
int m_width; |
|
|
|
// image width |
|
int m_height; |
|
|
|
// seeds stepx |
|
int m_stepx; |
|
|
|
// seeds stepy |
|
int m_stepy; |
|
|
|
// image channels |
|
int m_nr_channels; |
|
|
|
// region size |
|
int m_region_size; |
|
|
|
// ratio |
|
float m_ratio; |
|
|
|
private: |
|
|
|
// labels no |
|
int m_numlabels; |
|
|
|
// color coefficient |
|
float m_color_coeff; |
|
|
|
// dist coefficient |
|
float m_dist_coeff; |
|
|
|
// threshold coeff |
|
int m_threshold_coeff; |
|
|
|
// max value from |
|
// image channels |
|
float m_chvec_max; |
|
|
|
// stacked channels |
|
// of original image |
|
vector<Mat> m_chvec; |
|
|
|
// seeds on x |
|
vector<float> m_kseedsx; |
|
|
|
// seeds on y |
|
vector<float> m_kseedsy; |
|
|
|
// W |
|
Mat m_W; |
|
|
|
// labels storage |
|
Mat m_klabels; |
|
|
|
// initialization |
|
inline void initialize(); |
|
|
|
// fetch seeds |
|
inline void GetChSeeds(); |
|
|
|
// precompute vector space |
|
inline void GetFeatureSpace(); |
|
|
|
// LSC |
|
inline void PerformLSC( const int& num_iterations ); |
|
|
|
// pre-enforce connectivity over labels |
|
inline void PreEnforceLabelConnectivity( int min_element_size ); |
|
|
|
// enforce connectivity over labels |
|
inline void PostEnforceLabelConnectivity( int threshold ); |
|
|
|
// re-count superpixles |
|
inline void countSuperpixels(); |
|
|
|
}; |
|
|
|
class Superpixel |
|
{ |
|
public: |
|
|
|
int Label, Size; |
|
vector<int> Neighbor, xLoc, yLoc; |
|
|
|
Superpixel( int L = 0, int S = 0 ) : Label(L), Size(S) { } |
|
|
|
friend bool operator == ( Superpixel& S, int L ) |
|
{ |
|
return S.Label == L; |
|
} |
|
friend bool operator == ( int L, Superpixel& S ) |
|
{ |
|
return S.Label == L; |
|
} |
|
}; |
|
|
|
CV_EXPORTS Ptr<SuperpixelLSC> createSuperpixelLSC( InputArray image, int region_size, float ratio ) |
|
{ |
|
return makePtr<SuperpixelLSCImpl>( image, region_size, ratio ); |
|
} |
|
|
|
SuperpixelLSCImpl::SuperpixelLSCImpl( InputArray _image, int _region_size, float _ratio ) |
|
: m_region_size(_region_size), m_ratio(_ratio) |
|
{ |
|
if ( _image.isMat() ) |
|
{ |
|
Mat image = _image.getMat(); |
|
|
|
// image should be valid |
|
CV_Assert( !image.empty() ); |
|
|
|
// initialize sizes |
|
m_width = image.size().width; |
|
m_height = image.size().height; |
|
m_nr_channels = image.channels(); |
|
|
|
// intialize channels |
|
split( image, m_chvec ); |
|
} |
|
else if ( _image.isMatVector() ) |
|
{ |
|
_image.getMatVector( m_chvec ); |
|
|
|
// array should be valid |
|
CV_Assert( !m_chvec.empty() ); |
|
|
|
// initialize sizes |
|
m_width = m_chvec[0].size().width; |
|
m_height = m_chvec[0].size().height; |
|
m_nr_channels = (int) m_chvec.size(); |
|
} |
|
else |
|
CV_Error( Error::StsInternal, "Invalid InputArray." ); |
|
|
|
// init |
|
initialize(); |
|
|
|
// feature space |
|
GetFeatureSpace(); |
|
} |
|
|
|
SuperpixelLSCImpl::~SuperpixelLSCImpl() |
|
{ |
|
} |
|
|
|
int SuperpixelLSCImpl::getNumberOfSuperpixels() const |
|
{ |
|
return m_numlabels; |
|
} |
|
|
|
void SuperpixelLSCImpl::initialize() |
|
{ |
|
// basic coeffs |
|
m_color_coeff = 20.0f; |
|
m_threshold_coeff = 4; |
|
m_dist_coeff = m_color_coeff * m_ratio; |
|
|
|
// total amount of superpixels given region size |
|
m_numlabels = int(float(m_width * m_height) |
|
/ float(m_region_size * m_region_size)); |
|
|
|
// max intensity |
|
m_chvec_max = 0.0f; |
|
for( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
double chmin, chmax; |
|
minMaxIdx( m_chvec[b], &chmin, &chmax ); |
|
if ( m_chvec_max < chmax ) m_chvec_max = (float) chmax; |
|
} |
|
|
|
// intitialize label storage |
|
m_klabels = Mat( m_height, m_width, CV_32S, Scalar::all(0) ); |
|
|
|
// init seeds |
|
GetChSeeds(); |
|
} |
|
|
|
void SuperpixelLSCImpl::iterate( int num_iterations ) |
|
{ |
|
PerformLSC( num_iterations ); |
|
} |
|
|
|
void SuperpixelLSCImpl::getLabels(OutputArray labels_out) const |
|
{ |
|
labels_out.assign( m_klabels ); |
|
} |
|
|
|
void SuperpixelLSCImpl::getLabelContourMask(OutputArray _mask, bool _thick_line) const |
|
{ |
|
// default width |
|
int line_width = 2; |
|
|
|
if ( !_thick_line ) line_width = 1; |
|
|
|
_mask.create( m_height, m_width, CV_8SC1 ); |
|
Mat mask = _mask.getMat(); |
|
|
|
mask.setTo(1); |
|
|
|
const int dx8[8] = { -1, -1, 0, 1, 1, 1, 0, -1 }; |
|
const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1 }; |
|
|
|
int sz = m_width*m_height; |
|
|
|
vector<bool> istaken(sz, false); |
|
|
|
int mainindex = 0; |
|
for( int j = 0; j < m_height; j++ ) |
|
{ |
|
for( int k = 0; k < m_width; k++ ) |
|
{ |
|
int np = 0; |
|
for( int i = 0; i < 8; i++ ) |
|
{ |
|
int x = k + dx8[i]; |
|
int y = j + dy8[i]; |
|
|
|
if( (x >= 0 && x < m_width) && (y >= 0 && y < m_height) ) |
|
{ |
|
int index = y*m_width + x; |
|
|
|
if( false == istaken[index] ) |
|
{ |
|
if( m_klabels.at<int>(j,k) != m_klabels.at<int>(y,x) ) np++; |
|
} |
|
} |
|
} |
|
if( np > line_width ) |
|
{ |
|
mask.at<char>(j,k) = -1; |
|
istaken[mainindex] = true; |
|
} |
|
mainindex++; |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* enforceLabelConnectivity |
|
* |
|
* 1. finding an adjacent label for each new component at the start |
|
* 2. if a certain component is too small, assigning the previously found |
|
* adjacent label to this component, and not incrementing the label. |
|
* |
|
*/ |
|
void SuperpixelLSCImpl::enforceLabelConnectivity( int min_element_size ) |
|
{ |
|
int threshold = (m_width * m_height) |
|
/ (m_numlabels * m_threshold_coeff); |
|
|
|
PreEnforceLabelConnectivity( min_element_size ); |
|
PostEnforceLabelConnectivity( threshold ); |
|
countSuperpixels(); |
|
} |
|
|
|
/* |
|
* countSuperpixels() |
|
* |
|
* 1. count unique superpixels |
|
* 2. relabel all superpixels |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::countSuperpixels() |
|
{ |
|
std::map<int,int> labels; |
|
|
|
int labelNum = 0; |
|
int prev_label = -1; |
|
int mark_label = 0; |
|
for( int x = 0; x < m_width; x++ ) |
|
{ |
|
for( int y = 0; y < m_height; y++ ) |
|
{ |
|
int curr_label = m_klabels.at<int>(y,x); |
|
|
|
// relax, just do relabel |
|
if ( curr_label == prev_label ) |
|
{ |
|
m_klabels.at<int>(y,x) = mark_label; |
|
continue; |
|
} |
|
|
|
// on label change do map lookup |
|
map<int,int>::iterator it = labels.find( curr_label ); |
|
|
|
// if new label seen |
|
if ( it == labels.end() ) |
|
{ |
|
mark_label = labelNum; labelNum++; |
|
labels.insert( pair<int,int>( curr_label, mark_label ) ); |
|
m_klabels.at<int>(y,x) = mark_label; |
|
} else |
|
{ |
|
mark_label = it->second; |
|
m_klabels.at<int>(y,x) = mark_label; |
|
} |
|
prev_label = curr_label; |
|
} |
|
} |
|
m_numlabels = (int) labels.size(); |
|
} |
|
|
|
/* |
|
* PreEnforceLabelConnectivity |
|
* |
|
* 1. finding an adjacent label for each new component at the start |
|
* 2. if a certain component is too small, assigning the previously found |
|
* adjacent label to this component, and not incrementing the label. |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::PreEnforceLabelConnectivity( int min_element_size ) |
|
{ |
|
const int dx8[8] = { -1, -1, 0, 1, 1, 1, 0, -1 }; |
|
const int dy8[8] = { 0, -1, -1, -1, 0, 1, 1, 1 }; |
|
|
|
int adj = 0; |
|
vector<int> xLoc, yLoc; |
|
cv::Mat mask( m_height, m_width, CV_8U , Scalar::all(0) ); |
|
|
|
for( int i = 0; i < m_width; i++ ) |
|
{ |
|
for( int j = 0; j < m_height; j++) |
|
{ |
|
if( mask.at<uchar>(j,i) == 0 ) |
|
{ |
|
int L = m_klabels.at<int>(j,i); |
|
|
|
for( int k = 0; k < 8; k++ ) |
|
{ |
|
int x = i + dx8[k]; |
|
int y = j + dy8[k]; |
|
if ( x >= 0 && x <= m_width -1 |
|
&& y >= 0 && y <= m_height-1) |
|
{ |
|
if ( mask.at<uchar>(y,x) == 1 |
|
&& m_klabels.at<int>(y,x) != L ) |
|
adj = m_klabels.at<int>(y,x); |
|
break; |
|
} |
|
} |
|
|
|
mask.at<uchar>(j,i) = 1; |
|
xLoc.insert( xLoc.end(), i ); |
|
yLoc.insert( yLoc.end(), j ); |
|
|
|
size_t indexMarker = 0; |
|
while( indexMarker < xLoc.size() ) |
|
{ |
|
int x = xLoc[indexMarker]; |
|
int y = yLoc[indexMarker]; |
|
|
|
indexMarker++; |
|
|
|
int minX = ( x-1 <= 0 ) ? 0 : x-1; |
|
int minY = ( y-1 <= 0 ) ? 0 : y-1; |
|
int maxX = ( x+1 >= m_width -1 ) ? m_width -1 : x+1; |
|
int maxY = ( y+1 >= m_height-1 ) ? m_height-1 : y+1; |
|
for( int m = minX; m <= maxX; m++ ) |
|
{ |
|
for( int n = minY; n <= maxY; n++ ) |
|
{ |
|
if ( mask.at<uchar>(n,m) == 0 |
|
&& m_klabels.at<int>(n,m) == L ) |
|
{ |
|
mask.at<uchar>(n,m) = 1; |
|
xLoc.insert( xLoc.end(), m ); |
|
yLoc.insert( yLoc.end(), n ); |
|
} |
|
} |
|
} |
|
} |
|
if ( indexMarker < (size_t) min_element_size ) |
|
{ |
|
for( size_t k = 0; k < xLoc.size(); k++ ) |
|
{ |
|
int x = xLoc[k]; |
|
int y = yLoc[k]; |
|
m_klabels.at<int>(y,x) = adj; |
|
} |
|
} |
|
xLoc.clear(); |
|
yLoc.clear(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* |
|
* PostEnforceLabelConnectivity |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::PostEnforceLabelConnectivity( int threshold ) |
|
{ |
|
float PI2 = float(CV_PI / 2.0f); |
|
|
|
vector<float> centerW; |
|
queue <int> xLoc, yLoc; |
|
vector<float> centerX1, centerX2; |
|
vector<float> centerY1, centerY2; |
|
vector<int> strayX, strayY, Size; |
|
vector< vector<float> > centerC1( m_nr_channels ); |
|
vector< vector<float> > centerC2( m_nr_channels ); |
|
|
|
cv::Mat mask( m_height, m_width, CV_8U, Scalar::all(0) ); |
|
|
|
int L; |
|
int sLabel = -1; |
|
for( int i = 0; i < m_width; i++ ) |
|
{ |
|
for( int j = 0; j < m_height; j++ ) |
|
{ |
|
if( mask.at<uchar>(j,i) == 0 ) |
|
{ |
|
sLabel++; |
|
int count = 1; |
|
|
|
centerW.insert( centerW.end(), 0 ); |
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
centerC1[b].insert( centerC1[b].end(), 0 ); |
|
centerC2[b].insert( centerC2[b].end(), 0 ); |
|
} |
|
centerX1.insert( centerX1.end(), 0 ); |
|
centerX2.insert( centerX2.end(), 0 ); |
|
centerY1.insert( centerY1.end(), 0 ); |
|
centerY2.insert( centerY2.end(), 0 ); |
|
|
|
strayX.insert( strayX.end(), i ); |
|
strayY.insert( strayY.end(), j ); |
|
|
|
float Weight = m_W.at<float>(j,i); |
|
|
|
// accumulate dists |
|
centerW[sLabel] += Weight; |
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
|
|
float thetaC = 0.0f; |
|
switch ( m_chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) m_chvec[b].at<uchar>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) m_chvec[b].at<char>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) m_chvec[b].at<ushort>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) m_chvec[b].at<short>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) m_chvec[b].at<int>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) m_chvec[b].at<float>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) m_chvec[b].at<double>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
|
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = m_color_coeff * cos(thetaC) / m_nr_channels; |
|
float C2 = m_color_coeff * sin(thetaC) / m_nr_channels; |
|
|
|
centerC1[b][sLabel] += C1; centerC2[b][sLabel] += C2; |
|
} |
|
|
|
float thetaX = ( (float) i / (float) m_stepx ) * PI2; |
|
// we do not store pre-computed x1, x2 |
|
float X1 = m_dist_coeff * cos(thetaX); |
|
float X2 = m_dist_coeff * sin(thetaX); |
|
|
|
float thetaY = ( (float) j / (float) m_stepy ) * PI2; |
|
// we do not store pre-computed y1, y2 |
|
float Y1 = m_dist_coeff * cos(thetaY); |
|
float Y2 = m_dist_coeff * sin(thetaY); |
|
|
|
centerX1[sLabel] += X1; centerX2[sLabel] += X2; |
|
centerY1[sLabel] += Y1; centerY2[sLabel] += Y2; |
|
|
|
L = m_klabels.at<int>(j,i); |
|
m_klabels.at<int>(j,i) = sLabel; |
|
|
|
mask.at<uchar>(j,i) = 1; |
|
|
|
xLoc.push( i ); yLoc.push( j ); |
|
while( !xLoc.empty() ) |
|
{ |
|
int x = xLoc.front(); xLoc.pop(); |
|
int y = yLoc.front(); yLoc.pop(); |
|
int minX = ( x-1 <=0 ) ? 0 : x-1; |
|
int minY = ( y-1 <=0 ) ? 0 : y-1; |
|
int maxX = ( x+1 >= m_width -1 ) ? m_width -1 : x+1; |
|
int maxY = ( y+1 >= m_height-1 ) ? m_height-1 : y+1; |
|
for( int m = minX; m <= maxX; m++ ) |
|
{ |
|
for( int n = minY; n <= maxY; n++ ) |
|
{ |
|
if( mask.at<uchar>(n,m) == 0 |
|
&& m_klabels.at<int>(n,m) == L ) |
|
{ |
|
count++; |
|
xLoc.push(m); yLoc.push(n); |
|
|
|
mask.at<uchar>(n,m) = 1; |
|
m_klabels.at<int>(n,m) = sLabel; |
|
|
|
Weight = m_W.at<float>(n,m); |
|
centerW[sLabel] += Weight; |
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( m_chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) m_chvec[b].at<uchar>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) m_chvec[b].at<char>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) m_chvec[b].at<ushort>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) m_chvec[b].at<short>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) m_chvec[b].at<int>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) m_chvec[b].at<float>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) m_chvec[b].at<double>(j,i) / m_chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = m_color_coeff * cos(thetaC) / m_nr_channels; |
|
float C2 = m_color_coeff * sin(thetaC) / m_nr_channels; |
|
|
|
centerC1[b][sLabel] += C1; centerC2[b][sLabel] += C2; |
|
} |
|
|
|
thetaX = ( (float) m / (float) m_stepx ) * PI2; |
|
// we do not store pre-computed x1, x2 |
|
X1 = m_dist_coeff * cos(thetaX); |
|
X2 = m_dist_coeff * sin(thetaX); |
|
|
|
thetaY = ( (float) n / (float) m_stepy ) * PI2; |
|
// we do not store pre-computed y1, y2 |
|
Y1 = m_dist_coeff * cos(thetaY); |
|
Y2 = m_dist_coeff * sin(thetaY); |
|
|
|
centerX1[sLabel] += X1; centerX2[sLabel] += X2; |
|
centerY1[sLabel] += Y1; centerY2[sLabel] += Y2; |
|
|
|
} |
|
} |
|
} |
|
} |
|
Size.insert( Size.end(), count ); |
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
centerC1[b][sLabel] /= centerW[sLabel]; |
|
centerC2[b][sLabel] /= centerW[sLabel]; |
|
} |
|
centerX1[sLabel] /= centerW[sLabel]; |
|
centerX2[sLabel] /= centerW[sLabel]; |
|
centerY1[sLabel] /= centerW[sLabel]; |
|
centerY2[sLabel] /= centerW[sLabel]; |
|
} |
|
} |
|
} |
|
sLabel++; |
|
|
|
vector<Superpixel> Sarray; |
|
vector<int>::iterator Pointer; |
|
for( int i = 0; i < sLabel; i++) |
|
{ |
|
if( Size[i] < threshold ) |
|
{ |
|
int x = strayX[i]; |
|
int y = strayY[i]; |
|
|
|
L = m_klabels.at<int>(y,x); |
|
mask.at<uchar>(y,x) = 0; |
|
|
|
size_t indexMark = 0; |
|
Superpixel S( L, Size[i] ); |
|
|
|
S.xLoc.insert( S.xLoc.end(),x ); |
|
S.yLoc.insert( S.yLoc.end(),y ); |
|
while( indexMark < S.xLoc.size() ) |
|
{ |
|
x = S.xLoc[indexMark]; |
|
y = S.yLoc[indexMark]; |
|
|
|
indexMark++; |
|
|
|
int minX = ( x-1 <= 0 ) ? 0 : x-1; |
|
int minY = ( y-1 <= 0 ) ? 0 : y-1; |
|
int maxX = ( x+1 >= m_width -1 ) ? m_width -1 : x+1; |
|
int maxY = ( y+1 >= m_height-1 ) ? m_height-1 : y+1; |
|
for( int m = minX; m <= maxX; m++ ) |
|
{ |
|
for( int n = minY; n <= maxY; n++ ) |
|
{ |
|
if( mask.at<uchar>(n,m) == 1 |
|
&& m_klabels.at<int>(n,m) == L ) |
|
{ |
|
mask.at<uchar>(n,m) = 0; |
|
|
|
S.xLoc.insert( S.xLoc.end(), m ); |
|
S.yLoc.insert( S.yLoc.end(), n ); |
|
} |
|
else if( m_klabels.at<int>(n,m) != L ) |
|
{ |
|
int NewLabel = m_klabels.at<int>(n,m); |
|
Pointer = find( S.Neighbor.begin(), S.Neighbor.end(), NewLabel ); |
|
if ( Pointer == S.Neighbor.end() ) |
|
{ |
|
S.Neighbor.insert( S.Neighbor.begin(), NewLabel ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
Sarray.insert(Sarray.end(),S); |
|
} |
|
} |
|
|
|
vector<int>::iterator I, I2; |
|
vector<Superpixel>::iterator S; |
|
|
|
S = Sarray.begin(); |
|
while( S != Sarray.end() ) |
|
{ |
|
int Label1 = (*S).Label; |
|
int Label2 = -1; |
|
|
|
double MinDist = DBL_MAX; |
|
for ( I = (*S).Neighbor.begin(); I != (*S).Neighbor.end(); I++ ) |
|
{ |
|
double D = 0.0f; |
|
|
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
float diffcenterC1 = centerC1[b][Label1] |
|
- centerC1[b][*I]; |
|
float diffcenterC2 = centerC2[b][Label1] |
|
- centerC2[b][*I]; |
|
|
|
D += (diffcenterC1 * diffcenterC1) |
|
+ (diffcenterC2 * diffcenterC2); |
|
} |
|
|
|
float diffcenterX1 = centerX1[Label1] - centerX1[*I]; |
|
float diffcenterX2 = centerX2[Label1] - centerX2[*I]; |
|
float diffcenterY1 = centerY1[Label1] - centerY1[*I]; |
|
float diffcenterY2 = centerY2[Label1] - centerY2[*I]; |
|
|
|
D += (diffcenterX1 * diffcenterX1) |
|
+ (diffcenterX2 * diffcenterX2) |
|
+ (diffcenterY1 * diffcenterY1) |
|
+ (diffcenterY2 * diffcenterY2); |
|
|
|
// if within dist |
|
if ( D < MinDist ) |
|
{ |
|
MinDist = D; |
|
Label2 = (*I); |
|
} |
|
} |
|
|
|
double W1 = centerW[Label1]; |
|
double W2 = centerW[Label2]; |
|
|
|
double W = W1 + W2; |
|
|
|
for ( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
centerC1[b][Label2] = float((W2*centerC1[b][Label2] + W1*centerC1[b][Label1]) / W); |
|
centerC2[b][Label2] = float((W2*centerC2[b][Label2] + W1*centerC2[b][Label1]) / W); |
|
} |
|
centerX1[Label2] = float((W2*centerX1[Label2] + W1*centerX1[Label1]) / W); |
|
centerX2[Label2] = float((W2*centerX2[Label2] + W1*centerX2[Label1]) / W); |
|
centerY1[Label2] = float((W2*centerY1[Label2] + W1*centerY1[Label1]) / W); |
|
centerY2[Label2] = float((W2*centerY2[Label2] + W1*centerY2[Label1]) / W); |
|
|
|
centerW[Label2] = (float)W; |
|
for( size_t i = 0; i < (*S).xLoc.size(); i++ ) |
|
{ |
|
int x = (*S).xLoc[i]; |
|
int y = (*S).yLoc[i]; |
|
m_klabels.at<int>(y,x) = Label2; |
|
} |
|
|
|
vector<Superpixel>::iterator Stmp; |
|
Stmp = find( Sarray.begin(), Sarray.end(), Label2 ); |
|
if( Stmp != Sarray.end() ) |
|
{ |
|
Size[Label2] = Size[Label1] + Size[Label2]; |
|
if( Size[Label2] >= threshold ) |
|
{ |
|
Sarray.erase( S ); |
|
Sarray.erase( Stmp ); |
|
} |
|
else |
|
{ |
|
(*Stmp).xLoc.insert( (*Stmp).xLoc.end(), (*S).xLoc.begin(), (*S).xLoc.end() ); |
|
(*Stmp).yLoc.insert( (*Stmp).yLoc.end(), (*S).yLoc.begin(), (*S).yLoc.end() ); |
|
|
|
(*Stmp).Neighbor.insert( (*Stmp).Neighbor.end(), (*S).Neighbor.begin(), (*S).Neighbor.end() ); |
|
|
|
sort( (*Stmp).Neighbor.begin(), (*Stmp).Neighbor.end() ); |
|
|
|
I = unique( (*Stmp).Neighbor.begin(), (*Stmp).Neighbor.end() ); |
|
(*Stmp).Neighbor.erase( I, (*Stmp).Neighbor.end() ); |
|
|
|
I = find ( (*Stmp).Neighbor.begin(), (*Stmp).Neighbor.end(), Label1 ); |
|
(*Stmp).Neighbor.erase( I ); |
|
|
|
I = find ( (*Stmp).Neighbor.begin(), (*Stmp).Neighbor.end(), Label2 ); |
|
(*Stmp).Neighbor.erase( I ); |
|
|
|
Sarray.erase( S ); |
|
} |
|
} else Sarray.erase( S ); |
|
|
|
for( size_t i = 0; i < Sarray.size(); i++ ) |
|
{ |
|
I = find( Sarray[i].Neighbor.begin(), Sarray[i].Neighbor.end(), Label1 ); |
|
I2 = find( Sarray[i].Neighbor.begin(), Sarray[i].Neighbor.end(), Label2 ); |
|
|
|
if ( I != Sarray[i].Neighbor.end() |
|
&& I2 != Sarray[i].Neighbor.end() ) |
|
{ |
|
Sarray[i].Neighbor.erase( I ); |
|
} |
|
else |
|
if ( I != Sarray[i].Neighbor.end() |
|
&& I2 == Sarray[i].Neighbor.end() ) |
|
{ |
|
(*I) = Label2; |
|
} |
|
} |
|
|
|
S = Sarray.begin(); |
|
|
|
} |
|
} |
|
|
|
/* |
|
* GetChannelsSeeds_ForGivenStepSize |
|
* |
|
* The k seed values are |
|
* taken as uniform spatial |
|
* pixel samples. |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::GetChSeeds() |
|
{ |
|
int ColNum = (int) sqrt( (double) m_numlabels |
|
* ((double)m_width / (double)m_height) ); |
|
int RowNum = m_numlabels / ColNum; |
|
|
|
m_stepx = m_width / ColNum; |
|
m_stepy = m_height / RowNum; |
|
|
|
int Col_remain = m_width - (m_stepx*ColNum); |
|
int Row_remain = m_height - (m_stepy*RowNum); |
|
|
|
int count = 0; |
|
int t1 = 1, t2 = 1; |
|
int centerx, centery; |
|
|
|
for( int x = 0; x < ColNum; x++ ) |
|
{ |
|
t2 = 1; |
|
centerx = int((x*m_stepx) + (0.5f*m_stepx) + t1); |
|
if ( centerx >= m_width -1 ) centerx = m_width -1; |
|
|
|
for( int y = 0; y < RowNum; y++ ) |
|
{ |
|
centery = int((y*m_stepy) + (0.5f*m_stepy) + t2); |
|
|
|
if ( t2 < Row_remain ) t2++; |
|
if ( centery >= m_height-1 ) centery = m_height-1; |
|
|
|
m_kseedsx.push_back( (float)centerx ); |
|
m_kseedsy.push_back( (float)centery ); |
|
|
|
count++; |
|
} |
|
if ( t1 < Col_remain ) t1++; |
|
} |
|
// update amount |
|
m_numlabels = count; |
|
} |
|
|
|
struct FeatureSpaceSigmas |
|
{ |
|
FeatureSpaceSigmas( const vector< Mat >& _chvec, const int _nr_channels, |
|
const float _chvec_max, const float _dist_coeff, |
|
const float _color_coeff, const int _stepx, const int _stepy ) |
|
{ |
|
chvec = _chvec; |
|
stepx = _stepx; |
|
stepy = _stepy; |
|
chvec_max = _chvec_max; |
|
dist_coeff = _dist_coeff; |
|
nr_channels = _nr_channels; |
|
color_coeff = _color_coeff; |
|
|
|
PI2 = float(CV_PI / 2.0f); |
|
sigmaX1 = 0; sigmaX2 = 0; |
|
sigmaY1 = 0; sigmaY2 = 0; |
|
sigmaC1.resize( nr_channels ); |
|
sigmaC2.resize( nr_channels ); |
|
fill( sigmaC1.begin(), sigmaC1.end(), 0 ); |
|
fill( sigmaC2.begin(), sigmaC2.end(), 0 ); |
|
} |
|
|
|
FeatureSpaceSigmas( const FeatureSpaceSigmas& counter, Split ) |
|
{ |
|
*this = counter; |
|
sigmaX1 = 0; sigmaX2 = 0; |
|
sigmaY1 = 0; sigmaY2 = 0; |
|
sigmaC1.resize( nr_channels ); |
|
sigmaC2.resize( nr_channels ); |
|
fill( sigmaC1.begin(), sigmaC1.end(), 0 ); |
|
fill( sigmaC2.begin(), sigmaC2.end(), 0 ); |
|
} |
|
|
|
void operator()( const BlockedRange& range ) |
|
{ |
|
// previous block state |
|
double tmp_sigmaX1 = sigmaX1; |
|
double tmp_sigmaX2 = sigmaX2; |
|
double tmp_sigmaY1 = sigmaY1; |
|
double tmp_sigmaY2 = sigmaY2; |
|
vector<double> tmp_sigmaC1( nr_channels ); |
|
vector<double> tmp_sigmaC2( nr_channels ); |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
tmp_sigmaC1[b] = sigmaC1[b]; |
|
tmp_sigmaC2[b] = sigmaC2[b]; |
|
} |
|
|
|
for ( int x = range.begin(); x != range.end(); x++ ) |
|
{ |
|
float thetaX = ( (float) x / (float) stepx ) * PI2; |
|
// we do not store pre-computed x1, x2 |
|
float x1 = dist_coeff * cos(thetaX); |
|
float x2 = dist_coeff * sin(thetaX); |
|
|
|
for( int y = 0; y < chvec[0].rows; y++ ) |
|
{ |
|
float thetaY = ( (float) y / (float) stepy ) * PI2; |
|
// we do not store pre-computed y1, y2 |
|
float y1 = dist_coeff * cos(thetaY); |
|
float y2 = dist_coeff * sin(thetaY); |
|
|
|
// accumulate distance sigmas |
|
tmp_sigmaX1 += x1; tmp_sigmaX2 += x2; |
|
tmp_sigmaY1 += y1; tmp_sigmaY2 += y2; |
|
|
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) chvec[b].at<uchar>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) chvec[b].at<char>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) chvec[b].at<ushort>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) chvec[b].at<short>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) chvec[b].at<int>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) chvec[b].at<float>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) chvec[b].at<double>(y,x) / chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = color_coeff * cos(thetaC) / nr_channels; |
|
float C2 = color_coeff * sin(thetaC) / nr_channels; |
|
|
|
// accumulate sigmas per channels |
|
tmp_sigmaC1[b] += C1; tmp_sigmaC2[b] += C2; |
|
} |
|
} |
|
} |
|
sigmaX1 = tmp_sigmaX1; sigmaX2 = tmp_sigmaX2; |
|
sigmaY1 = tmp_sigmaY1; sigmaY2 = tmp_sigmaY2; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
sigmaC1[b] = tmp_sigmaC1[b]; |
|
sigmaC2[b] = tmp_sigmaC2[b]; |
|
} |
|
} |
|
|
|
void join( FeatureSpaceSigmas& fsc ) |
|
{ |
|
sigmaX1 += fsc.sigmaX1; sigmaX2 += fsc.sigmaX2; |
|
sigmaY1 += fsc.sigmaY1; sigmaY2 += fsc.sigmaY2; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
sigmaC1[b] += fsc.sigmaC1[b]; |
|
sigmaC2[b] += fsc.sigmaC2[b]; |
|
} |
|
} |
|
|
|
float PI2; |
|
int nr_channels; |
|
int stepx, stepy; |
|
|
|
double sigmaX1, sigmaX2; |
|
double sigmaY1, sigmaY2; |
|
|
|
float chvec_max; |
|
float dist_coeff; |
|
float color_coeff; |
|
|
|
vector<Mat> chvec; |
|
vector<double> sigmaC1; |
|
vector<double> sigmaC2; |
|
}; |
|
|
|
struct FeatureSpaceWeights : ParallelLoopBody |
|
{ |
|
FeatureSpaceWeights( const vector< Mat >& _chvec, Mat* _W, |
|
const double _sigmaX1, const double _sigmaX2, |
|
const double _sigmaY1, const double _sigmaY2, |
|
vector<double>& _sigmaC1, vector<double>& _sigmaC2, |
|
const int _nr_channels, const float _chvec_max, |
|
const float _dist_coeff, const float _color_coeff, |
|
const int _stepx, const int _stepy ) |
|
{ |
|
W = _W; |
|
chvec = _chvec; |
|
stepx = _stepx; |
|
stepy = _stepy; |
|
chvec_max = _chvec_max; |
|
dist_coeff = _dist_coeff; |
|
nr_channels = _nr_channels; |
|
color_coeff = _color_coeff; |
|
|
|
PI2 = float(CV_PI / 2.0f); |
|
|
|
sigmaX1 = _sigmaX1; sigmaX2 = _sigmaX2; |
|
sigmaY1 = _sigmaY1; sigmaY2 = _sigmaY2; |
|
sigmaC1 = _sigmaC1; sigmaC2 = _sigmaC2; |
|
} |
|
|
|
void operator()( const Range& range ) const |
|
{ |
|
for( int x = range.start; x < range.end; x++ ) |
|
{ |
|
float thetaX = ( (float) x / (float) stepx ) * PI2; |
|
|
|
for( int y = 0; y < chvec[0].rows; y++ ) |
|
{ |
|
float thetaY = ( (float) y / (float) stepy ) * PI2; |
|
|
|
// accumulate distance channels weighted by sigmas |
|
W->at<float>(y,x) += float((dist_coeff * cos(thetaX)) * sigmaX1); |
|
W->at<float>(y,x) += float((dist_coeff * sin(thetaX)) * sigmaX2); |
|
W->at<float>(y,x) += float((dist_coeff * cos(thetaY)) * sigmaY1); |
|
W->at<float>(y,x) += float((dist_coeff * sin(thetaY)) * sigmaY2); |
|
|
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) chvec[b].at<uchar>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) chvec[b].at<char>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) chvec[b].at<ushort>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) chvec[b].at<short>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) chvec[b].at<int>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) chvec[b].at<float>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) chvec[b].at<double>(y,x) / chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
// accumulate color channels weighted by sigmas |
|
W->at<float>(y,x) += float((color_coeff * cos(thetaC) / nr_channels) * sigmaC1[b]); |
|
W->at<float>(y,x) += float((color_coeff * sin(thetaC) / nr_channels) * sigmaC2[b]); |
|
} |
|
} |
|
} |
|
} |
|
|
|
Mat* W; |
|
float PI2; |
|
int nr_channels; |
|
int stepx, stepy; |
|
|
|
double sigmaX1, sigmaX2; |
|
double sigmaY1, sigmaY2; |
|
|
|
float chvec_max; |
|
float dist_coeff; |
|
float color_coeff; |
|
|
|
vector<Mat> chvec; |
|
vector<double> sigmaC1; |
|
vector<double> sigmaC2; |
|
}; |
|
|
|
/* |
|
* Compute Feature Space |
|
* |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::GetFeatureSpace() |
|
{ |
|
double sigmaX1 = 0.0f, sigmaX2 = 0.0f; |
|
double sigmaY1 = 0.0f, sigmaY2 = 0.0f; |
|
vector<double> sigmaC1( m_nr_channels , 0.0f ); |
|
vector<double> sigmaC2( m_nr_channels , 0.0f ); |
|
|
|
// compute feature space accumulation sigmas |
|
FeatureSpaceSigmas fss( m_chvec, m_nr_channels, m_chvec_max, |
|
m_dist_coeff, m_color_coeff, m_stepx, m_stepy ); |
|
parallel_reduce( BlockedRange(0, m_width), fss ); |
|
|
|
sigmaX1 = fss.sigmaX1; sigmaX2 = fss.sigmaX2; |
|
sigmaY1 = fss.sigmaY1; sigmaY2 = fss.sigmaY2; |
|
for( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
sigmaC1[b] = fss.sigmaC1[b]; |
|
sigmaC2[b] = fss.sigmaC2[b]; |
|
} |
|
|
|
// normalize sigmas |
|
sigmaY1 /= m_width*m_height; |
|
sigmaY2 /= m_width*m_height; |
|
sigmaX1 /= m_width*m_height; |
|
sigmaX2 /= m_width*m_height; |
|
for( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
sigmaC1[b] /= m_width*m_height; |
|
sigmaC2[b] /= m_width*m_height; |
|
} |
|
|
|
// compute m_W normalization array |
|
m_W = Mat( m_height, m_width, CV_32F ); |
|
parallel_for_( Range(0, m_width), FeatureSpaceWeights( m_chvec, &m_W, |
|
sigmaX1, sigmaX2, sigmaY1, sigmaY2, sigmaC1, sigmaC2, |
|
m_nr_channels, m_chvec_max, m_dist_coeff, m_color_coeff, |
|
m_stepx, m_stepy ) ); |
|
} |
|
|
|
struct FeatureSpaceCenters : ParallelLoopBody |
|
{ |
|
FeatureSpaceCenters( const vector< Mat >& _chvec, const Mat& _W, |
|
const vector<float>& _kseedsx, const vector<float>& _kseedsy, |
|
vector<float>* _centerX1, vector<float>* _centerX2, |
|
vector<float>* _centerY1, vector<float>* _centerY2, |
|
vector< vector<float> >* _centerC1, vector< vector<float> >* _centerC2, |
|
const int _nr_channels, const float _chvec_max, |
|
const float _dist_coeff, const float _color_coeff, |
|
const int _stepx, const int _stepy ) |
|
{ |
|
W = _W; |
|
chvec = _chvec; |
|
stepx = _stepx; |
|
stepy = _stepy; |
|
chvec_max = _chvec_max; |
|
dist_coeff = _dist_coeff; |
|
nr_channels = _nr_channels; |
|
color_coeff = _color_coeff; |
|
|
|
PI2 = float(CV_PI / 2.0f); |
|
|
|
kseedsx = _kseedsx; |
|
kseedsy = _kseedsy; |
|
width = chvec[0].cols; |
|
height = chvec[0].rows; |
|
|
|
centerX1 = _centerX1; centerX2 = _centerX2; |
|
centerY1 = _centerY1; centerY2 = _centerY2; |
|
centerC1 = _centerC1; centerC2 = _centerC2; |
|
} |
|
|
|
void operator()( const Range& range ) const |
|
{ |
|
for( int i = range.start; i < range.end; i++ ) |
|
{ |
|
centerX1->at(i) = 0.0f; centerX2->at(i) = 0.0f; |
|
centerY1->at(i) = 0.0f; centerY2->at(i) = 0.0f; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
centerC1->at(b)[i] = 0.0f; centerC2->at(b)[i] = 0.0f; |
|
} |
|
|
|
int X = (int)kseedsx[i]; int Y = (int)kseedsy[i]; |
|
int minX = (X-stepx/4 <= 0) ? 0 : X-stepx/4; |
|
int minY = (Y-stepy/4 <= 0) ? 0 : Y-stepy/4; |
|
int maxX = (X+stepx/4 >= width -1) ? width -1 : X+stepx/4; |
|
int maxY = (Y+stepy/4 >= height-1) ? height-1 : Y+stepy/4; |
|
|
|
int count = 0; |
|
for( int x = minX; x <= maxX; x++ ) |
|
{ |
|
float thetaX = ( (float) x / (float) stepx ) * PI2; |
|
|
|
float tx1 = dist_coeff * cos(thetaX); |
|
float tx2 = dist_coeff * sin(thetaX); |
|
|
|
for( int y = minY; y <= maxY; y++ ) |
|
{ |
|
count++; |
|
float thetaY = ( (float) y / (float) stepy ) * PI2; |
|
|
|
// we do not store pre-computed x1, x2 |
|
float x1 = tx1 / W.at<float>(y,x); |
|
float x2 = tx2 / W.at<float>(y,x); |
|
|
|
// we do not store pre-computed y1, y2 |
|
float y1 = (dist_coeff * cos(thetaY)) / W.at<float>(y,x); |
|
float y2 = (dist_coeff * sin(thetaY)) / W.at<float>(y,x); |
|
|
|
centerX1->at(i) += x1; centerX2->at(i) += x2; |
|
centerY1->at(i) += y1; centerY2->at(i) += y2; |
|
|
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) chvec[b].at<uchar>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) chvec[b].at<char>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) chvec[b].at<ushort>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) chvec[b].at<short>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) chvec[b].at<int>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) chvec[b].at<float>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) chvec[b].at<double>(y,x) / chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = (color_coeff * cos(thetaC) / nr_channels) / W.at<float>(y,x); |
|
float C2 = (color_coeff * sin(thetaC) / nr_channels) / W.at<float>(y,x); |
|
|
|
centerC1->at(b)[i] += C1; centerC2->at(b)[i] += C2; |
|
} |
|
} |
|
} |
|
// normalize |
|
centerX1->at(i) /= count; centerX2->at(i) /= count; |
|
centerY1->at(i) /= count; centerY2->at(i) /= count; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
centerC1->at(b)[i] /= count; centerC2->at(b)[i] /= count; |
|
} |
|
} |
|
} |
|
|
|
Mat W; |
|
float PI2; |
|
int nr_channels; |
|
int stepx, stepy; |
|
int width, height; |
|
|
|
float chvec_max; |
|
float dist_coeff; |
|
float color_coeff; |
|
|
|
vector<Mat> chvec; |
|
vector<float> kseedsx, kseedsy; |
|
vector< vector<float> >* centerC1; |
|
vector< vector<float> >* centerC2; |
|
vector<float> *centerX1, *centerX2; |
|
vector<float> *centerY1, *centerY2; |
|
}; |
|
|
|
struct FeatureSpaceKmeans : ParallelLoopBody |
|
{ |
|
FeatureSpaceKmeans( Mat* _klabels, Mat* _dist, |
|
const vector< Mat >& _chvec, const Mat& _W, |
|
const vector<float>& _kseedsx, const vector<float>& _kseedsy, |
|
vector<float>& _centerX1, vector<float>& _centerX2, |
|
vector<float>& _centerY1, vector<float>& _centerY2, |
|
vector< vector<float> >& _centerC1, vector< vector<float> >& _centerC2, |
|
const int _nr_channels, const float _chvec_max, |
|
const float _dist_coeff, const float _color_coeff, |
|
const int _stepx, const int _stepy ) |
|
{ |
|
W = _W; |
|
dist = _dist; |
|
chvec = _chvec; |
|
stepx = _stepx; |
|
stepy = _stepy; |
|
klabels = _klabels; |
|
chvec_max = _chvec_max; |
|
dist_coeff = _dist_coeff; |
|
nr_channels = _nr_channels; |
|
color_coeff = _color_coeff; |
|
|
|
PI2 = float(CV_PI / 2.0f); |
|
|
|
kseedsx = _kseedsx; |
|
kseedsy = _kseedsy; |
|
width = chvec[0].cols; |
|
height = chvec[0].rows; |
|
|
|
centerX1 = _centerX1; centerX2 = _centerX2; |
|
centerY1 = _centerY1; centerY2 = _centerY2; |
|
centerC1 = _centerC1; centerC2 = _centerC2; |
|
} |
|
|
|
void operator()( const Range& range ) const |
|
{ |
|
for( int i = range.start; i < range.end; i++ ) |
|
{ |
|
int X = (int)kseedsx[i]; int Y = (int)kseedsy[i]; |
|
int minX = (X-(stepx) <= 0) ? 0 : X-stepx; |
|
int minY = (Y-(stepy) <= 0) ? 0 : Y-stepy; |
|
int maxX = (X+(stepx) >= width -1) ? width -1 : X+stepx; |
|
int maxY = (Y+(stepy) >= height-1) ? height-1 : Y+stepy; |
|
|
|
for( int x = minX; x <= maxX; x++ ) |
|
{ |
|
float thetaX = ( (float) x / (float) stepx ) * PI2; |
|
|
|
float tx1 = dist_coeff * cos(thetaX); |
|
float tx2 = dist_coeff * sin(thetaX); |
|
|
|
for( int y = minY; y <= maxY; y++ ) |
|
{ |
|
float thetaY = ( (float) y / (float) stepy ) * PI2; |
|
|
|
// we do not store pre-computed x1, x2 |
|
float x1 = tx1 / W.at<float>(y,x); |
|
float x2 = tx2 / W.at<float>(y,x); |
|
// we do not store pre-computed y1, y2 |
|
float y1 = (dist_coeff * cos(thetaY)) / W.at<float>(y,x); |
|
float y2 = (dist_coeff * sin(thetaY)) / W.at<float>(y,x); |
|
|
|
float diffx1 = x1 - centerX1[i]; float diffx2 = x2 - centerX2[i]; |
|
float diffy1 = y1 - centerY1[i]; float diffy2 = y2 - centerY2[i]; |
|
|
|
// compute distance given distance terms |
|
double D = (diffx1 * diffx1) + (diffx2 * diffx2) |
|
+ (diffy1 * diffy1) + (diffy2 * diffy2); |
|
|
|
// compute distance given channels terms |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) chvec[b].at<uchar>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) chvec[b].at<char>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) chvec[b].at<ushort>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) chvec[b].at<short>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) chvec[b].at<int>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) chvec[b].at<float>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) chvec[b].at<double>(y,x) / chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = (color_coeff * cos(thetaC) / nr_channels) / W.at<float>(y,x); |
|
float C2 = (color_coeff * sin(thetaC) / nr_channels) / W.at<float>(y,x); |
|
|
|
float diffC1 = C1 - centerC1[b][i]; float diffC2 = C2 - centerC2[b][i]; |
|
|
|
D += (diffC1 * diffC1) + (diffC2 * diffC2); |
|
} |
|
|
|
// assign label if within D |
|
if ( D < dist->at<float>(y,x) ) |
|
{ |
|
dist->at<float>(y,x) = (float)D; |
|
klabels->at<int>(y,x) = i; |
|
} |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
Mat W; |
|
float PI2; |
|
int nr_channels; |
|
int stepx, stepy; |
|
int width, height; |
|
|
|
float chvec_max; |
|
float dist_coeff; |
|
float color_coeff; |
|
|
|
Mat* dist; |
|
Mat* klabels; |
|
vector<Mat> chvec; |
|
vector<float> kseedsx, kseedsy; |
|
vector<float> centerX1, centerX2; |
|
vector<float> centerY1, centerY2; |
|
vector< vector<float> > centerC1; |
|
vector< vector<float> > centerC2; |
|
}; |
|
|
|
struct FeatureCenterDists |
|
{ |
|
FeatureCenterDists( const vector< Mat >& _chvec, const Mat& _W, const Mat& _klabels, |
|
const int _nr_channels, const float _chvec_max, const float _dist_coeff, |
|
const float _color_coeff, const int _stepx, const int _stepy, const int _numlabels ) |
|
{ |
|
W = _W; |
|
chvec = _chvec; |
|
stepx = _stepx; |
|
stepy = _stepy; |
|
klabels = _klabels; |
|
numlabels = _numlabels; |
|
chvec_max = _chvec_max; |
|
dist_coeff = _dist_coeff; |
|
nr_channels = _nr_channels; |
|
color_coeff = _color_coeff; |
|
|
|
PI2 = float(CV_PI / 2.0f); |
|
|
|
Wsum.resize(numlabels); |
|
kseedsx.resize(numlabels); |
|
kseedsy.resize(numlabels); |
|
centerX1.resize(numlabels); |
|
centerX2.resize(numlabels); |
|
centerY1.resize(numlabels); |
|
centerY2.resize(numlabels); |
|
centerC1.resize(nr_channels); |
|
centerC2.resize(nr_channels); |
|
clusterSize.resize(numlabels); |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
centerC1[b].resize(numlabels); |
|
centerC2[b].resize(numlabels); |
|
} |
|
// refill with zero all arrays |
|
fill(centerX1.begin(), centerX1.end(), 0.0f); |
|
fill(centerX2.begin(), centerX2.end(), 0.0f); |
|
fill(centerY1.begin(), centerY1.end(), 0.0f); |
|
fill(centerY2.begin(), centerY2.end(), 0.0f); |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
fill(centerC1[b].begin(), centerC1[b].end(), 0.0f); |
|
fill(centerC2[b].begin(), centerC2[b].end(), 0.0f); |
|
} |
|
fill(Wsum.begin(), Wsum.end(), 0.0f); |
|
fill(kseedsx.begin(), kseedsx.end(), 0.0f); |
|
fill(kseedsy.begin(), kseedsy.end(), 0.0f); |
|
fill(clusterSize.begin(), clusterSize.end(), 0); |
|
} |
|
|
|
FeatureCenterDists( const FeatureCenterDists& counter, Split ) |
|
{ |
|
*this = counter; |
|
// refill with zero all arrays |
|
fill(centerX1.begin(), centerX1.end(), 0.0f); |
|
fill(centerX2.begin(), centerX2.end(), 0.0f); |
|
fill(centerY1.begin(), centerY1.end(), 0.0f); |
|
fill(centerY2.begin(), centerY2.end(), 0.0f); |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
fill(centerC1[b].begin(), centerC1[b].end(), 0.0f); |
|
fill(centerC2[b].begin(), centerC2[b].end(), 0.0f); |
|
} |
|
fill(Wsum.begin(), Wsum.end(), 0.0f); |
|
fill(kseedsx.begin(), kseedsx.end(), 0.0f); |
|
fill(kseedsy.begin(), kseedsy.end(), 0.0f); |
|
fill(clusterSize.begin(), clusterSize.end(), 0); |
|
|
|
} |
|
|
|
void operator()( const BlockedRange& range ) |
|
{ |
|
// previous block state |
|
vector<float> tmp_Wsum = Wsum; |
|
vector<float> tmp_kseedsx = kseedsx; |
|
vector<float> tmp_kseedsy = kseedsy; |
|
vector<float> tmp_centerX1 = centerX1; |
|
vector<float> tmp_centerX2 = centerX2; |
|
vector<float> tmp_centerY1 = centerY1; |
|
vector<float> tmp_centerY2 = centerY2; |
|
vector< vector<float> > tmp_centerC1 = centerC1; |
|
vector< vector<float> > tmp_centerC2 = centerC2; |
|
vector<int> tmp_clusterSize = clusterSize; |
|
|
|
for ( int x = range.begin(); x != range.end(); x++ ) |
|
{ |
|
|
|
float thetaX = ( (float) x / (float) stepx ) * PI2; |
|
|
|
// we do not store pre-computed x1, x2 |
|
float x1 = (dist_coeff * cos(thetaX)); |
|
float x2 = (dist_coeff * sin(thetaX)); |
|
|
|
for( int y = 0; y < chvec[0].rows; y++ ) |
|
{ |
|
float thetaY = ( (float) y / (float) stepy ) * PI2; |
|
|
|
// we do not store pre-computed y1, y2 |
|
float y1 = (dist_coeff * cos(thetaY)); |
|
float y2 = (dist_coeff * sin(thetaY)); |
|
|
|
int L = klabels.at<int>(y,x); |
|
|
|
tmp_centerX1[L] += x1; tmp_centerX2[L] += x2; |
|
tmp_centerY1[L] += y1; tmp_centerY2[L] += y2; |
|
|
|
// compute distance given channels terms |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
float thetaC = 0.0f; |
|
switch ( chvec[b].depth() ) |
|
{ |
|
case CV_8U: |
|
thetaC = ( (float) chvec[b].at<uchar>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_8S: |
|
thetaC = ( (float) chvec[b].at<char>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16U: |
|
thetaC = ( (float) chvec[b].at<ushort>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_16S: |
|
thetaC = ( (float) chvec[b].at<short>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32S: |
|
thetaC = ( (float) chvec[b].at<int>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_32F: |
|
thetaC = ( (float) chvec[b].at<float>(y,x) / chvec_max ) * PI2; |
|
break; |
|
case CV_64F: |
|
thetaC = ( (float) chvec[b].at<double>(y,x) / chvec_max ) * PI2; |
|
break; |
|
default: |
|
CV_Error( Error::StsInternal, "Invalid matrix depth" ); |
|
break; |
|
} |
|
|
|
// we do not store pre-computed C1[b], C2[b] |
|
float C1 = (color_coeff * cos(thetaC) / nr_channels); |
|
float C2 = (color_coeff * sin(thetaC) / nr_channels); |
|
|
|
tmp_centerC1[b][L] += C1; tmp_centerC2[b][L] += C2; |
|
|
|
} |
|
tmp_clusterSize[L]++; |
|
tmp_Wsum[L] += W.at<float>(y,x); |
|
tmp_kseedsx[L] += x; tmp_kseedsy[L] += y; |
|
} |
|
} |
|
|
|
Wsum = tmp_Wsum; |
|
kseedsx = tmp_kseedsx; |
|
kseedsy = tmp_kseedsy; |
|
clusterSize = tmp_clusterSize; |
|
centerX1 = tmp_centerX1; centerX2 = tmp_centerX2; |
|
centerY1 = tmp_centerY1; centerY2 = tmp_centerY2; |
|
centerC1 = tmp_centerC1; centerC2 = tmp_centerC2; |
|
} |
|
|
|
void join( FeatureCenterDists& fcd ) |
|
{ |
|
for (int l = 0; l < numlabels; l++) |
|
{ |
|
Wsum[l] += fcd.Wsum[l]; |
|
kseedsx[l] += fcd.kseedsx[l]; |
|
kseedsy[l] += fcd.kseedsy[l]; |
|
centerX1[l] += fcd.centerX1[l]; |
|
centerX2[l] += fcd.centerX2[l]; |
|
centerY1[l] += fcd.centerY1[l]; |
|
centerY2[l] += fcd.centerY2[l]; |
|
clusterSize[l] += fcd.clusterSize[l]; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
centerC1[b][l] += fcd.centerC1[b][l]; |
|
centerC2[b][l] += fcd.centerC2[b][l]; |
|
} |
|
} |
|
} |
|
|
|
Mat W; |
|
float PI2; |
|
int numlabels; |
|
int nr_channels; |
|
int stepx, stepy; |
|
|
|
float chvec_max; |
|
float dist_coeff; |
|
float color_coeff; |
|
|
|
Mat klabels; |
|
vector<Mat> chvec; |
|
|
|
vector<float> Wsum; |
|
vector<int> clusterSize; |
|
vector<float> kseedsx, kseedsy; |
|
vector<float> centerX1, centerX2; |
|
vector<float> centerY1, centerY2; |
|
vector< vector<float> > centerC1, centerC2; |
|
|
|
}; |
|
|
|
struct FeatureNormals : ParallelLoopBody |
|
{ |
|
FeatureNormals( const vector<float>& _Wsum, const vector<int>& _clusterSize, |
|
vector<float>* _kseedsx, vector<float>* _kseedsy, |
|
vector<float>* _centerX1, vector<float>* _centerX2, |
|
vector<float>* _centerY1, vector<float>* _centerY2, |
|
vector< vector<float> >* _centerC1, vector< vector<float> >* _centerC2, |
|
const int _numlabels, const int _nr_channels ) |
|
{ |
|
Wsum = _Wsum; |
|
numlabels = _numlabels; |
|
clusterSize = _clusterSize; |
|
nr_channels = _nr_channels; |
|
|
|
kseedsx = _kseedsx; kseedsy = _kseedsy; |
|
centerX1 = _centerX1; centerX2 = _centerX2; |
|
centerY1 = _centerY1; centerY2 = _centerY2; |
|
centerC1 = _centerC1; centerC2 = _centerC2; |
|
} |
|
|
|
void operator()( const Range& range ) const |
|
{ |
|
for( int i = range.start; i < range.end; i++ ) |
|
{ |
|
if ( Wsum[i] != 0 ) |
|
{ |
|
centerX1->at(i) /= Wsum[i]; centerX2->at(i) /= Wsum[i]; |
|
centerY1->at(i) /= Wsum[i]; centerY2->at(i) /= Wsum[i]; |
|
for( int b = 0; b < nr_channels; b++ ) |
|
{ |
|
centerC1->at(b)[i] /= Wsum[i]; centerC2->at(b)[i] /= Wsum[i]; |
|
} |
|
} |
|
if ( clusterSize[i] != 0 ) |
|
{ |
|
kseedsx->at(i) /= clusterSize[i]; |
|
kseedsy->at(i) /= clusterSize[i]; |
|
} |
|
} |
|
} |
|
|
|
int numlabels; |
|
vector<float> Wsum; |
|
vector<int> clusterSize; |
|
int nr_channels; |
|
|
|
vector<float> *kseedsx, *kseedsy; |
|
vector<float> *centerX1, *centerX2; |
|
vector<float> *centerY1, *centerY2; |
|
vector< vector<float> > *centerC1; |
|
vector< vector<float> > *centerC2; |
|
}; |
|
|
|
|
|
/* |
|
* PerformSuperpixelLSC |
|
* |
|
* Performs weighted kmeans segmentation |
|
* in (4 + 2*m_nr_channels) dimensional space |
|
* |
|
*/ |
|
inline void SuperpixelLSCImpl::PerformLSC( const int& itrnum ) |
|
{ |
|
// allocate initial workspaces |
|
cv::Mat dist( m_height, m_width, CV_32F ); |
|
|
|
vector<float> centerX1( m_numlabels ); |
|
vector<float> centerX2( m_numlabels ); |
|
vector<float> centerY1( m_numlabels ); |
|
vector<float> centerY2( m_numlabels ); |
|
vector< vector<float> > centerC1( m_nr_channels ); |
|
vector< vector<float> > centerC2( m_nr_channels ); |
|
for( int b = 0; b < m_nr_channels; b++ ) |
|
{ |
|
centerC1[b].resize( m_numlabels ); |
|
centerC2[b].resize( m_numlabels ); |
|
} |
|
vector<float> Wsum( m_numlabels ); |
|
vector<int> clusterSize( m_numlabels ); |
|
|
|
// compute weighted distance centers |
|
parallel_for_( Range(0, m_numlabels), FeatureSpaceCenters( |
|
m_chvec, m_W, m_kseedsx, m_kseedsy, |
|
¢erX1, ¢erX2, ¢erY1, ¢erY2, |
|
¢erC1, ¢erC2, m_nr_channels, m_chvec_max, |
|
m_dist_coeff, m_color_coeff, m_stepx, m_stepy ) ); |
|
|
|
// parallel reduce structure |
|
FeatureCenterDists fcd( m_chvec, m_W, m_klabels, m_nr_channels, m_chvec_max, |
|
m_dist_coeff, m_color_coeff, m_stepx, m_stepy, m_numlabels ); |
|
|
|
// K-Means |
|
for( int itr = 0; itr < itrnum; itr++ ) |
|
{ |
|
|
|
dist.setTo( FLT_MAX ); |
|
|
|
// k-mean |
|
parallel_for_( Range(0, m_numlabels), FeatureSpaceKmeans( |
|
&m_klabels, &dist, m_chvec, m_W, m_kseedsx, m_kseedsy, |
|
centerX1, centerX2, centerY1, centerY2, centerC1, centerC2, |
|
m_nr_channels, m_chvec_max, m_dist_coeff, m_color_coeff, |
|
m_stepx, m_stepy ) ); |
|
|
|
// accumulate center distances |
|
parallel_reduce( BlockedRange(0, m_width), fcd ); |
|
|
|
// featch out the results |
|
Wsum = fcd.Wsum; clusterSize = fcd.clusterSize; |
|
m_kseedsx = fcd.kseedsx; m_kseedsy = fcd.kseedsy; |
|
centerX1 = fcd.centerX1; centerX2 = fcd.centerX2; |
|
centerY1 = fcd.centerY1; centerY2 = fcd.centerY2; |
|
centerC1 = fcd.centerC1; centerC2 = fcd.centerC2; |
|
|
|
|
|
// normalize accumulated distances |
|
parallel_for_( Range(0, m_numlabels), FeatureNormals( |
|
Wsum, clusterSize, &m_kseedsx, &m_kseedsy, |
|
¢erX1, ¢erX2, ¢erY1, ¢erY2, |
|
¢erC1, ¢erC2, m_numlabels, m_nr_channels ) ); |
|
} |
|
} |
|
|
|
} // namespace ximgproc |
|
} // namespace cv
|
|
|