some more refactoring
This commit is contained in:
parent
e16d89e8d6
commit
d27068f79a
@ -39,10 +39,14 @@
|
|||||||
//
|
//
|
||||||
//M*/
|
//M*/
|
||||||
|
|
||||||
|
#include "opencv2/core/base.hpp"
|
||||||
|
|
||||||
#ifndef __OPENCV_DENOISING_ARRAYS_HPP__
|
#ifndef __OPENCV_DENOISING_ARRAYS_HPP__
|
||||||
#define __OPENCV_DENOISING_ARRAYS_HPP__
|
#define __OPENCV_DENOISING_ARRAYS_HPP__
|
||||||
|
|
||||||
template <class T> struct Array2d {
|
template <class T>
|
||||||
|
struct Array2d
|
||||||
|
{
|
||||||
T* a;
|
T* a;
|
||||||
int n1,n2;
|
int n1,n2;
|
||||||
bool needToDeallocArray;
|
bool needToDeallocArray;
|
||||||
@ -50,14 +54,16 @@ template <class T> struct Array2d {
|
|||||||
Array2d(const Array2d& array2d):
|
Array2d(const Array2d& array2d):
|
||||||
a(array2d.a), n1(array2d.n1), n2(array2d.n2), needToDeallocArray(false)
|
a(array2d.a), n1(array2d.n1), n2(array2d.n2), needToDeallocArray(false)
|
||||||
{
|
{
|
||||||
if (array2d.needToDeallocArray) {
|
if (array2d.needToDeallocArray)
|
||||||
// copy constructor for self allocating arrays not supported
|
{
|
||||||
throw new std::exception();
|
CV_Error(Error::BadDataPtr, "Copy constructor for self allocating arrays not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Array2d(T* _a, int _n1, int _n2):
|
Array2d(T* _a, int _n1, int _n2):
|
||||||
a(_a), n1(_n1), n2(_n2), needToDeallocArray(false) {}
|
a(_a), n1(_n1), n2(_n2), needToDeallocArray(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Array2d(int _n1, int _n2):
|
Array2d(int _n1, int _n2):
|
||||||
n1(_n1), n2(_n2), needToDeallocArray(true)
|
n1(_n1), n2(_n2), needToDeallocArray(true)
|
||||||
@ -65,28 +71,34 @@ template <class T> struct Array2d {
|
|||||||
a = new T[n1*n2];
|
a = new T[n1*n2];
|
||||||
}
|
}
|
||||||
|
|
||||||
~Array2d() {
|
~Array2d()
|
||||||
if (needToDeallocArray) {
|
{
|
||||||
|
if (needToDeallocArray)
|
||||||
delete[] a;
|
delete[] a;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
T* operator [] (int i) {
|
T* operator [] (int i)
|
||||||
|
{
|
||||||
return a + i*n2;
|
return a + i*n2;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T* row_ptr(int i) {
|
inline T* row_ptr(int i)
|
||||||
|
{
|
||||||
return (*this)[i];
|
return (*this)[i];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> struct Array3d {
|
template <class T>
|
||||||
|
struct Array3d
|
||||||
|
{
|
||||||
T* a;
|
T* a;
|
||||||
int n1,n2,n3;
|
int n1,n2,n3;
|
||||||
bool needToDeallocArray;
|
bool needToDeallocArray;
|
||||||
|
|
||||||
Array3d(T* _a, int _n1, int _n2, int _n3):
|
Array3d(T* _a, int _n1, int _n2, int _n3):
|
||||||
a(_a), n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(false) {}
|
a(_a), n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
Array3d(int _n1, int _n2, int _n3):
|
Array3d(int _n1, int _n2, int _n3):
|
||||||
n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(true)
|
n1(_n1), n2(_n2), n3(_n3), needToDeallocArray(true)
|
||||||
@ -94,29 +106,34 @@ template <class T> struct Array3d {
|
|||||||
a = new T[n1*n2*n3];
|
a = new T[n1*n2*n3];
|
||||||
}
|
}
|
||||||
|
|
||||||
~Array3d() {
|
~Array3d()
|
||||||
if (needToDeallocArray) {
|
{
|
||||||
|
if (needToDeallocArray)
|
||||||
delete[] a;
|
delete[] a;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Array2d<T> operator [] (int i) {
|
Array2d<T> operator [] (int i)
|
||||||
|
{
|
||||||
Array2d<T> array2d(a + i*n2*n3, n2, n3);
|
Array2d<T> array2d(a + i*n2*n3, n2, n3);
|
||||||
return array2d;
|
return array2d;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T* row_ptr(int i1, int i2) {
|
inline T* row_ptr(int i1, int i2)
|
||||||
|
{
|
||||||
return a + i1*n2*n3 + i2*n3;
|
return a + i1*n2*n3 + i2*n3;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T> struct Array4d {
|
template <class T>
|
||||||
|
struct Array4d
|
||||||
|
{
|
||||||
T* a;
|
T* a;
|
||||||
int n1,n2,n3,n4;
|
int n1,n2,n3,n4;
|
||||||
bool needToDeallocArray;
|
bool needToDeallocArray;
|
||||||
int steps[4];
|
int steps[4];
|
||||||
|
|
||||||
void init_steps() {
|
void init_steps()
|
||||||
|
{
|
||||||
steps[0] = n2*n3*n4;
|
steps[0] = n2*n3*n4;
|
||||||
steps[1] = n3*n4;
|
steps[1] = n3*n4;
|
||||||
steps[2] = n4;
|
steps[2] = n4;
|
||||||
@ -136,22 +153,25 @@ template <class T> struct Array4d {
|
|||||||
init_steps();
|
init_steps();
|
||||||
}
|
}
|
||||||
|
|
||||||
~Array4d() {
|
~Array4d()
|
||||||
if (needToDeallocArray) {
|
{
|
||||||
|
if (needToDeallocArray)
|
||||||
delete[] a;
|
delete[] a;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Array3d<T> operator [] (int i) {
|
Array3d<T> operator [] (int i)
|
||||||
|
{
|
||||||
Array3d<T> array3d(a + i*n2*n3*n4, n2, n3, n4);
|
Array3d<T> array3d(a + i*n2*n3*n4, n2, n3, n4);
|
||||||
return array3d;
|
return array3d;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline T* row_ptr(int i1, int i2, int i3) {
|
inline T* row_ptr(int i1, int i2, int i3)
|
||||||
|
{
|
||||||
return a + i1*n2*n3*n4 + i2*n3*n4 + i3*n4;
|
return a + i1*n2*n3*n4 + i2*n3*n4 + i3*n4;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int step_size(int dimension) {
|
inline int step_size(int dimension)
|
||||||
|
{
|
||||||
return steps[dimension];
|
return steps[dimension];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -117,7 +117,8 @@ static void fastNlMeansDenoisingMultiCheckPreconditions(
|
|||||||
int templateWindowSize, int searchWindowSize)
|
int templateWindowSize, int searchWindowSize)
|
||||||
{
|
{
|
||||||
int src_imgs_size = static_cast<int>(srcImgs.size());
|
int src_imgs_size = static_cast<int>(srcImgs.size());
|
||||||
if (src_imgs_size == 0) {
|
if (src_imgs_size == 0)
|
||||||
|
{
|
||||||
CV_Error(Error::StsBadArg, "Input images vector should not be empty!");
|
CV_Error(Error::StsBadArg, "Input images vector should not be empty!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,12 +137,12 @@ static void fastNlMeansDenoisingMultiCheckPreconditions(
|
|||||||
"should be choosen corresponding srcImgs size!");
|
"should be choosen corresponding srcImgs size!");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < src_imgs_size; i++) {
|
for (int i = 1; i < src_imgs_size; i++)
|
||||||
if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type()) {
|
if (srcImgs[0].size() != srcImgs[i].size() || srcImgs[0].type() != srcImgs[i].type())
|
||||||
|
{
|
||||||
CV_Error(Error::StsBadArg, "Input images should have the same size and type!");
|
CV_Error(Error::StsBadArg, "Input images should have the same size and type!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
|
void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _dst,
|
||||||
int imgToDenoiseIndex, int temporalWindowSize,
|
int imgToDenoiseIndex, int temporalWindowSize,
|
||||||
@ -152,12 +153,13 @@ void cv::fastNlMeansDenoisingMulti( InputArrayOfArrays _srcImgs, OutputArray _ds
|
|||||||
|
|
||||||
fastNlMeansDenoisingMultiCheckPreconditions(
|
fastNlMeansDenoisingMultiCheckPreconditions(
|
||||||
srcImgs, imgToDenoiseIndex,
|
srcImgs, imgToDenoiseIndex,
|
||||||
temporalWindowSize, templateWindowSize, searchWindowSize
|
temporalWindowSize, templateWindowSize, searchWindowSize);
|
||||||
);
|
|
||||||
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
||||||
Mat dst = _dst.getMat();
|
Mat dst = _dst.getMat();
|
||||||
|
|
||||||
switch (srcImgs[0].type()) {
|
switch (srcImgs[0].type())
|
||||||
|
{
|
||||||
case CV_8U:
|
case CV_8U:
|
||||||
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
parallel_for_(cv::Range(0, srcImgs[0].rows),
|
||||||
FastNlMeansMultiDenoisingInvoker<uchar>(
|
FastNlMeansMultiDenoisingInvoker<uchar>(
|
||||||
@ -192,15 +194,15 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputAr
|
|||||||
|
|
||||||
fastNlMeansDenoisingMultiCheckPreconditions(
|
fastNlMeansDenoisingMultiCheckPreconditions(
|
||||||
srcImgs, imgToDenoiseIndex,
|
srcImgs, imgToDenoiseIndex,
|
||||||
temporalWindowSize, templateWindowSize, searchWindowSize
|
temporalWindowSize, templateWindowSize, searchWindowSize);
|
||||||
);
|
|
||||||
|
|
||||||
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
_dst.create(srcImgs[0].size(), srcImgs[0].type());
|
||||||
Mat dst = _dst.getMat();
|
Mat dst = _dst.getMat();
|
||||||
|
|
||||||
int src_imgs_size = static_cast<int>(srcImgs.size());
|
int src_imgs_size = static_cast<int>(srcImgs.size());
|
||||||
|
|
||||||
if (srcImgs[0].type() != CV_8UC3) {
|
if (srcImgs[0].type() != CV_8UC3)
|
||||||
|
{
|
||||||
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
|
CV_Error(Error::StsBadArg, "Type of input images should be CV_8UC3!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -211,7 +213,8 @@ void cv::fastNlMeansDenoisingColoredMulti( InputArrayOfArrays _srcImgs, OutputAr
|
|||||||
std::vector<Mat> src_lab(src_imgs_size);
|
std::vector<Mat> src_lab(src_imgs_size);
|
||||||
std::vector<Mat> l(src_imgs_size);
|
std::vector<Mat> l(src_imgs_size);
|
||||||
std::vector<Mat> ab(src_imgs_size);
|
std::vector<Mat> ab(src_imgs_size);
|
||||||
for (int i = 0; i < src_imgs_size; i++) {
|
for (int i = 0; i < src_imgs_size; i++)
|
||||||
|
{
|
||||||
src_lab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC3);
|
src_lab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC3);
|
||||||
l[i] = Mat::zeros(srcImgs[0].size(), CV_8UC1);
|
l[i] = Mat::zeros(srcImgs[0].size(), CV_8UC1);
|
||||||
ab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC2);
|
ab[i] = Mat::zeros(srcImgs[0].size(), CV_8UC2);
|
||||||
|
@ -101,7 +101,7 @@ inline int getNearestPowerOf2(int value)
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
FastNlMeansDenoisingInvoker<T>::FastNlMeansDenoisingInvoker(
|
FastNlMeansDenoisingInvoker<T>::FastNlMeansDenoisingInvoker(
|
||||||
const cv::Mat& src, cv::Mat& dst,
|
const Mat& src, Mat& dst,
|
||||||
int template_window_size,
|
int template_window_size,
|
||||||
int search_window_size,
|
int search_window_size,
|
||||||
const float h) :
|
const float h) :
|
||||||
@ -115,15 +115,13 @@ FastNlMeansDenoisingInvoker<T>::FastNlMeansDenoisingInvoker(
|
|||||||
search_window_size_ = search_window_half_size_ * 2 + 1;
|
search_window_size_ = search_window_half_size_ * 2 + 1;
|
||||||
|
|
||||||
border_size_ = search_window_half_size_ + template_window_half_size_;
|
border_size_ = search_window_half_size_ + template_window_half_size_;
|
||||||
copyMakeBorder(src_, extended_src_,
|
copyMakeBorder(src_, extended_src_, border_size_, border_size_, border_size_, border_size_, BORDER_DEFAULT);
|
||||||
border_size_, border_size_, border_size_, border_size_, cv::BORDER_DEFAULT);
|
|
||||||
|
|
||||||
const int max_estimate_sum_value = search_window_size_ * search_window_size_ * 255;
|
const int max_estimate_sum_value = search_window_size_ * search_window_size_ * 255;
|
||||||
fixed_point_mult_ = std::numeric_limits<int>::max() / max_estimate_sum_value;
|
fixed_point_mult_ = std::numeric_limits<int>::max() / max_estimate_sum_value;
|
||||||
|
|
||||||
// precalc weight for every possible l2 dist between blocks
|
// precalc weight for every possible l2 dist between blocks
|
||||||
// additional optimization of precalced weights to replace division(averaging) by binary shift
|
// additional optimization of precalced weights to replace division(averaging) by binary shift
|
||||||
|
|
||||||
CV_Assert(template_window_size_ <= 46340); // sqrt(INT_MAX)
|
CV_Assert(template_window_size_ <= 46340); // sqrt(INT_MAX)
|
||||||
int template_window_size_sq = template_window_size_ * template_window_size_;
|
int template_window_size_sq = template_window_size_ * template_window_size_;
|
||||||
almost_template_window_size_sq_bin_shift_ = getNearestPowerOf2(template_window_size_sq);
|
almost_template_window_size_sq_bin_shift_ = getNearestPowerOf2(template_window_size_sq);
|
||||||
@ -157,12 +155,14 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
int row_from = range.start;
|
int row_from = range.start;
|
||||||
int row_to = range.end - 1;
|
int row_to = range.end - 1;
|
||||||
|
|
||||||
|
// sums of cols anf rows for current pixel p
|
||||||
Array2d<int> dist_sums(search_window_size_, search_window_size_);
|
Array2d<int> dist_sums(search_window_size_, search_window_size_);
|
||||||
|
|
||||||
// for lazy calc optimization
|
// for lazy calc optimization (sum of cols for current pixel)
|
||||||
Array3d<int> col_dist_sums(template_window_size_, search_window_size_, search_window_size_);
|
Array3d<int> col_dist_sums(template_window_size_, search_window_size_, search_window_size_);
|
||||||
|
|
||||||
int first_col_num = -1;
|
int first_col_num = -1;
|
||||||
|
// last elements of column sum (for each element in row)
|
||||||
Array3d<int> up_col_dist_sums(src_.cols, search_window_size_, search_window_size_);
|
Array3d<int> up_col_dist_sums(src_.cols, search_window_size_, search_window_size_);
|
||||||
|
|
||||||
for (int i = row_from; i <= row_to; i++)
|
for (int i = row_from; i <= row_to; i++)
|
||||||
@ -177,7 +177,6 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
{
|
{
|
||||||
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
|
calcDistSumsForFirstElementInRow(i, dist_sums, col_dist_sums, up_col_dist_sums);
|
||||||
first_col_num = 0;
|
first_col_num = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -186,7 +185,6 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
{
|
{
|
||||||
calcDistSumsForElementInFirstRow(i, j, first_col_num,
|
calcDistSumsForElementInFirstRow(i, j, first_col_num,
|
||||||
dist_sums, col_dist_sums, up_col_dist_sums);
|
dist_sums, col_dist_sums, up_col_dist_sums);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -205,7 +203,6 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
for (int y = 0; y < search_window_size; y++)
|
for (int y = 0; y < search_window_size; y++)
|
||||||
{
|
{
|
||||||
int * dist_sums_row = dist_sums.row_ptr(y);
|
int * dist_sums_row = dist_sums.row_ptr(y);
|
||||||
|
|
||||||
int * col_dist_sums_row = col_dist_sums.row_ptr(first_col_num, y);
|
int * col_dist_sums_row = col_dist_sums.row_ptr(first_col_num, y);
|
||||||
int * up_col_dist_sums_row = up_col_dist_sums.row_ptr(j, y);
|
int * up_col_dist_sums_row = up_col_dist_sums.row_ptr(j, y);
|
||||||
|
|
||||||
@ -214,19 +211,14 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
|
|
||||||
for (int x = 0; x < search_window_size; x++)
|
for (int x = 0; x < search_window_size; x++)
|
||||||
{
|
{
|
||||||
|
// remove from current pixel sum column sum with index "first_col_num"
|
||||||
dist_sums_row[x] -= col_dist_sums_row[x];
|
dist_sums_row[x] -= col_dist_sums_row[x];
|
||||||
|
|
||||||
col_dist_sums_row[x] =
|
int bx = start_bx + x;
|
||||||
up_col_dist_sums_row[x] +
|
col_dist_sums_row[x] = up_col_dist_sums_row[x] + calcUpDownDist(a_up, a_down, b_up_ptr[bx], b_down_ptr[bx]);
|
||||||
calcUpDownDist(
|
|
||||||
a_up, a_down,
|
|
||||||
b_up_ptr[start_bx + x], b_down_ptr[start_bx + x]
|
|
||||||
);
|
|
||||||
|
|
||||||
dist_sums_row[x] += col_dist_sums_row[x];
|
dist_sums_row[x] += col_dist_sums_row[x];
|
||||||
|
|
||||||
up_col_dist_sums_row[x] = col_dist_sums_row[x];
|
up_col_dist_sums_row[x] = col_dist_sums_row[x];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,9 +227,7 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calc weights
|
// calc weights
|
||||||
int weights_sum = 0;
|
int estimation[3], weights_sum = 0;
|
||||||
|
|
||||||
int estimation[3];
|
|
||||||
for (size_t channel_num = 0; channel_num < sizeof(T); channel_num++)
|
for (size_t channel_num = 0; channel_num < sizeof(T); channel_num++)
|
||||||
estimation[channel_num] = 0;
|
estimation[channel_num] = 0;
|
||||||
|
|
||||||
@ -247,9 +237,7 @@ void FastNlMeansDenoisingInvoker<T>::operator() (const Range& range) const
|
|||||||
int* dist_sums_row = dist_sums.row_ptr(y);
|
int* dist_sums_row = dist_sums.row_ptr(y);
|
||||||
for (int x = 0; x < search_window_size_; x++)
|
for (int x = 0; x < search_window_size_; x++)
|
||||||
{
|
{
|
||||||
int almostAvgDist =
|
int almostAvgDist = dist_sums_row[x] >> almost_template_window_size_sq_bin_shift_;
|
||||||
dist_sums_row[x] >> almost_template_window_size_sq_bin_shift_;
|
|
||||||
|
|
||||||
int weight = almost_dist2weight_[almostAvgDist];
|
int weight = almost_dist2weight_[almostAvgDist];
|
||||||
weights_sum += weight;
|
weights_sum += weight;
|
||||||
|
|
||||||
@ -302,9 +290,7 @@ inline void FastNlMeansDenoisingInvoker<T>::calcDistSumsForFirstElementInRow(
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
inline void FastNlMeansDenoisingInvoker<T>::calcDistSumsForElementInFirstRow(
|
inline void FastNlMeansDenoisingInvoker<T>::calcDistSumsForElementInFirstRow(
|
||||||
int i,
|
int i, int j, int first_col_num,
|
||||||
int j,
|
|
||||||
int first_col_num,
|
|
||||||
Array2d<int>& dist_sums,
|
Array2d<int>& dist_sums,
|
||||||
Array3d<int>& col_dist_sums,
|
Array3d<int>& col_dist_sums,
|
||||||
Array3d<int>& up_col_dist_sums) const
|
Array3d<int>& up_col_dist_sums) const
|
||||||
@ -326,8 +312,7 @@ inline void FastNlMeansDenoisingInvoker<T>::calcDistSumsForElementInFirstRow(
|
|||||||
int by = start_by + y;
|
int by = start_by + y;
|
||||||
int bx = start_bx + x;
|
int bx = start_bx + x;
|
||||||
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
for (int ty = -template_window_half_size_; ty <= template_window_half_size_; ty++)
|
||||||
col_dist_sums[new_last_col_num][y][x] +=
|
col_dist_sums[new_last_col_num][y][x] += calcDist<T>(extended_src_, ay + ty, ax, by + ty, bx);
|
||||||
calcDist<T>(extended_src_, ay + ty, ax, by + ty, bx);
|
|
||||||
|
|
||||||
dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x];
|
dist_sums[y][x] += col_dist_sums[new_last_col_num][y][x];
|
||||||
up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x];
|
up_col_dist_sums[j][y][x] = col_dist_sums[new_last_col_num][y][x];
|
||||||
|
Loading…
x
Reference in New Issue
Block a user