Commit 7d13acdb authored by Muresan Mircea Paul's avatar Muresan Mircea Paul

added asserts

removed unnecessary code
modified enums and other

removed class descriptor.cpp

removed white spaces and fixed warnings

Changed to kernel
parent 37478fc5
......@@ -41,50 +41,75 @@
//the use of this software, even if advised of the possibility of such damage.
/*****************************************************************************************************************\
* The interface contains the main descriptors that will be implemented in the descriptor class *
* The file contains the implemented descriptors *
\******************************************************************************************************************/
#include "descriptor.hpp"
using namespace cv;
using namespace stereo;
Descriptor::Descriptor()
{
}
//!Implementation for computing the Census transform on the given image
void Descriptor::applyCensusOnImage(const cv::Mat &img, int kernelSize, cv::Mat &dist, const int type)
void cv::stereo::applyCensusOnImage(const cv::Mat &img, int kernelSize, cv::Mat &dist, const int type)
{
CV_Assert(img.type() == CV_8UC1);
CV_Assert(kernelSize <= 5);
CV_Assert(type < 2 && type >= 0);
int n2 = (kernelSize - 1) / 2;
parallel_for_(cv::Range(n2, img.rows - n2), singleImageCensus(img.data, img.cols, img.rows, n2, (int *)dist.data, type));
}
/**
Two variations of census applied on input images
Implementation of a census transform which is taking into account just the some pixels from the census kernel thus allowing for larger block sizes
**/
void Descriptor::applyCensusOnImages(const cv::Mat &im1, cv::Mat &im2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type)
void cv::stereo::applyCensusOnImages(const cv::Mat &im1,const cv::Mat &im2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type)
{
CV_Assert(im1.size() == im2.size());
CV_Assert(im1.type() == CV_8UC1 && im2.type() == CV_8UC1);
CV_Assert(type < 2 && type >= 0);
CV_Assert(kernelSize <= (type == 0 ? 5 : 10));
int n2 = (kernelSize - 1) / 2;
parallel_for_(cv::Range(n2, im1.rows - n2), parallelCensus(im1.data, im2.data, im1.cols, im2.rows, n2, (int *)dist.data, (int *)dist2.data, type));
if(type == Dense_Census)
{
parallel_for_(cv::Range(n2, im1.rows - n2),
CombinedDescriptor<1,1,1,CensusKernel>(im1.cols, im1.rows,n2,(int *)dist.data,(int *)dist2.data,CensusKernel(im1.data, im2.data),n2));
}
else if(type == Sparse_Census)
{
parallel_for_(cv::Range(n2, im1.rows - n2),
CombinedDescriptor<2,2,1,CensusKernel>(im1.cols, im1.rows,n2,(int *)dist.data,(int *)dist2.data,CensusKernel(im1.data, im2.data),n2));
}
}
/**
STANDARD_MCT - Modified census which is memorizing for each pixel 2 bits and includes a tolerance to the pixel comparison
MCT_MEAN_VARIATION - Implementation of a modified census transform which is also taking into account the variation to the mean of the window not just the center pixel
**/
void Descriptor::applyMCTOnImages(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, int t, cv::Mat &dist, cv::Mat &dist2, const int type)
void cv::stereo::applyMCTOnImages(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, int t, cv::Mat &dist, cv::Mat &dist2, const int type)
{
CV_Assert(img1.size() == img2.size());
CV_Assert(img1.type() == CV_8UC1 && img2.type() == CV_8UC1);
CV_Assert(type < 2 && type >= 0);
CV_Assert(kernelSize <= 9);
int n2 = (kernelSize - 1) >> 1;
parallel_for_(cv::Range(n2, img1.rows - n2), parallelMctDescriptor(img1.data, img2.data, img1.cols, img2.rows, n2,t, (int *)dist.data, (int *)dist2.data, type));
if(type == StandardMct)
{
parallel_for_(cv::Range(n2, img1.rows - n2),
CombinedDescriptor<2,3,2,MCTKernel>(img1.cols, img1.rows,n2,(int *)dist.data,(int *)dist2.data,MCTKernel(img1.data, img2.data,t),n2));
}
else
{
//MV
}
}
/**The classical center symetric census
A modified version of cs census which is comparing a pixel with its correspondent after the center
**/
void Descriptor::applySimetricCensus(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type)
void cv::stereo::applySimetricCensus(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type)
{
CV_Assert(img1.size() == img2.size());
CV_Assert(img1.type() == CV_8UC1 && img2.type() == CV_8UC1);
CV_Assert(type < 2 && type >= 0);
CV_Assert(kernelSize <= 7);
int n2 = (kernelSize - 1) >> 1;
if(type == ClassicCenterSymetricCensus)
{
parallel_for_(cv::Range(n2, img1.rows - n2), parallelSymetricCensus(img1.data, img2.data, img1.cols, img2.rows, n2, (int *)dist.data, (int *)dist2.data, type));
}
else if(type == ModifiedCenterSymetricCensus)
{
parallel_for_(cv::Range(n2, img1.rows - n2),
CombinedDescriptor<1,1,1,ModifiedCsCensus>(img1.cols, img1.rows,n2,(int *)dist.data,(int *)dist2.data,ModifiedCsCensus(img1.data, img2.data,n2),1));
}
}
//!brief binary descriptor used in stereo correspondence
void Descriptor::applyBrifeDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2)
void cv::stereo::applyBrifeDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2)
{
//TO DO
//marked the variables in order to avoid warnings
......@@ -94,8 +119,7 @@ void Descriptor::applyBrifeDescriptor(const cv::Mat &image1, const cv::Mat &imag
(void)dist2;
(void)kernelSize;
}
//The classical Rank Transform
void Descriptor::applyRTDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2)
void cv::stereo::applyRTDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2)
{
//TO DO
//marked the variables in order to avoid warnings
......@@ -105,7 +129,3 @@ void Descriptor::applyRTDescriptor(const cv::Mat &image1, const cv::Mat &image2
(void)dist2;
(void)kernelSize;
}
Descriptor::~Descriptor(void)
{
}
......@@ -54,219 +54,206 @@ namespace cv
{
namespace stereo
{
enum ClassicCensus { Dense_Census, Sparse_Census};
enum SymetricCensus {ClassicCenterSymetricCensus, ModifiedCenterSymetricCensus};
enum MCT {StandardMct,MeanVariation};
enum CensusImage {SSE, NonSSE};
//!class implemented to run the census descriptor in parralel
class parallelCensus :public ParallelLoopBody
enum { Dense_Census, Sparse_Census, StarCensus};
enum {ClassicCenterSymetricCensus, ModifiedCenterSymetricCensus};
enum {StandardMct,MeanVariation};
enum {SSE, NonSSE};
//!Mean Variation is a robust kernel that compares a pixel
//!not just with the center but also with the mean of the window
struct MVKernel
{
uint8_t *image1;
uint8_t *image2;
uint8_t *integralLeft;
uint8_t *integralRight;
MVKernel(uint8_t *img, uint8_t *img2, uint8_t *integralL, uint8_t *integralR): image1(img),image2(img2),integralLeft(integralL), integralRight(integralR){}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int &c, int &c2) const
{
private:
uint8_t *image1, *image2;
int *dst1, *dst2;
int n2, width, height, type;
public:
parallelCensus(uint8_t * img1, uint8_t * img2, int w, int h, int k2, int * distance1, int * distance2,const int t) :
image1(img1), image2(img2), dst1(distance1), dst2(distance2), n2(k2), width(w), height(h), type(t){}
virtual void operator()(const cv::Range &r) const {
int step = (type == ClassicCensus::Sparse_Census)? 2:1;
for (int i = r.start; i <= r.end ; i++)
}
};
//!kernel that takes the pixels from certain positions from a patch
//!offers verry good results
struct StarKernel
{
int rWidth = i * width;
for (int j = n2; j <= width - n2; j++)
uint8_t *image1;
uint8_t *image2;
StarKernel(uint8_t *img, uint8_t *img2): image1(img),image2(img2){}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int &c, int &c2) const
{
//imitialize the costs for the 2 census descriptors
int c = 0;
int c2 = 0;
for (int ii = i - n2; ii <= i + n2; ii+=step)
}
};
//!Compares pixels from a patch giving high weights to pixels in which
//!the intensity is higher. The other pixels receive a lower weight
struct MCTKernel
{
int rrWidth = ii * width;
for (int jj = j - n2; jj <= j + n2; jj+=step)
uint8_t *image1;
uint8_t *image2;
int t;
MCTKernel(uint8_t * img,uint8_t *img2, int threshold) : image1(img),image2(img2), t(threshold) {}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int &c, int &c2) const
{
if (ii != i || jj != j)
if (image1[rrWidth + jj] > image1[rWidth + j] - t)
{
//compare a pixel with the center from the kernel
if (image1[rrWidth + jj] > image1[rWidth + j])
c <<= 2;
c |= 0x3;
}
else if (image1[rWidth + j] - t < image1[rrWidth + jj] && image1[rWidth + j] + t >= image1[rrWidth + jj])
{
c <<= 2;
c = c + 1;
}
c = c * 2;
else
{
c <<= 2;
}
if (ii != i || jj != j)
if (image2[rrWidth + jj] > image2[rWidth + j] - t)
{
//compare pixel with center for image 2
if (image2[rrWidth + jj] > image2[rWidth + j])
c2 <<= 2;
c2 |= 0x3;
}
else if (image2[rWidth + j] - t < image2[rrWidth + jj] && image2[rWidth + j] + t >= image2[rrWidth + jj])
{
c2 <<= 2;
c2 = c2 + 1;
}
c2 = c2 * 2;
}
}
}
dst1[(rWidth + j)] = c;
dst2[(rWidth + j)] = c2;
}
else
{
c2 <<= 2;
}
}
};
//!class that implemented the census descriptor on single images
class singleImageCensus : public ParallelLoopBody
//!A madified cs census that compares a pixel with the imediat neightbour starting
//!from the center
struct ModifiedCsCensus
{
private:
uint8_t *image;
int *dst;
int n2, width, height, type;
public:
singleImageCensus(uint8_t * img1, int w, int h, int k2, int * distance1,const int t) :
image(img1), dst(distance1), n2(k2), width(w), height(h), type(t){}
virtual void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
uint8_t *image1;
uint8_t *image2;
int n2;
ModifiedCsCensus(uint8_t *im1, uint8_t *im2, int ker):image1(im1),image2(im2),n2(ker){}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int &c, int &c2) const
{
int rWidth = i * width;
for (int j = n2; j <= width - n2; j++)
{
if (type == CensusImage::SSE)
if (image1[(rrWidth + jj)] > image1[(w2 + (jj + n2))])
{
//to do
c = c + 1;
}
else
{
int c = 0;
for (int ii = i - n2; ii <= i + n2; ii++)
c = c * 2;
if (image2[(rrWidth + jj)] > image2[(w2 + (jj + n2))])
{
int rrWidth = ii * width;
for (int jj = j - n2; jj <= j + n2; jj++)
c2 = c2 + 1;
}
c2 = c2 * 2;
}
};
//!A kernel in which a pixel is compared with the center of the window
struct CensusKernel
{
if (ii != i || jj != j)
uint8_t *image1;
uint8_t *image2;
CensusKernel(uint8_t *im1, uint8_t *im2):image1(im1),image2(im2){}
void operator()(int rrWidth,int w2, int rWidth, int jj, int j, int &c, int &c2) const
{
if (image[(rrWidth + jj)] > image[(rWidth + j)])
//compare a pixel with the center from the kernel
if (image1[rrWidth + jj] > image1[rWidth + j])
{
c = c + 1;
}
c = c * 2;
//compare pixel with center for image 2
if (image2[rrWidth + jj] > image2[rWidth + j])
{
c2 = c2 + 1;
}
}
}
dst[(rWidth + j)] = c;
}
}
}
c2 = c2 * 2;
}
};
//! parallel implementation of MCT type of descriptors
class parallelMctDescriptor:public ParallelLoopBody
//template clas which efficiently combines the descriptors
template <int step_start, int step_end, int step_inc, typename Kernel>
class CombinedDescriptor:public ParallelLoopBody
{
private:
uint8_t *image1, *image2;
int *dst1, *dst2;
int n2,t , width, height, type;
int n2 , width, height;
int n2_stop;
Kernel kernel_;
public:
parallelMctDescriptor(uint8_t * img1, uint8_t * img2, int w, int h, int k2,int threshold, int * distance1, int * distance2,const int tip) :
image1(img1), image2(img2), dst1(distance1), dst2(distance2), n2(k2), t(threshold), width(w), height(h), type(tip){}
virtual void operator()(const cv::Range &r) const {
CombinedDescriptor(int w, int h, int k2, int * distance1, int * distance2, Kernel kernel,int k2Stop) :
width(w), height(h), n2(k2),dst1(distance1), dst2(distance2), kernel_(kernel), n2_stop(k2Stop){}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int rWidth = i * width;
int distV = (i)* width;
for (int j = n2 + 2; j <= width - n2 - 2; j++)
{
int c = 0;
int c2 = 0;
if (type == MCT::StandardMct)
for(int step = step_start; step <= step_end; step += step_inc)
{
for (int ii = i - n2; ii <= i + n2; ii += 2)
for (int ii = - n2; ii <= + n2_stop; ii += step)
{
int rrWidth = ii * width;
for (int jj = j - n2; jj <= j + n2; jj += 2)
int rrWidth = (ii + i) * width;
int rrWidthC = (ii + i + n2) * width;
for (int jj = j - n2; jj <= j + n2; jj += step)
{
if (ii != i || jj != j)
{
if (image1[rrWidth + jj] > image1[rWidth + j] - t)
{
c <<= 2;
c |= 0x3;
kernel_(rrWidth,rrWidthC, rWidth, jj, j, c,c2);
}
else if (image1[rWidth + j] - t < image1[rrWidth + jj] && image1[rWidth + j] + t >= image1[rrWidth + jj])
{
c <<= 2;
c = c + 1;
}
else
{
c <<= 2;
}
}
if (ii != i || jj != j)
{
if (image2[rrWidth + jj] > image2[rWidth + j] - t)
{
c2 <<= 2;
c2 |= 0x3;
}
else if (image2[rWidth + j] - t < image2[rrWidth + jj] && image2[rWidth + j] + t >= image2[rrWidth + jj])
{
c2 <<= 2;
c2 = c2 + 1;
}
else
{
c2 <<= 2;
}
dst1[rWidth + j] = c;
dst2[rWidth + j] = c2;
}
}
}
for (int ii = i - n2; ii <= i + n2; ii += 4)
{
int rrWidth = ii * width;
for (int jj = j - n2; jj <= j + n2; jj += 4)
};
//!class that implemented the census descriptor on single images
class singleImageCensus : public ParallelLoopBody
{
if (ii != i || jj != j)
private:
uint8_t *image;
int *dst;
int n2, width, height, type;
public:
singleImageCensus(uint8_t * img1, int w, int h, int k2, int * distance1,const int t) :
image(img1), dst(distance1), n2(k2), width(w), height(h), type(t){}
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
if (image1[rrWidth + jj] > image1[rWidth + j] - t)
int rWidth = i * width;
for (int j = n2; j <= width - n2; j++)
{
c <<= 2;
c |= 0x3;
}
else if (image1[rWidth + j] - t < image1[rrWidth + jj] && image1[rWidth + j] + t >= image1[rrWidth + jj])
if (type == SSE)
{
c <<= 2;
c += 1;
//to do
}
else
{
c <<= 2;
}
}
if (ii != i || jj != j)
int c = 0;
for (int ii = i - n2; ii <= i + n2; ii++)
{
if (image2[rrWidth + jj] > image2[rWidth + j] - t)
int rrWidth = ii * width;
for (int jj = j - n2; jj <= j + n2; jj++)
{
c2 <<= 2;
c2 |= 0x3;
}
else if (image2[rWidth + j] - t < image2[rrWidth + jj] && image2[rWidth + j] + t >= image2[rrWidth + jj])
if (ii != i || jj != j)
{
c2 <<= 2;
c2 = c2 + 1;
}
else
if (image[(rrWidth + jj)] > image[(rWidth + j)])
{
c2 <<= 2;
}
c = c + 1;
}
c = c * 2;
}
}
}
else if (type == MCT::MeanVariation)
{
//to do mean variation
dst[(rWidth + j)] = c;
}
dst1[distV + j] = c;
dst2[distV + j] = c2;
}
}
}
};
//!paralel implementation of the center symetric census
class parallelSymetricCensus:public ParallelLoopBody
......@@ -278,9 +265,7 @@ namespace cv
public:
parallelSymetricCensus(uint8_t * img1, uint8_t * img2, int w, int h, int k2, int * distance1, int * distance2,const int t) :
image1(img1), image2(img2), dst1(distance1), dst2(distance2), n2(k2), width(w), height(h), type(t){}
virtual void operator()(const cv::Range &r) const {
void operator()(const cv::Range &r) const {
for (int i = r.start; i <= r.end ; i++)
{
int distV = (i)* width;
......@@ -289,23 +274,17 @@ namespace cv
int c = 0;
int c2 = 0;
//the classic center symetric census which compares the curent pixel with its symetric not its center.
if (type == SymetricCensus::ClassicCenterSymetricCensus)
{
for (int ii = -n2; ii < 0; ii++)
{
int rrWidth = (ii + i) * width;
for (int jj = -n2; jj <= +n2; jj++)
{
if (ii != i || jj != j)
{
if (image1[(rrWidth + (jj + j))] > image1[((ii * (-1) + i) * width + (-1 * jj) + j)])
{
c = c + 1;
}
c = c * 2;
}
if (ii != i || jj != j)
{
if (image2[(rrWidth + (jj + j))] > image2[((ii * (-1) + i) * width + (-1 * jj) + j)])
{
c2 = c2 + 1;
......@@ -313,7 +292,6 @@ namespace cv
c2 = c2 * 2;
}
}
}
for (int jj = -n2; jj < 0; jj++)
{
if (image1[(i * width + (jj + j))] > image1[(i * width + (-1 * jj) + j)])
......@@ -326,63 +304,34 @@ namespace cv
c2 = c2 + 1;
}
c2 = c2 * 2;
}
}//a modified version of cs census which compares each pixel with its correspondent from
//the same distance from the center
else if (type == SymetricCensus::ModifiedCenterSymetricCensus)
{
for (int ii = i - n2; ii <= i + 1; ii++)
{
int rrWidth = ii * width;
int rrWidthC = (ii + n2) * width;
for (int jj = j - n2; jj <= j + n2; jj += 2)
{
if (ii != i || jj != j)
{
if (image1[(rrWidth + jj)] > image1[(rrWidthC + (jj + n2))])
{
c = c + 1;
}
c = c * 2;
}
if (ii != i || jj != j)
{
if (image2[(rrWidth + jj)] > image2[(rrWidthC + (jj + n2))])
{
c2 = c2 + 1;
}
c2 = c2 * 2;
}
}
}
}
dst1[(distV + j)] = c;
dst2[(distV + j)] = c2;
}
}
}
};
class Descriptor
{
public:
//Implementation for computing the Census transform on the given image
void applyCensusOnImage(const cv::Mat &image, int kernelSize, cv::Mat &dist, const int type = 0);
//two variations of census applied on input images
//Implementation of a census transform which is taking into account just the some pixels from the census kernel thus allowing for larger block sizes
void applyCensusOnImages(const cv::Mat &image1, cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type = ClassicCensus::Sparse_Census);
// STANDARD_MCT - Modified census which is memorizing for each pixel 2 bits and includes a tolerance to the pixel comparison
//MCT_MEAN_VARIATION - Implementation of a modified census transform which is also taking into account the variation to the mean of the window not just the center pixel
void applyMCTOnImages(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, int t, cv::Mat &dist, cv::Mat &dist2, const int type = MCT::StandardMct);
//The classical center symetric census
//A modified version of cs census which is comparing the a pixel with its correspondent from the after the center
void applySimetricCensus(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type = SymetricCensus::ClassicCenterSymetricCensus);
//The brief binary descriptor
//!Implementation for computing the Census transform on the given image
void applyCensusOnImage(const cv::Mat &img, int kernelSize, cv::Mat &dist, const int type);
/**
Two variations of census applied on input images
Implementation of a census transform which is taking into account just the some pixels from the census kernel thus allowing for larger block sizes
**/
void applyCensusOnImages(const cv::Mat &im1,const cv::Mat &im2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type);
/**
STANDARD_MCT - Modified census which is memorizing for each pixel 2 bits and includes a tolerance to the pixel comparison
MCT_MEAN_VARIATION - Implementation of a modified census transform which is also taking into account the variation to the mean of the window not just the center pixel
**/
void applyMCTOnImages(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, int t, cv::Mat &dist, cv::Mat &dist2, const int type);
/**The classical center symetric census
A modified version of cs census which is comparing a pixel with its correspondent after the center
**/
void applySimetricCensus(const cv::Mat &img1, const cv::Mat &img2, int kernelSize, cv::Mat &dist, cv::Mat &dist2, const int type);
//!brief binary descriptor used in stereo correspondence
void applyBrifeDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2);
//The classical Rank Transform
void applyRTDescriptor(const cv::Mat &image1, const cv::Mat &image2, int kernelSize, cv::Mat &dist, cv::Mat &dist2);
Descriptor();
~Descriptor(void);
};
}
}
#endif
......
......@@ -110,7 +110,6 @@ namespace cv
for (x = 0; x < size.width; x++)
vsum[x] = (ushort)(vsum[x] + sptr[srcstep*y + x]);
}
for (y = 0; y < size.height; y++)
{
const uchar* top = sptr + srcstep*MAX(y - wsz2 - 1, 0);
......@@ -119,7 +118,6 @@ namespace cv
const uchar* curr = sptr + srcstep*y;
const uchar* next = sptr + srcstep*MIN(y + 1, size.height - 1);
uchar* dptr = dst.ptr<uchar>(y);
for (x = 0; x < size.width; x++)
vsum[x] = (ushort)(vsum[x] + bottom[x] - top[x]);
......@@ -132,10 +130,8 @@ namespace cv
int sum = vsum[0] * (wsz2 + 1);
for (x = 1; x <= wsz2; x++)
sum += vsum[x];
int val = ((curr[0] * 5 + curr[1] + prev[0] + next[0])*scale_g - sum*scale_s) >> 10;
dptr[0] = tab[val + OFS];
for (x = 1; x < size.width - 1; x++)
{
sum += vsum[x + wsz2] - vsum[x - wsz2 - 1];
......@@ -610,7 +606,6 @@ namespace cv
#else
const bool useShorts = false;
#endif
const double SAD_overhead_coeff = 10.0;
double N0 = 8000000 / (useShorts ? 1 : 4); // approx tbb's min number instructions reasonable for one thread
double maxStripeSize = std::min(std::max(N0 / (width * ndisp), (wsz - 1) * SAD_overhead_coeff), (double)height);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment