mirror of
https://github.com/opencv/opencv_contrib.git
synced 2025-10-21 14:41:58 +08:00
564 lines
18 KiB
C++
564 lines
18 KiB
C++
// 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 "trackerCSRTUtils.hpp"
|
|
|
|
namespace cv {
|
|
|
|
Mat circshift(Mat matrix, int dx, int dy)
|
|
{
|
|
Mat matrix_out = matrix.clone();
|
|
int idx_y = 0;
|
|
int idx_x = 0;
|
|
for(int i=0; i<matrix.rows; i++) {
|
|
for(int j=0; j<matrix.cols; j++) {
|
|
idx_y = modul(i+dy+1, matrix.rows);
|
|
idx_x = modul(j+dx+1, matrix.cols);
|
|
matrix_out.at<float>(idx_y, idx_x) = matrix.at<float>(i,j);
|
|
}
|
|
}
|
|
return matrix_out;
|
|
}
|
|
|
|
Mat gaussian_shaped_labels(const float sigma, const int w, const int h)
|
|
{
|
|
// create 2D Gaussian peak, convert to Fourier space and stores it into the yf
|
|
Mat y = Mat::zeros(h, w, CV_32F);
|
|
float w2 = static_cast<float>(cvFloor(w / 2));
|
|
float h2 = static_cast<float>(cvFloor(h / 2));
|
|
|
|
// calculate for each pixel separatelly
|
|
for(int i=0; i<y.rows; i++) {
|
|
for(int j=0; j<y.cols; j++) {
|
|
y.at<float>(i,j) = (float)exp((-0.5 / pow(sigma, 2)) * (pow((i+1-h2), 2) + pow((j+1-w2), 2)));
|
|
}
|
|
}
|
|
// wrap-arround with the circulat shifting
|
|
y = circshift(y, -cvFloor(y.cols / 2), -cvFloor(y.rows / 2));
|
|
Mat yf;
|
|
dft(y, yf, DFT_COMPLEX_OUTPUT);
|
|
return yf;
|
|
}
|
|
|
|
std::vector<Mat> fourier_transform_features(const std::vector<Mat> &M)
|
|
{
|
|
std::vector<Mat> out(M.size());
|
|
Mat channel;
|
|
// iterate over channels and convert them to Fourier domain
|
|
for(size_t k = 0; k < M.size(); k++) {
|
|
M[k].convertTo(channel, CV_32F);
|
|
dft(channel, channel, DFT_COMPLEX_OUTPUT);
|
|
out[k] = (channel);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
Mat divide_complex_matrices(const Mat &A, const Mat &B)
|
|
{
|
|
std::vector<Mat> va,vb;
|
|
split(A, va);
|
|
split(B, vb);
|
|
|
|
Mat a = va.at(0);
|
|
Mat b = va.at(1);
|
|
Mat c = vb.at(0);
|
|
Mat d = vb.at(1);
|
|
|
|
Mat div = c.mul(c) + d.mul(d);
|
|
Mat real_part = (a.mul(c) + b.mul(d));
|
|
Mat im_part = (b.mul(c) - a.mul(d));
|
|
divide(real_part, div, real_part);
|
|
divide(im_part, div, im_part);
|
|
|
|
std::vector<Mat> tmp(2);
|
|
tmp[0] = real_part;
|
|
tmp[1] = im_part;
|
|
Mat res;
|
|
merge(tmp, res);
|
|
return res;
|
|
}
|
|
|
|
Mat get_subwindow(
|
|
const Mat &image,
|
|
const Point2f center,
|
|
const int w,
|
|
const int h,
|
|
Rect *valid_pixels)
|
|
{
|
|
int startx = cvFloor(center.x) + 1 - (cvFloor(w/2));
|
|
int starty = cvFloor(center.y) + 1 - (cvFloor(h/2));
|
|
Rect roi(startx, starty, w, h);
|
|
int padding_left = 0, padding_right = 0, padding_top = 0, padding_bottom = 0;
|
|
if(roi.x < 0) {
|
|
padding_left = -roi.x;
|
|
roi.x = 0;
|
|
}
|
|
if(roi.y < 0) {
|
|
padding_top = -roi.y;
|
|
roi.y = 0;
|
|
}
|
|
roi.width -= padding_left;
|
|
roi.height-= padding_top;
|
|
if(roi.x + roi.width >= image.cols) {
|
|
padding_right = roi.x + roi.width - image.cols;
|
|
roi.width = image.cols - roi.x;
|
|
}
|
|
if(roi.y + roi.height >= image.rows) {
|
|
padding_bottom = roi.y + roi.height - image.rows;
|
|
roi.height = image.rows - roi.y;
|
|
}
|
|
Mat subwin = image(roi).clone();
|
|
copyMakeBorder(subwin, subwin, padding_top, padding_bottom, padding_left, padding_right, BORDER_REPLICATE);
|
|
|
|
if(valid_pixels != NULL) {
|
|
*valid_pixels = Rect(padding_left, padding_top, roi.width, roi.height);
|
|
}
|
|
return subwin;
|
|
}
|
|
|
|
float subpixel_peak(const Mat &response, const std::string &s, const Point2f &p)
|
|
{
|
|
int i_p0, i_p_l, i_p_r; // indexes in response
|
|
float p0, p_l, p_r; // values in response
|
|
|
|
if(s.compare("vertical") == 0) {
|
|
// neighbouring rows
|
|
i_p0 = cvRound(p.y);
|
|
i_p_l = modul(cvRound(p.y) - 1, response.rows);
|
|
i_p_r = modul(cvRound(p.y) + 1, response.rows);
|
|
int px = static_cast<int>(p.x);
|
|
p0 = response.at<float>(i_p0, px);
|
|
p_l = response.at<float>(i_p_l, px);
|
|
p_r = response.at<float>(i_p_r, px);
|
|
} else if(s.compare("horizontal") == 0) {
|
|
// neighbouring cols
|
|
i_p0 = cvRound(p.x);
|
|
i_p_l = modul(cvRound(p.x) - 1, response.cols);
|
|
i_p_r = modul(cvRound(p.x) + 1, response.cols);
|
|
int py = static_cast<int>(p.y);
|
|
p0 = response.at<float>(py, i_p0);
|
|
p_l = response.at<float>(py, i_p_l);
|
|
p_r = response.at<float>(py, i_p_r);
|
|
} else {
|
|
std::cout << "Warning: unknown subpixel peak direction!" << std::endl;
|
|
return 0;
|
|
}
|
|
float delta = 0.5f * (p_r - p_l) / (2*p0 - p_r - p_l);
|
|
if(!std::isfinite(delta)) {
|
|
delta = 0;
|
|
}
|
|
|
|
return delta;
|
|
}
|
|
|
|
inline float chebpoly(const int n, const float x)
|
|
{
|
|
float res;
|
|
if (fabs(x) <= 1)
|
|
res = cos(n*acos(x));
|
|
else
|
|
res = cosh(n*acosh(x));
|
|
return res;
|
|
}
|
|
|
|
static Mat chebwin(int N, const float atten)
|
|
{
|
|
Mat out(N , 1, CV_32FC1);
|
|
int nn, i;
|
|
float M, n, sum = 0, max=0;
|
|
float tg = static_cast<float>(pow(10,atten/20.0f)); /* 1/r term [2], 10^gamma [2] */
|
|
float x0 = cosh((1.0f/(N-1))*acosh(tg));
|
|
M = (N-1)/2.0f;
|
|
if(N%2==0)
|
|
M = M + 0.5f; /* handle even length windows */
|
|
for(nn=0; nn<(N/2+1); nn++) {
|
|
n = nn-M;
|
|
sum = 0;
|
|
for(i=1; i<=M; i++){
|
|
sum += chebpoly(N-1,x0*static_cast<float>(cos(CV_PI*i/N))) *
|
|
static_cast<float>(cos(2.0f*n*CV_PI*i/N));
|
|
}
|
|
out.at<float>(nn,0) = tg + 2*sum;
|
|
out.at<float>(N-nn-1,0) = out.at<float>(nn,0) ;
|
|
if(out.at<float>(nn,0) > max)
|
|
max = out.at<float>(nn,0);
|
|
}
|
|
for(nn=0; nn<N; nn++)
|
|
out.at<float>(nn,0) /= max; /* normalize everything */
|
|
|
|
return out;
|
|
}
|
|
|
|
|
|
static double modified_bessel(int order, double x)
|
|
{
|
|
// sum m=0:inf 1/(m! * Gamma(m + order + 1)) * (x/2)^(2m + order)
|
|
const double eps = 1e-13;
|
|
double result = 0;
|
|
double m = 0;
|
|
double gamma = 1.0;
|
|
for(int i = 2; i <= order; ++i)
|
|
gamma *= i;
|
|
double term = pow(x,order) / (pow(2,order) * gamma);
|
|
|
|
while(term > eps * result) {
|
|
result += term;
|
|
//calculate new term in series
|
|
++m;
|
|
term *= (x*x) / (4*m*(m+order));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Mat get_hann_win(Size sz)
|
|
{
|
|
Mat hann_rows = Mat::ones(sz.height, 1, CV_32F);
|
|
Mat hann_cols = Mat::ones(1, sz.width, CV_32F);
|
|
int NN = sz.height - 1;
|
|
if(NN != 0) {
|
|
for (int i = 0; i < hann_rows.rows; ++i) {
|
|
hann_rows.at<float>(i,0) = (float)(1.0/2.0 * (1.0 - cos(2*CV_PI*i/NN)));
|
|
}
|
|
}
|
|
NN = sz.width - 1;
|
|
if(NN != 0) {
|
|
for (int i = 0; i < hann_cols.cols; ++i) {
|
|
hann_cols.at<float>(0,i) = (float)(1.0/2.0 * (1.0 - cos(2*CV_PI*i/NN)));
|
|
}
|
|
}
|
|
return hann_rows * hann_cols;
|
|
}
|
|
|
|
Mat get_kaiser_win(Size sz, float alpha)
|
|
{
|
|
Mat kaiser_rows = Mat::ones(sz.height, 1, CV_32F);
|
|
Mat kaiser_cols = Mat::ones(1, sz.width, CV_32F);
|
|
|
|
int N = sz.height - 1;
|
|
double shape = alpha;
|
|
double den = 1.0 / modified_bessel(0, shape);
|
|
|
|
for(int n = 0; n <= N; ++n) {
|
|
double K = (2.0 * n * 1.0/N) - 1.0;
|
|
double x = sqrt(1.0 - (K * K));
|
|
kaiser_rows.at<float>(n,0) = static_cast<float>(modified_bessel(0, shape * x) * den);
|
|
}
|
|
|
|
N = sz.width - 1;
|
|
for(int n = 0; n <= N; ++n) {
|
|
double K = (2.0 * n * 1.0/N) - 1.0;
|
|
double x = sqrt(1.0 - (K * K));
|
|
kaiser_cols.at<float>(0,n) = static_cast<float>(modified_bessel(0, shape * x) * den);
|
|
}
|
|
|
|
return kaiser_rows * kaiser_cols;
|
|
}
|
|
|
|
Mat get_chebyshev_win(Size sz, float attenuation)
|
|
{
|
|
Mat cheb_rows = chebwin(sz.height, attenuation);
|
|
Mat cheb_cols = chebwin(sz.width, attenuation).t();
|
|
return cheb_rows * cheb_cols;
|
|
}
|
|
|
|
static void computeHOG32D(const Mat &imageM, Mat &featM, const int sbin, const int pad_x, const int pad_y)
|
|
{
|
|
const int dimHOG = 32;
|
|
CV_Assert(pad_x >= 0);
|
|
CV_Assert(pad_y >= 0);
|
|
CV_Assert(imageM.channels() == 3);
|
|
CV_Assert(imageM.depth() == CV_64F);
|
|
|
|
// epsilon to avoid division by zero
|
|
const double eps = 0.0001;
|
|
// number of orientations
|
|
const int numOrient = 18;
|
|
// unit vectors to compute gradient orientation
|
|
const double uu[9] = {1.000, 0.9397, 0.7660, 0.5000, 0.1736, -0.1736, -0.5000, -0.7660, -0.9397};
|
|
const double vv[9] = {0.000, 0.3420, 0.6428, 0.8660, 0.9848, 0.9848, 0.8660, 0.6428, 0.3420};
|
|
|
|
// image size
|
|
const Size imageSize = imageM.size();
|
|
// block size
|
|
// int bW = cvRound((double)imageSize.width/(double)sbin);
|
|
// int bH = cvRound((double)imageSize.height/(double)sbin);
|
|
int bW = cvFloor((double)imageSize.width/(double)sbin);
|
|
int bH = cvFloor((double)imageSize.height/(double)sbin);
|
|
const Size blockSize(bW, bH);
|
|
// size of HOG features
|
|
int oW = max(blockSize.width-2, 0) + 2*pad_x;
|
|
int oH = max(blockSize.height-2, 0) + 2*pad_y;
|
|
Size outSize = Size(oW, oH);
|
|
// size of visible
|
|
const Size visible = blockSize*sbin;
|
|
|
|
// initialize historgram, norm, output feature matrices
|
|
Mat histM = Mat::zeros(Size(blockSize.width*numOrient, blockSize.height), CV_64F);
|
|
Mat normM = Mat::zeros(Size(blockSize.width, blockSize.height), CV_64F);
|
|
featM = Mat::zeros(Size(outSize.width*dimHOG, outSize.height), CV_64F);
|
|
|
|
// get the stride of each matrix
|
|
const size_t imStride = imageM.step1();
|
|
const size_t histStride = histM.step1();
|
|
const size_t normStride = normM.step1();
|
|
const size_t featStride = featM.step1();
|
|
|
|
// calculate the zero offset
|
|
const double* im = imageM.ptr<double>(0);
|
|
double* const hist = histM.ptr<double>(0);
|
|
double* const norm = normM.ptr<double>(0);
|
|
double* const feat = featM.ptr<double>(0);
|
|
|
|
for (int y = 1; y < visible.height - 1; y++)
|
|
{
|
|
for (int x = 1; x < visible.width - 1; x++)
|
|
{
|
|
// OpenCV uses an interleaved format: BGR-BGR-BGR
|
|
const double* s = im + 3*min(x, imageM.cols-2) + min(y, imageM.rows-2)*imStride;
|
|
|
|
// blue image channel
|
|
double dyb = *(s+imStride) - *(s-imStride);
|
|
double dxb = *(s+3) - *(s-3);
|
|
double vb = dxb*dxb + dyb*dyb;
|
|
|
|
// green image channel
|
|
s += 1;
|
|
double dyg = *(s+imStride) - *(s-imStride);
|
|
double dxg = *(s+3) - *(s-3);
|
|
double vg = dxg*dxg + dyg*dyg;
|
|
|
|
// red image channel
|
|
s += 1;
|
|
double dy = *(s+imStride) - *(s-imStride);
|
|
double dx = *(s+3) - *(s-3);
|
|
double v = dx*dx + dy*dy;
|
|
|
|
// pick the channel with the strongest gradient
|
|
if (vg > v) { v = vg; dx = dxg; dy = dyg; }
|
|
if (vb > v) { v = vb; dx = dxb; dy = dyb; }
|
|
|
|
// snap to one of the 18 orientations
|
|
double best_dot = 0;
|
|
int best_o = 0;
|
|
for (int o = 0; o < (int)numOrient/2; o++)
|
|
{
|
|
double dot = uu[o]*dx + vv[o]*dy;
|
|
if (dot > best_dot)
|
|
{
|
|
best_dot = dot;
|
|
best_o = o;
|
|
}
|
|
else if (-dot > best_dot)
|
|
{
|
|
best_dot = -dot;
|
|
best_o = o + (int)(numOrient/2);
|
|
}
|
|
}
|
|
|
|
// add to 4 historgrams around pixel using bilinear interpolation
|
|
double yp = ((double)y+0.5)/(double)sbin - 0.5;
|
|
double xp = ((double)x+0.5)/(double)sbin - 0.5;
|
|
int iyp = (int)cvFloor(yp);
|
|
int ixp = (int)cvFloor(xp);
|
|
double vy0 = yp - iyp;
|
|
double vx0 = xp - ixp;
|
|
double vy1 = 1.0 - vy0;
|
|
double vx1 = 1.0 - vx0;
|
|
v = sqrt(v);
|
|
|
|
// fill the value into the 4 neighborhood cells
|
|
if (iyp >= 0 && ixp >= 0)
|
|
*(hist + iyp*histStride + ixp*numOrient + best_o) += vy1*vx1*v;
|
|
|
|
if (iyp >= 0 && ixp+1 < blockSize.width)
|
|
*(hist + iyp*histStride + (ixp+1)*numOrient + best_o) += vx0*vy1*v;
|
|
|
|
if (iyp+1 < blockSize.height && ixp >= 0)
|
|
*(hist + (iyp+1)*histStride + ixp*numOrient + best_o) += vy0*vx1*v;
|
|
|
|
if (iyp+1 < blockSize.height && ixp+1 < blockSize.width)
|
|
*(hist + (iyp+1)*histStride + (ixp+1)*numOrient + best_o) += vy0*vx0*v;
|
|
|
|
} // for y
|
|
} // for x
|
|
|
|
// compute the energy in each block by summing over orientation
|
|
for (int y = 0; y < blockSize.height; y++)
|
|
{
|
|
const double* src = hist + y*histStride;
|
|
double* dst = norm + y*normStride;
|
|
double const* const dst_end = dst + blockSize.width;
|
|
// for each cell
|
|
while (dst < dst_end)
|
|
{
|
|
*dst = 0;
|
|
for (int o = 0; o < (int)(numOrient/2); o++)
|
|
{
|
|
*dst += (*src + *(src + numOrient/2))*
|
|
(*src + *(src + numOrient/2));
|
|
src++;
|
|
}
|
|
dst++;
|
|
src += numOrient/2;
|
|
}
|
|
}
|
|
|
|
// compute the features
|
|
for (int y = pad_y; y < outSize.height - pad_y; y++)
|
|
{
|
|
for (int x = pad_x; x < outSize.width - pad_x; x++)
|
|
{
|
|
double* dst = feat + y*featStride + x*dimHOG;
|
|
double* p, n1, n2, n3, n4;
|
|
const double* src;
|
|
|
|
p = norm + (y - pad_y + 1)*normStride + (x - pad_x + 1);
|
|
n1 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
|
p = norm + (y - pad_y)*normStride + (x - pad_x + 1);
|
|
n2 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
|
p = norm + (y- pad_y + 1)*normStride + x - pad_x;
|
|
n3 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
|
p = norm + (y - pad_y)*normStride + x - pad_x;
|
|
n4 = 1.0f / sqrt(*p + *(p + 1) + *(p + normStride) + *(p + normStride + 1) + eps);
|
|
|
|
double t1 = 0.0, t2 = 0.0, t3 = 0.0, t4 = 0.0;
|
|
|
|
// contrast-sesitive features
|
|
src = hist + (y - pad_y + 1)*histStride + (x - pad_x + 1)*numOrient;
|
|
for (int o = 0; o < numOrient; o++)
|
|
{
|
|
double val = *src;
|
|
double h1 = min(val*n1, 0.2);
|
|
double h2 = min(val*n2, 0.2);
|
|
double h3 = min(val*n3, 0.2);
|
|
double h4 = min(val*n4, 0.2);
|
|
*(dst++) = 0.5 * (h1 + h2 + h3 + h4);
|
|
|
|
src++;
|
|
t1 += h1;
|
|
t2 += h2;
|
|
t3 += h3;
|
|
t4 += h4;
|
|
}
|
|
|
|
// contrast-insensitive features
|
|
src = hist + (y - pad_y + 1)*histStride + (x - pad_x + 1)*numOrient;
|
|
for (int o = 0; o < numOrient/2; o++)
|
|
{
|
|
double sum = *src + *(src + numOrient/2);
|
|
double h1 = min(sum * n1, 0.2);
|
|
double h2 = min(sum * n2, 0.2);
|
|
double h3 = min(sum * n3, 0.2);
|
|
double h4 = min(sum * n4, 0.2);
|
|
*(dst++) = 0.5 * (h1 + h2 + h3 + h4);
|
|
src++;
|
|
}
|
|
|
|
// texture features
|
|
*(dst++) = 0.2357 * t1;
|
|
*(dst++) = 0.2357 * t2;
|
|
*(dst++) = 0.2357 * t3;
|
|
*(dst++) = 0.2357 * t4;
|
|
// truncation feature
|
|
*dst = 0;
|
|
}// for x
|
|
}// for y
|
|
// Truncation features
|
|
for (int m = 0; m < featM.rows; m++)
|
|
{
|
|
for (int n = 0; n < featM.cols; n += dimHOG)
|
|
{
|
|
if (m > pad_y - 1 && m < featM.rows - pad_y && n > pad_x*dimHOG - 1 && n < featM.cols - pad_x*dimHOG)
|
|
continue;
|
|
|
|
featM.at<double>(m, n + dimHOG - 1) = 1;
|
|
} // for x
|
|
}// for y
|
|
}
|
|
|
|
std::vector<Mat> get_features_hog(const Mat &im, const int bin_size)
|
|
{
|
|
Mat hogmatrix;
|
|
Mat im_;
|
|
im.convertTo(im_, CV_64FC3, 1.0/255.0);
|
|
computeHOG32D(im_,hogmatrix,bin_size,1,1);
|
|
hogmatrix.convertTo(hogmatrix, CV_32F);
|
|
Size hog_size = im.size();
|
|
hog_size.width /= bin_size;
|
|
hog_size.height /= bin_size;
|
|
Mat hogc(hog_size, CV_32FC(32), hogmatrix.data);
|
|
std::vector<Mat> features;
|
|
split(hogc, features);
|
|
return features;
|
|
}
|
|
|
|
std::vector<Mat> get_features_cn(const Mat &ppatch_data, const Size &output_size) {
|
|
Mat patch_data = ppatch_data.clone();
|
|
Vec3b & pixel = patch_data.at<Vec3b>(0,0);
|
|
unsigned index;
|
|
|
|
Mat cnFeatures = Mat::zeros(patch_data.rows,patch_data.cols,CV_32FC(10));
|
|
|
|
for(int i=0;i<patch_data.rows;i++){
|
|
for(int j=0;j<patch_data.cols;j++){
|
|
pixel=patch_data.at<Vec3b>(i,j);
|
|
index=(unsigned)(cvFloor((float)pixel[2]/8)+32*cvFloor((float)pixel[1]/8)+32*32*cvFloor((float)pixel[0]/8));
|
|
|
|
//copy the values
|
|
for(int k=0;k<10;k++){
|
|
cnFeatures.at<Vec<float,10> >(i,j)[k]=(float)ColorNames[index][k];
|
|
}
|
|
}
|
|
}
|
|
std::vector<Mat> result;
|
|
split(cnFeatures, result);
|
|
for (size_t i = 0; i < result.size(); i++) {
|
|
if (output_size.width > 0 && output_size.height > 0) {
|
|
resize(result.at(i), result.at(i), output_size, INTER_CUBIC);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::vector<Mat> get_features_rgb(const Mat &patch, const Size &output_size)
|
|
{
|
|
std::vector<Mat> channels;
|
|
split(patch, channels);
|
|
for(size_t k=0; k<channels.size(); k++) {
|
|
channels[k].convertTo(channels[k], CV_32F, 1.0/255.0, -0.5);
|
|
channels[k] = channels[k] - mean(channels[k])[0];
|
|
resize(channels[k], channels[k], output_size, INTER_CUBIC);
|
|
}
|
|
return channels;
|
|
}
|
|
|
|
double get_max(const Mat &m)
|
|
{
|
|
double val;
|
|
minMaxLoc(m, NULL, &val, NULL, NULL);
|
|
return val;
|
|
}
|
|
|
|
double get_min(const Mat &m)
|
|
{
|
|
double val;
|
|
minMaxLoc(m, &val, NULL, NULL, NULL);
|
|
return val;
|
|
}
|
|
|
|
Mat bgr2hsv(const Mat &img)
|
|
{
|
|
Mat hsv_img;
|
|
cvtColor(img, hsv_img, COLOR_BGR2HSV);
|
|
std::vector<Mat> hsv_img_channels;
|
|
split(hsv_img, hsv_img_channels);
|
|
hsv_img_channels.at(0).convertTo(hsv_img_channels.at(0), CV_8UC1, 255.0 / 180.0);
|
|
merge(hsv_img_channels, hsv_img);
|
|
return hsv_img;
|
|
}
|
|
|
|
} //cv namespace
|