GLSL/C++ 实现滤镜效果

入门效果之浮雕

"浮雕"图象效果是指图像的前景前向凸出背景。常见于一些纪念碑的雕刻上。要实现浮雕事实上很easy。我们把图象的一个象素和左上方的象素进行求差运算。并加上一个灰度。这个灰度就是表示背景颜色。这里我们设置这个插值为128 (图象RGB的值是0-255)。同一时候,我们还应该把这两个颜色的差值转换为亮度信息.否则浮雕图像会出现彩色。

    "precision mediump float;      \n""varying vec2 v_texCoord;      \n""uniform sampler2D s_baseMap;     \n""uniform vec2 TexSize;            \n""void main()                 \n""{                             \n""   vec2 tex =v_texCoord;   \n""	vec2 upLeftUV = vec2(tex.x-1.0/TexSize.x,tex.y-1.0/TexSize.y);           \n""	vec4 curColor = texture2D(s_baseMap,v_texCoord);                           \n""	vec4 upLeftColor = texture2D(s_baseMap,upLeftUV);                  \n""	vec4 delColor = curColor - upLeftColor;                           \n""	float h = 0.3*delColor.x + 0.59*delColor.y + 0.11*delColor.z;                  \n""   vec4 bkColor = vec4(0.5, 0.5, 0.5, 1.0);                   \n""	gl_FragColor = vec4(h,h,h,0.0) +bkColor;                             \n""}                           \n";

C++版

void relief(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){int upperleft;upperleft=(y-1)*Stride+4*(x-1);Vec4 Pixel,NowPixel;NowPixel.SetPixel(pre,index);Pixel.SetPixel(pre,upperleft);Pixel=NowPixel-Pixel;BeGray(Pixel);Pixel.GetPixelToNow(now,index);
}

   


入门效果之马赛克

接下来我们完毕一个更加常见的效果—马赛克.图片的马赛克就是把图片的一个相当大小的区域用同一个点的颜色来表示.能够觉得是大规模的减少图像的分辨率,而让图像的一些细节隐藏起来, 比方电视中要秀一下某个罪犯的身材,却又不能展示他的脸,这个时候我们就能够给他的脸加一个马赛克.

用HLSL代码实现马赛克是很easy的,可是相同的,我们须要一些额外的步骤,第一步就是先把纹理坐标转换成图像实际大小的整数坐标.接下来,我们要把图像这个坐标量化---比方马赛克块的大小是8x8象素。那么我们能够用下列方法来得到马赛克后的图像採样值,如果[x.y]为图像的整数坐标:

[x,y]mosaic = [ int(x/8)*8 , int(y/8)*8].

    "precision mediump float;    \n""varying vec2 v_texCoord;    \n""uniform sampler2D s_baseMap;\n""uniform vec2 TexSize;       \n""vec2 mosaicSize = vec2(8,8);\n""void main()                 \n""{                           \n""	vec2 intXY = vec2(v_texCoord.x*TexSize.x, v_texCoord.y*TexSize.y);   \n""	vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x,floor(intXY.y/mosaicSize.y)*mosaicSize.y);  \n""	vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x,XYMosaic.y/TexSize.y);     \n""	vec4 baseMap = texture2D(s_baseMap,UVMosaic);                        \n""	gl_FragColor = baseMap;                                              \n""}                                                                       \n";

C++ 版

void Mosaic(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){Vec4 Pixel,Pixel_UpperLeft;Pixel.SetPixel(pre,index);if (x%8==0&&y%8==0){Pixel.GetPixelToNow(now,index);} else{int tmpX,tmpY;tmpX=x/8*8;tmpY=y/8*8;int index_UpperLeft;index_UpperLeft=tmpY*Stride+4*(tmpX);Pixel_UpperLeft.SetPixel(pre,index_UpperLeft);Pixel_UpperLeft.GetPixelToNow(now,index);}
}



读者可能会发现这个马赛克太普通了,确实它不够新颖,以下我们来改良一下。我们希望达到这样一个效果:马赛克区域不是方的,而是圆的,圆形区域以外,我们用图像原来的颜色覆盖。这样我们须要改变一下代码。

首先求出原来马赛克区域的正中心(原来是左上角):然后计算图像採样点到这个中心的距离,假设在马赛克圆内。就用区域的中心颜色,否则就用原来的颜色。

改良后的代码例如以下。这里我们把马赛克区域大小调节成16x16。这样效果更明显。

    "precision highp float;            \n""varying vec2 v_texCoord;            \n""uniform sampler2D s_baseMap;        \n""uniform vec2 TexSize;               \n""vec2 mosaicSize = vec2(8,8);      \n""void main()                         \n""{                                   \n""	vec2 intXY = vec2(v_texCoord.x*TexSize.x, v_texCoord.y*TexSize.y);    \n""	vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x,floor(intXY.y/mosaicSize.y)*mosaicSize.y) + 0.5*mosaicSize; \n""	vec2 delXY = XYMosaic - intXY;   \n""	float delL = length(delXY);      \n""	vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x,XYMosaic.y/TexSize.y); \n""	vec4 _finalColor;                \n""	if(delL< 0.5*mosaicSize.x)       \n""		_finalColor = texture2D(s_baseMap,UVMosaic);  \n""	else                             \n""		_finalColor = texture2D(s_baseMap,v_texCoord);  \n""	gl_FragColor = _finalColor;      \n""}                                   \n"  ;

C++版

void Mosaic_Point(int x,int y,BYTE *pre,BYTE *now,int Stride,int index){Vec4 Pixel,Pixel_UpperLeft;Pixel.SetPixel(pre,index);int i,j;i=x%8;j=y%8;double dist=sqrt(double((4-i)*(4-i)+(4-j)*(4-j)));if (dist>4){Pixel.GetPixelToNow(now,index);} else{int tmpX,tmpY;tmpX=x/8*8;tmpY=y/8*8;int index_UpperLeft;index_UpperLeft=tmpY*Stride+4*(tmpX);Pixel_UpperLeft.SetPixel(pre,index_UpperLeft);Pixel_UpperLeft.GetPixelToNow(now,index);}
}


图:  改良后的马赛克效果

进阶效果之锐化模糊

以上两个效果相对照较简单,姑且称之为入门效果。 它并没实用到太多数字图像处理或者信号处理方面的知识。

接下来我们要介绍略微复杂一点的效果。第一个就是图像的模糊和锐化。

图像的模糊又成为图像的平滑(smoothing),我们知道人眼对高频成分是非常敏感的。假设在一个亮度连续变化的图像中,突然出现一个亮点,那么我们非常easy察觉出来,类似的,假设图像有个突然的跳跃—明显的边缘,我们也是非常easy察觉出来的。

这些突然变化的分量就是图像的高频成分。

人眼一般是通过低频成分来辨别轮廓,通过高频成分来感知细节的(这也是为什么照片分辨率低的时候,人们仅仅能辨认出照片的大概轮廓,而看不到细节)。可是这些高频成分通常也包括了噪声成分。图像的平滑处理就是滤除图像的高频成分。

那么怎样才干滤除图像的高频成分呢?我们先来介绍一下图像数字滤波器的概念。

简单通俗的来说,图像的数字滤波器事实上就是一个n x n的数组(数组中的元素成为滤波器的系数或者滤波器的权重。n称为滤波器的阶)。

对图像做滤波的时候。把某个像素为中心的nxn个像素的值和这个滤波器做卷积运算(也就是相应位置上的像素和相应位置上的权重的乘积累加起来),公式例如以下

 当中x , y 为当前正在处理的像素坐标。

通常情况下,我们滤波器的阶数为3已经足够了,用于模糊处理的3x3滤波器例如以下

                                

经过这种滤波器。事实上就是等效于把一个像素和周围8个像素一起求平均值,这是很合理的---等于把一个像素和周围几个像素搅拌在一起—自然就模糊了。


"precision mediump float; 							   \n""vec4 dip_filter(mat3 _filter, sampler2D _image, vec2 _xy, vec2 texSize)               \n""{                                                									  \n""	mat3 _filter_pos_delta_x=mat3(vec3(-1.0, 0.0, 1.0), vec3(0.0, 0.0 ,1.0) ,vec3(1.0,0.0,1.0));            \n""   mat3 _filter_pos_delta_y=mat3(vec3(-1.0,-1.0,-1.0),vec3(-1.0,0.0,0.0),vec3(-1.0,1.0,1.0));              \n""	vec4 final_color = vec4(0.0, 0.0, 0.0, 0.0);                                      \n""	for(int i = 0; i<3; i++)                                                          \n""	{                                                                                 \n""		for(int j = 0; j<3; j++)                                                      \n""		{                                                                             \n""			vec2 _xy_new = vec2(_xy.x + _filter_pos_delta_x[i][j], _xy.y + _filter_pos_delta_y[i][j]); \n""			vec2 _uv_new = vec2(_xy_new.x/texSize.x, _xy_new.y/texSize.y);            \n""			final_color += texture2D(_image,_uv_new) * _filter[i][j];                 \n""		}																			  \n""	}																				  \n""	return final_color;																  \n""}																					  \n""varying vec2 v_texCoord;															  \n""uniform vec2 TexSize;    															  \n""uniform sampler2D s_baseMap;														  \n""void main()																		  \n""{																					  \n""	vec2 intXY = vec2(v_texCoord.x * TexSize.x, v_texCoord.y * TexSize.y);   		  \n""	mat3 _smooth_fil = mat3(1.0/9.0,1.0/9.0,1.0/9.0,										  \n""							1.0/9.0,1.0/9.0,1.0/9.0,										  \n""							1.0/9.0,1.0/9.0,1.0/9.0);										  \n""   vec4 tmp = dip_filter(_smooth_fil, s_baseMap, intXY, TexSize);""	gl_FragColor = tmp;                                                  			  \n""}																					  \n";

C++版

void dip_filter(int x,int y,BYTE *pre,BYTE *now,int Stride,int index)
{int dir_x[3][3]={-1,0,1,-1,0,1,-1,0,1};int dir_y[3][3]={1,1,1,0,0,0,-1,-1,-1};int tmpX,tmpY;Vec4 Pixel,PrePixel;double num=sqrt(2.0);double filter[3][3]={-1,0,1,-num,0,num,-1,0,1};Pixel.Clear();for (int i=0;i<3;i++){for (int j=0;j<3;j++){tmpX=x+dir_x[i][j];tmpY=y+dir_y[i][j];int tmp=tmpY*Stride+4*(tmpX);PrePixel.SetPixel(pre,tmp);Pixel+=PrePixel*filter[i][j];}}BeGray(Pixel);Pixel.GetPixelToNow(now,index);
}


以上的模糊滤波器称为BOX滤波器,是最简单的滤波器。假设考虑到离开中心像素的距离对滤波器系数的影响,我们通常採用更加合理的滤波器---高斯滤波器—一种通过2维高斯採样得到的滤波器。它的模板例如以下:


非常easy看出来。离开中心越远的像素。权重系数越小。

对于锐化操作,经常使用的锐化模板是拉普拉斯(Laplacian)模板,这个模板定义例如以下:


easy看出拉普拉斯模板的作法:先将自身与周围的8个象素相减,表示自身与周围象素的区别。再将这个区别加上自身作为新象素的灰度。

可见,假设一片暗区出现了一个亮点,那么锐化处理的结果是这个亮点变得更亮,这就增强了图像的细节。

以下三副图分别表示了经过BOX滤波。高斯滤波和拉普拉斯滤波后的图像

  

BOX 模糊                  高斯模糊                    拉普拉斯锐化

高斯模糊和拉普拉斯锐化效果的GLSL和BOX的代码基本一致,就是filter的系数不同。这里不在列出。

通过这个两个效果。我们介绍了图像的滤波操作,这种操作,也成为模板操作。它实现了一种邻域运算(Neighborhood Operation),即某个象素点的结果灰度不仅和该象素灰度有关。并且和其邻域点的值有关。模板运算在图象处理中常常要用到。能够看出。它是一项很耗时的运算。有一种优化的方法称为可分离式滤波,就是使用两个pass来进行x/y方向分别滤波,能让运算次数大大降低。

并且滤波器阶数越高,优势越明显。

数字图像滤波的时候,相同还须要注意边界像素的问题,只是幸好,GLSL能让边界处理更加的透明和简单。


进阶效果之描边效果

相对浮雕效果来说。描边(边缘检測)的代码并不复杂多少,仅仅是在理论上相对来说略微复杂一点,并且效果看上去更加的讨人喜欢一些。

我们知道 ,假设在图像的边缘处。灰度值肯定经过一个跳跃。我们能够计算出这个跳跃,并对这个值进行一些处理,来得到边缘浓黑的描边效果。

首先我们能够考虑对这个象素的左右两个象素进行差值。得到一个差量。这个差量越大,表示图像越处于边缘,并且这个边缘应该左右方向的,相同我们能得到上下方向和两个对角线上的图像边缘。这样我们构造一个滤波器


经过这个滤波器后。我们得到的是图像在这个象素处的变化差值,我们把它转化成灰度值,并求绝对值(差值可能为负),然后我们定义差值的绝对值越大的地方越黑(边缘显然是黑的)。否则越白,我们便得到例如以下的效果:

图:铅笔描边效果

该效果的代码例如以下(当中dip_filter函数代码同上):

	"precision mediump float; 							   \n""vec4 dip_filter(mat3 _filter, sampler2D _image, vec2 _xy, vec2 texSize)               \n""{                                                									  \n""	mat3 _filter_pos_delta_x=mat3(vec3(-1.0, 0.0, 1.0), vec3(0.0, 0.0 ,1.0) ,vec3(1.0,0.0,1.0));            \n""   mat3 _filter_pos_delta_y=mat3(vec3(-1.0,-1.0,-1.0),vec3(-1.0,0.0,0.0),vec3(-1.0,1.0,1.0));              \n""	vec4 final_color = vec4(0.0, 0.0, 0.0, 0.0);                                      \n""	for(int i = 0; i<3; i++)                                                          \n""	{                                                                                 \n""		for(int j = 0; j<3; j++)                                                      \n""		{                                                                             \n""			vec2 _xy_new = vec2(_xy.x + _filter_pos_delta_x[i][j], _xy.y + _filter_pos_delta_y[i][j]); \n""			vec2 _uv_new = vec2(_xy_new.x/texSize.x, _xy_new.y/texSize.y);            \n""			final_color += texture2D(_image,_uv_new) * _filter[i][j];                 \n""		}																			  \n""	}																				  \n""	return final_color;																  \n""}																					  \n""varying vec2 v_texCoord;															  \n""uniform vec2 TexSize;    															  \n""uniform sampler2D s_baseMap;														  \n""void main()																		  \n""{																					  \n""	vec2 intXY = vec2(v_texCoord.x * TexSize.x, v_texCoord.y * TexSize.y);   		  \n""	mat3 _smooth_fil = mat3(-0.5,-1.0,0.0,										  \n""							-1.0,0.0,1.0,										  \n""							 0.0,1.0,0.5);										  \n""   vec4 delColor = dip_filter(_smooth_fil, s_baseMap, intXY, TexSize);           \n""   float deltaGray = 0.3*delColor.x + 0.59*delColor.y + 0.11*delColor.z;          \n""   if(deltaGray < 0.0) deltaGray = -1.0 * deltaGray;                             \n""   deltaGray = 1.0 - deltaGray;                                                  \n""	gl_FragColor = vec4(deltaGray,deltaGray,deltaGray,1.0);                        \n""}																					  \n";

C++版

void Gaussian_filter(int x,int y,BYTE *pre,BYTE *now,int Stride,int index)
{int dir_x[3][3]={-1,0,1,-1,0,1,-1,0,1};int dir_y[3][3]={1,1,1,0,0,0,-1,-1,-1};int tmpX,tmpY;Vec4 Pixel,PrePixel;double filter[3][3]={1.0/16,2.0/16,1.0/16,2.0/16,4.0/16,2.0/16,1.0/16,2.0/16,1.0/16};Pixel.Clear();for (int i=0;i<3;i++){for (int j=0;j<3;j++){tmpX=x+dir_x[i][j];tmpY=y+dir_y[i][j];int tmp=tmpY*Stride+4*(tmpX);PrePixel.SetPixel(pre,tmp);Pixel+=PrePixel*filter[i][j];}}//BeGray(Pixel);Pixel.GetPixelToNow(now,index);
}




上面演示的效果种用到的模板就是一种边缘检測器,在信号处理上是一种基于梯度的滤波器。又称边缘算子。梯度是有方向的,和边沿的方向总是正交(垂直)的,在上面的代码中,我们採用的就是一个梯度为45度方向模板。它能够检測出135度方向的边沿。

以上是简单的边缘检測算子。更加严格的。我们能够採样Sobel算子,Sobel 算子有两个,一个是检測水平边沿的,还有一个是检測垂直平边沿的。相同,Sobel算子还有一种形式是各向同性Sobel算子。也有两个,一个是检測水平边沿的,还有一个是检測垂直边沿的。各向同性Sobel算子和普通Sobel算子相比,它的位置加权系数更为准确,在检測不同方向的边沿时梯度的幅度一致。读者能够自行尝试Sobel算子的效果,仅仅要改动pencil_filter的值就能够了。

高级效果之伪 HDR/Blow

HDR和Blow在如今主流游戏中是很时髦的效果。

所谓HDR就是高动态范围的意思,我们知道。在普通的显示器和位图里。每通道都是8-bit,也就是说RGB分量的范围都是0-255,这用来表示现实中的颜色显然是远远不够的,现实中的图像的动态范围远远大的多,那么怎样在现有的显示设备里尽可能的保持更大的动态范围,并且让它能更符合人眼的习惯就成了图形学研究的一个热点。通常真正的HDR的做法都是採用浮点纹理。把渲染运算的过程中,我们使用16bit的动态范围来保存运算结果,然后我们对运算结果进行分析。求出这个图像的中间灰度值,然后对图像进行调整映射到LDR的设备中。可是这种算法有两个很耗资源的过程,当中一个是浮点纹理,另外一个就是求图像中间灰度(通常情况是把图像不停的渲染到RenderTarget。每渲染一次,图像大小缩小一半。直到缩小到1x1大,一个1024 x1024的图像须要渲染10次!)。因此尽管HDR的效果很美丽。可是眼下还是仅仅有为数不多的游戏採用了这种算法,大部分都是採用的伪HDR+blow效果。

伪HDR效果一般是又一次调整图像的亮度曲线。让亮的更亮,暗的更暗一些,而Blow效果则是图像的亮度扩散开来。产生非常柔的效果。

在这里我们採用一个二次曲线来又一次调整图像的亮度,这个曲线的方程是

   x [ (2-4k) x + 4k-1 ).

K的取值范围为0.5 – 2.0

经过这个公式调整以后,图像上亮的区域将更加的亮。而且总体亮度会提高。那么接下来,我们怎样使图像的亮度扩散开来呢?一种可行的方法就是对场景图像做一次downsample。

把它变成原来的1/4次大小,那样就等于亮度往外扩散了4x4个象素的区域。

	"precision mediump float;     \n""varying vec2 v_texCoord;	  \n""uniform sampler2D s_baseMap; \n""uniform float k;					  \n""vec4 xposure(vec4 _color, float gray, float ex)  \n""{							  \n""	float b = (4.0*ex - 1.0);     \n""	float a = 1.0 - b;          \n""	float f = gray*(a*gray + b); \n""	return f*_color;		  \n""}							  \n""void main()				  \n""{							  \n""	vec4 _dsColor = texture2D(s_baseMap, v_texCoord); \n""	float _lum = 0.3*_dsColor.x + 0.59*_dsColor.y;    \n""	vec4 _fColor = texture2D(s_baseMap, v_texCoord);  \n""	gl_FragColor = xposure(_fColor, _lum, k);         \n""}                                                    \n";

C++版

void HDR(int x,int y,BYTE *pre,BYTE *now,int index,double k){Vec4 Pixel;double GrayPixel;Pixel.SetPixel(pre,index);GrayPixel=GetGray(Pixel)/255.0;double b=(4*k-1.0);double a=1-b;double f=GrayPixel*(a*GrayPixel+b);Pixel*=f;OverFlow(Pixel);Pixel.GetPixelToNow(now,index);
}


以下是原图像和经过处理后图像的对照:

  

原图                      k = 1.1                      k = 1.6

图:经过伪HDR+Blow处理过的图像和原图的对照

高级效果之水彩化

真正的水彩效果在shader中是比較难实现的。它须要进行中值滤波后累加等一些操作,还须要处理NPR中的笔触一类的概念。本文绕开这些概念,仅仅从视觉效果上能尽量模拟出水彩的画的那种感觉来。

我们知道,水彩画一个最大的特点是水彩在纸上流动扩散后会和周围的颜色搅拌在一起,另外一个特点就是水彩一般会形成一个个的色块,过渡不像照片那样的平滑。

针对这两个特点。

我们能够设计这种一个算法来模拟水彩画的效果。

我们能够採用噪声纹理的方式。既事先计算好一个nxn的随机数数组,作为纹理传递给Pixel shader,这样在Pixel Shader里我们就能获得随机数了。得到随机数后,我们将随机数映射成纹理坐标的偏移值,就能模拟出色彩的扩散了。典型的噪声纹理是这个样子的:

图:噪声纹理

接下来我们须要处理色块,我们对颜色的RGB值分别进行量化,把RGB分量由原来的8bit量化成比特数更低的值。这样颜色的过渡就会显得不那么的平滑,而是会呈现出一定的色块效果。

通过以上两步处理后,我们得到的图像依旧有非常多的细节。尤其是第一步处理中产生的非常多细节噪点,非常自然的我们就想到通过平滑模糊的方式来过滤掉这些高频噪声成分。

算法设计好了,接下来看看我们怎样在RenderMonkey里实现这个算法。

类似上一个效果。我们须要两个pass来完毕这个算法,第一个pass叫flow pass,模拟颜色的流动和处理颜色的量化。

第二个pass叫Gauss pass。也就是前面提到的高斯模糊算法。

我们的重点在第一个pass。

在模拟扩散的pass中,我们相同须要一个RenderTarget。以把结果保存在当中以便兴许处理,然后还须要一个噪声纹理来产生随机数。详细代码例如以下:

	"precision mediump float;    \n""varying vec2 v_texCoord;    \n""uniform sampler2D s_baseMap;  \n""uniform vec2 TexSize;       \n""float _waterPower = 40.0;     \n""float _quatLevel = 5.0;       \n""vec4 quant(vec4 _cl, float n)  \n""{                            \n""	_cl.x = floor(_cl.x*255.0/n)*n/255.0;  \n""	_cl.y = floor(_cl.y*255.0/n)*n/255.0;  \n""	_cl.z = floor(_cl.z*255.0/n)*n/255.0;  \n""	return _cl;                            \n""}                                         \n""void main()                               \n""{                                         \n""	vec4 noiseColor = _waterPower*texture2D(s_baseMap,v_texCoord);           \n""	vec2 newUV =vec2 (v_texCoord.x + noiseColor.x/TexSize.x,v_texCoord.y + noiseColor.y/TexSize.y);  \n""	vec4 _fColor = texture2D(s_baseMap,newUV);                 \n""	gl_FragColor = quant(_fColor, 255.0/pow(2,_quatLevel));   \n""}                                                \n";

C++版

void WaterFilter(int x,int y,BYTE *pre,BYTE *now,int Stride,int index,double _quatLevel,double _waterPower,int width,int height){Vec4 nowPixel,RoundPixel;int indexRound;double Level;nowPixel.SetPixel(pre,index);int rx=rand()%2,ry=rand()%2;indexRound=(y+rx)*Stride+(x+ry)*4;RoundPixel.SetPixel(pre,indexRound);Level=255/pow(2,_quatLevel);RoundPixel.r=floor(RoundPixel.r/Level)*Level;RoundPixel.g=floor(RoundPixel.g/Level)*Level;RoundPixel.b=floor(RoundPixel.b/Level)*Level;OverFlow(RoundPixel);RoundPixel.GetPixelToNow(now,index);
}


代码中的_quatLevel用来表示对图像的量化比特数。值越小。色块越明显。比較合理的取值范围是2-6。_waterPower则表示图像颜色扩散范围,取值范围在8-64之间的效果比較好。

以下是经过水彩画处理后的图像:

 

 图:水彩画效果。左图量化比特数为6比特,扩散范围为20象素。

                 右图量化比特数为5比特,扩散范围为40象素


最后贴个C++版用到的函数

struct Vec4{double r,g,b,a;Vec4 (){this->r=0;this->g=0;this->b=0;this->a=0;}Vec4(double r,double g,double b){this->r=r;this->g=g;this->b=b;}Vec4 operator+(const double one) const{return Vec4(r+one,g+one,b+one);}Vec4 operator+(const Vec4& rhs)const{return Vec4(r+rhs.r,g+rhs.g,b+rhs.b);}Vec4& operator+=(const double one){r+=one;g+=one;b+=one;return *this;}Vec4& operator+=(const Vec4& rhs){r+=rhs.r;g+=rhs.g;b+=rhs.b;return *this;}Vec4 operator-(const double one) const{return Vec4(r-one,g-one,b-one);}Vec4 operator-(const Vec4& rhs)const{return Vec4(r-rhs.r,g-rhs.g,b-rhs.b);}Vec4& operator-=(const double one){r-=one;g-=one;b-=one;return *this;}Vec4 operator*(const double one) const{return Vec4(r*one,g*one,b*one);}Vec4& operator*=(const double one){r*=one;g*=one;b*=one;return *this;}Vec4 operator/(const double one) const{return Vec4(r/one,g/one,b/one);}Vec4& operator/=(const double one){r/=one;g/=one;b/=one;return *this;}void Clear(){r=g=b=0;}void SetPixel(BYTE *pre,int index){this->r=pre[index];this->g=pre[index+1];this->b=pre[index+2];this->a=pre[index+3];}void SetPixel(double RGB){this->r=RGB;this->g=RGB;this->b=RGB;}void SetPixel(double R,double G,double B){this->r=R;this->g=G;this->b=B;}void GetPixelToNow(BYTE *now,int index){now[index]=this->r;now[index+1]=this->g;now[index+2]=this->b;}
};
void OverFlow(Vec4 &Pixel){if (Pixel.r>255.0){Pixel.r=255;}else if (Pixel.r<0.0){Pixel.r=-Pixel.r;}if (Pixel.g>255.0){Pixel.g=255;}else if (Pixel.g<0.0){Pixel.g=-Pixel.g;}if (Pixel.b>255.0){Pixel.b=255;}else if (Pixel.b<0.0){Pixel.b=-Pixel.b;}
}
void OverFlow(double &Pixel){if (Pixel>255.0){Pixel=255;}else if (Pixel<0.0){Pixel=-Pixel;}
}
void ToGray(Vec4 &Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;Pixel.SetPixel(detaGray);OverFlow(Pixel);
}
double GetGray(Vec4 Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;OverFlow(detaGray);return detaGray;
}
void BeGray(Vec4 &Pixel){double detaGray;detaGray=Pixel.r*0.3+Pixel.g*0.59+Pixel.b*0.11;if (detaGray<0.0){detaGray=-detaGray;}else if (detaGray>255.0){detaGray=255;}detaGray=255-detaGray;Pixel.SetPixel(detaGray);
}
void Solve(){BYTE *pre = (BYTE*)m_srcImg.Scan0;BYTE *now = (BYTE*)m_copySrcImg.Scan0;//函数在这边调用就可以。

    int index=0,Stride=m_copySrcImg.Stride;     int width=m_pCopyImg->GetWidth();     int height=m_pCopyImg->GetHeight();     index=Stride+4;     for (int j=1;j<m_copySrcImg.Height-1;j++)     {         for (int i=1;i<m_copySrcImg.Width-1;i++)         {            //Gaussian_filter(i,j,pre,now,Stride,index);             //dip_filter(i,j,pre,now,Stride,index);             //relief(i,j,pre,now,Stride,index);             //Mosaic(i,j,pre,now,Stride,index);             //Mosaic_Point(i,j,pre,now,Stride,index);             //HDR(i,j,pre,now,index,1.1);             //WaterFilter(i,j,pre,now,Stride,index,10,40,width,height);                 index+=4;         }         index+=8;     }     //调用函数。

}



转载于:https://www.cnblogs.com/lxjshuju/p/7343873.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/454963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cv mat的shape_pybind11—opencv图像处理(numpy数据交换)

前言C opencv中图像和矩阵的表示采用Mat类&#xff0c;比如imread()读取的结果就是返回一个Mat对象。对于python而言&#xff0c;numpy 通常用于矩阵运算&#xff0c; 矩阵&#xff0c;图像表示为numpy.ndarray类。因此&#xff0c;想要将python numpy.ndarray的数据传递到C op…

H.264算法的优化策略

文章来源&#xff1a; http://www.tichinese.com/Article/Video/200909/2150.html 编辑&#xff1a;小乙哥 1 代码优化的主要方法 通过代码移植能够获得在DSP上初步运行的代码&#xff0c;但是它由于没有考虑到DSP自身的硬件特点&#xff0c;不适合DSP强大的并行处理能力&#…

吃饭、睡觉、打星星之“打星星”!

大家见过这样的星星么&#xff1f; 你想要多少就可以多少的星星&#xff01;&#xff01;&#xff01; 下面我们就来用奇妙的JavaScript来实现 首先我们要引入一个输入包 let readline require("readline-sync");然后再让客户输入数字&#xff0c;并将其存放起来con…

使用iconv-lite解决node当中不支持GBK编码的问题

1、Node环境当中不支持GBK编码 node.js当中的Buffer对象支持的编码格式的种类有限&#xff0c;大概有ascii、utf8、utf16le、ucs2、base64、binary、hex。不支持GBK的编码形式。对于windows系统来说&#xff0c;由于历史原因&#xff0c;许多文件默认的编码格式均为GBK。 比如我…

mysql 集群架构_mysql企业常用集群架构

转自 https://blog.csdn.net/kingice1014/article/details/760200611、mysql企业常用集群架构在中小型互联网的企业中。mysql的集群一般就是上图的架构。WEB节点读取数据库的时候读取dbproxy服务器。dbproxy服务器通过对SQL语句的判断来进行数据库的读写分离。读请求负载到从库…

关于Vue2.0,Express实现的简单跨域

npm install express -g 通过npm全局安装express&#xff0c;之后可以通过 express --version 来查看express版本 express server 通过express server生成server项目文件 npm install 安装server的项目依赖 可以通过执行server下的bin\www文件可以开启服务 在www文件我们可以默…

mysql返回yyyy mm dd_怎么把取出mysql数据库中的yyyy-MM-dd日期转成yyyy年MM月dd日格式...

您好&#xff0c;通过两个个步骤可以完成转换&#xff1a;第一步&#xff1a;日期处理可以在模板数据集中通过sql语句转换&#xff0c;转换方式方式如下&#xff1a;SELECT DATE_FORMAT(NOW(),%Y) YEAR输出结果&#xff1a;2018SELECT DATE_F…

感动一生的几句话

为什么80%的码农都做不了架构师&#xff1f;>>> 很多东西就掌握在我们手中&#xff1a; 比如快乐&#xff0c;你不快乐&#xff0c;谁会同情你的悲伤&#xff1b; 比如坚强&#xff0c;你不坚强&#xff0c;谁会怜悯你的懦弱&#xff1b; 比如努力&#xff0c;你不…

patator mysql 字典_利用patator进行子域名爆破

前言:原来朋友写的一个子域名爆破工具挺好用,这前几天API接口关了.痛苦万分.自己也写了一个类似的但是不咋稳定.特地google找了下 找到一款patator.效果和速度还是不错的。knock的速度真心受不了啊patator是由Python写的 不用安装下载即可.下载地址&#xff1a;http://code.goo…

[bzoj1059]矩阵游戏

虽然是一道水难题&#xff0c;但是我这种蒟蒻还是要讲一讲的。 Description 小Q是一个非常聪明的孩子&#xff0c;除了国际象棋&#xff0c;他还很喜欢玩一个电脑益智游戏——矩阵游戏。矩阵游戏在一个N*N黑白方阵进行&#xff08;如同国际象棋一般&#xff0c;只是颜色是随意的…

golang mysql 插入_Mysql学习(一)添加一个新的用户并用golang操作Mysql

Mysql添加一个新的用户并赋予权限添加一个自己的用户到mysql首先我们需要先用root用户登录mysql&#xff0c;但是刚安装完没有密码&#xff0c;我们先跳过密码ailumiyanaailumiyana:~/Git_Project/Go_Test$ sudo mysqld_safe --skip-grant-tables2019-01-07T01:35:51.559420Z m…

云计算构建基石之Hyper-V:虚拟机管理

本文讲的是云计算构建基石之Hyper-V:虚拟机管理,作为云计算的重要基石&#xff0c;虚拟化技术的好坏起着关键作用。Hyper-V作为微软重要的虚拟化解决技术&#xff0c;在微软云计算构建解决方案中&#xff0c;更是关键至关键&#xff0c;基础之基础。在本系列文章中&#xff0c;…

3GP文件格式分析

1. 概述现在很多智能手机都支持多媒体功能&#xff0c;特别是音频和视频播放功能&#xff0c;而3GP文件格式是手机端普遍支持的视频文件格式。目前很多手机都支持h263视频编码格式的视频文件播放&#xff0c;还有些手机支持h264。音频文件格式普遍支持amr&#xff0c;有些手…

mysql group concat_MySQL 的 GROUP_CONCAT 函数详解

GROUP_CONCAT(expr) 函数会从 expr 中连接所有非 NULL 的字符串。如果没有非 NULL 的字符串&#xff0c;那么它就会返回 NULL。语法如下&#xff1a;GROUP_CONCAT 语法规则它在递归查询中用的比较多&#xff0c;但要使用好它并不容易。所以让我们一起来看看吧&#xff1a;假设有…

光荣之路测试开发面试linux考题之四:性能命令

Hi,大家好我是tom,I am back.今天要给大家讲讲linux系统一些性能相关命令。 1.fdisk 磁盘管理 是一个强大的危险命令&#xff0c;所有涉及磁盘的操作都由该命令完成&#xff0c;包括&#xff1a;新增磁盘、增删改磁盘分区等。 1.fdisk -l 查看磁盘分区情况 Disk /dev/sda: 27.8…

mac安装完mysql后关机特别慢_mysql-Mac终端下遇到的问题总结

为了方便启动mysql服务&#xff0c;修改/etc/.bash_profile文件&#xff0c;如下alias mysql"/usr/local/mysql/bin/mysql"alias mysqladmin"/usr/local/mysql/bin/mysqladmin"或者alias mysqlstart"sudo /usr/local/mysql/support-files/mysql.serve…

sending data mysql slow Mysql查询非常慢的可能原因

1.用explain看看mysql的执行情况,可以得知,task_id扫描了近20万条数据,而且这个task_id不是索引 2.为这个task_id所在的表,将此字段添加索引后,查询就变得很快了 转载于:https://www.cnblogs.com/Skrillex/p/7365590.html

打包上架

昨天写的打包上架&#xff0c;分组到了文章&#xff0c;发现不便查看贴链接到这里&#xff1a; http://www.cnblogs.com/ITCoderW/articles/7597969.html 最近一个版本的审核的过程 当我们上传到APP Store一个新的版本后 登录ITunes Connect就可以看到相应的版本的审核的状态 粗…

架构设计--仅是软件开发之第二大影响力?!

SDWest2006&#xff08;译注1&#xff09;对我来说是个有趣的大会。我除了星期三之外&#xff08;当时我正飞往费城参加一个客户会议 因此错过了Jolt颁奖部分&#xff09;每天都在演讲。我也参加了一些谈话和会议&#xff1b;其中最引人关注的是Mike Cohn的计划与估算的谈话。…

WiFi密码分享有妙招 不必口头相传

移动互联网的迅速崛起&#xff0c;使得我们可以方便的使用手持移动设备进行上网。尤其是在家庭中&#xff0c;使用智能手机、平板电脑、笔记本电脑等移动设备进行上网和娱乐已经成为主流&#xff0c;台式机上网正日渐式微。在家中时&#xff0c;我们通过无线路由器提供的WiFi网…