这篇GPGPU 概念1: 数组= 纹理 - 文档文章提出的数组与纹理相等让人打开新的眼界与思维,本文在这文基础上,尝试把这部分思想拿来用在VBO粒子系统上.

  在前面的文章中,我们把CPU的数据传到GPU后,然后就直接从桢缓冲到显示屏幕上了,那么还能不能把从GPU的数据拿回来放入CPU,然后进行处理。例如最基本的GPGPU编程中,把数组放入GPU运算后返回CPU。以及图片用GPU来加速处理。

  和以前把数据从CPU通过相关GLSL,Cg着色器语言传入数据到GPU并进行处理不同,多了个返回数据到CPU中,因为GPU擅长的是并行处理,所以我们一般把一系列数据处理从CPU中交给GPU。在前面的顶点拾取中,获取地形高度中,我们都把一系列数据当做纹理,然后从CPU传入GPU中处理。现在关键问题是,如果把GPU数据送回到CPU中。

  在前面这篇文章中(WebGL 利用FBO完成立方体贴图),我们看到前面,可以先在FBO里,把当前的内存数据经过GPU处理后输出到应用程序桢缓冲关联的纹理中,然后此纹理拿来做后面球所需要的立方体贴图。在这过程中,我们可以知道通过FBO实现,数据从CPU处理传入GPU,在着色器中进行处理后,然后我们可以在CPU中得到对应FBO中纹理里的数据。

  下面我们完成一个简单实例,通过把一个数组,传入GPU中,在GPU中进行处理,然后返回到CPU中并显示出来。在这我们只介绍,数据一对一的处理,就是传入多少数据,返回多少数据。

  结合上面所说,大致过程如下,第一步创建FBO,并关联一个纹理(存放经过GPU处理的数据),然后再创建一个纹理,里面用来存放我们需要处理的数据,因此要求,这个纹理和FBO中关联的纹理大小一样,方便处理。第二步,我们在创建的FBO中,来完成一个输出渲染,因为上步,要求相同大小的纹理,所以这个渲染窗口我们需要特殊处理下,在这窗口,最大保证窗口大小与纹理大小一样,纹理刚好占用整个窗口大小。这样才能保证输出到FBO中的纹理和传入的纹理索引一一对应。这样FBO中的纹理就存放的是原始数据经过GPU处理后的数据。

  让我们来完成整个过程,首先,我们把转入的数据整理成我们需要的数组列表。请看代码如下:

     def __init__(this,*args):
this.imageFormat = GL_FLOAT
if len(args) == 1 :
if isinstance(args[0],Image):
image = args[0]
this.width = image.size[0]
this.height = image.size[1]
this.data = image.im
this.imageFormat = GL_UNSIGNED_BYTE
if isinstance(args[0],list):
listf = args[0]
this.width = len(listf) if len(listf) > 0 else 1
this.height = 1
this.data = listf
if len(args) == 3:
this.width = int(args[0])
this.height = int(args[1])
this.data = args[2]
this.imagedata = []
for i in range(this.height):
for j in range(this.width):
index = i * this.width + j
f4 = this.data[index] if index < len(this.data) else float(j) / float(this.width)
r,g,b,a = 0.0,0.0,0.0,0.0
if isinstance(f4,tuple):
if len(f4) == 2:
r,g = f4
elif len(f4) == 3:
r,g,b = f4
else:
r,g,b,a = f4
else:
r = f4
this.imagedata.append(r)
this.imagedata.append(g)
this.imagedata.append(b)
this.imagedata.append(a)
#this.imagedata = ny.array(this.imagedata,dtype=ny.float)
#print this.imagedata

初始化

  这段代码里,会列举几种情况,一种是直接传入的Image信息,还有是传入的自定义数组,还有定义了格式化长与宽的数组。然后结合传入数组的信息,可以是单个数据,也可以是如Image里保存的如r,g,b,a这样的四元组,整理后,得到我们需要的纹理的长与宽,纹理所保存的数据。

  然后如上面的第一步中所说,创建一个FBO和与之关联的纹理,还有一个保存需要处理的数据的纹理。具体代码如下:

     def createFBO(this):
this.fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
#create texture for fbotext,GPU->CPU
this.fbotext = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D,this.fbotext)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, this.width, this.height,0,GL_RGBA,GL_FLOAT,None)#GL_FLOAT
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
#relation fbo and fbotext(FBO and Texture2D)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,this.fbotext,0) #glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,this.fbotext,0)
glBindFramebuffer(GL_FRAMEBUFFER,0)
#save data to texture.CPU->GPU
this.fbodata = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D,this.fbodata)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
print this.imageFormat
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, this.width, this.height, 0, GL_RGBA,this.imageFormat, this.imagedata)

创建FBO与纹理

  就如上面所说,创建FBO,二个纹理,其中纹理需要注意的是glTexImage2D中的第三个参数,这里我们选择的是GL_RGBA32F,这样可以在纹理中,对应的r,g,b,a通道保证存放完整的32位浮点数据,而不必要限制在0.0-1.0。下面这些参数按理说可以不设置,但是如果你传入的是图片,为了保证处理后的图片显示正常,还是有必要设置的。其中glTexImage2D的参数对应详细说明,如果大家有兴趣,可以自己搜索。

  下面就如上面第二步所说,创建输出渲染,窗口需要与纹理大小一一对应,主要代码如下:

     def renderFBO(this,shader):
glDisable(GL_DEPTH_TEST)
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
glPushAttrib(GL_VIEWPORT_BIT)#| GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glDrawBuffer(GL_COLOR_ATTACHMENT0)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0,this.width,0.0,this.height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glViewport(0,0,this.width,this.height)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, this.fbodata)
glUseProgram(shader)
glUniform1i(shader.tex0, 0)
glUniform1f(shader.xl, this.width)
glUniform1f(shader.yl, this.height)
glBegin(GL_QUADS)
glVertex2f(0.0, 0.0)
glVertex2f(this.width, 0.0)
glVertex2f(this.width, this.height)
glVertex2f(0.0, this.height)
glEnd()
glUseProgram(0)
glBindFramebuffer(GL_FRAMEBUFFER,0)

输出数据并保存到FBO对应纹理上

  嗯,我发现需要把对应的着色器代码一起贴出来,然后再好说明。请看对应的着色器代码如下:

 gpgpu_v = """
//#version 330 compatibility
#version
out vec4 pos;
void main() {
pos = vec4(gl_Vertex);
//The following coding must be in fragment shader
//vec2 xy = v.xy;
//vec2 uv = vec2(xy/vec2(xw,yw)).xy;
//o_color = texture2D(tex0, uv);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}""" gpgpu_f = """
//#version 330 compatibility
#version
in vec4 pos;
uniform sampler2D tex0;
uniform float xw;
uniform float yw;
void main() {
vec2 xy = pos.xy;
vec2 uv = vec2(xy/vec2(xw,yw)).xy;
vec4 o_color = texture2D(tex0, uv);// vec4(uv.x,uv.y, 0, 1 );//
o_color = o_color + vec4(0.2);
gl_FragColor = o_color;
}"""

着色器

 这二段代码就是主要的过程了,首先告诉OpenGL,我们用的是应用程序桢缓冲(glBindFramebuffer),是不输出到屏幕中的。首先就是把输出窗口和纹理大小一一对应,保证纹理刚好显示在一个满屏幕上,因此,在这我们用的投影矩阵是正投影矩阵,和透视投影矩阵的区别,这里就不详细说明,简单来说,透视投影会带给人如人眼中看到的远处的特殊显示比较少的效果,而正投影就是远处和近处大小显示不改变,他们主要就是对三维点xyz中的z值采用不同的处理造成的。而视图矩阵,和模型矩阵一样,他们不需要改变点的位置,直接采用单元矩阵就可。结合着色器中相应代码,简单来说,传入的长与宽,是用了得到相应顶点的索引值,这里相应代码在上文中的获取地形的高度一样的道理,这里不需要x,y二分量分别加0.5,是因为顶点一个是0-x,一个是(-x/2)-x/2.根据我们平常根据索引取数组里的值一样,只是多考虑成二维的情况。特意说下,相关过程据需要放入片断着色器中,因为我们是顶点和像素一一对应,只有在片断中,才是针对像素的处理。

  下面我们来验证我们二个处理,看下能不能满足我们。一个是图片,我们把图片里的r,g,b,a分量分别加上0.2,这样看起来应该显示更亮一些。第二个我们是传入一个数组,然后按上面我们进行处理后,输出到控制台里面。

  第一个,传入数据与相应改动如下(在上文与前面的基础上): 在gpgpubasic中,新增一个方法,主要是输出原始图片,与经过GPU在各分量上加上0.2处理后的图像。代码增加如下:

     def render(this,shader):
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0,this.width * 2,0.0,this.height * 2)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glViewport(0,0,this.width,this.height) glColor4f(1.0, 1.0, 1.0, 0.5)
glActiveTexture(GL_TEXTURE0)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, this.fbodata)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0)
glVertex2f(0.0, 0.0)
glTexCoord2f(1.0, 0.0)
glVertex2f(this.width, 0.0)
glTexCoord2f(1.0, 1.0)
glVertex2f(this.width, this.height)
glTexCoord2f(0.0, 1.0)
glVertex2f(0.0, this.height)
glEnd() glTranslatef(this.width + 10, 0.0, 0.0)
glColor4f(1.0, 1.0, 1.0, 0.5)
glActiveTexture(GL_TEXTURE0)
glEnable(GL_TEXTURE_2D)
glBindTexture(GL_TEXTURE_2D, this.fbotext)
glBegin(GL_QUADS)
glTexCoord2f(0.0, 0.0)
glVertex2f(0.0, 0.0)
glTexCoord2f(1.0, 0.0)
glVertex2f(this.width, 0.0)
glTexCoord2f(1.0, 1.0)
glVertex2f(this.width, this.height)
glTexCoord2f(0.0, 1.0)
glVertex2f(0.0, this.height)
glEnd()

输出二张图。

  效果如下:

  显示效果如我们所想,整体上变亮了。

  第二个例子,我们传入一个数组,然后在GPU对每个数组加1。相应代码个性如下:

 part = particle.gpgpubasic([11,12,13,14,15])
def InitGL(width,height):
glClearColor(0.1,0.1,0.5,0.1)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glShadeModel(GL_SMOOTH)
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
camera.move(0.0,1.3,0.0)
camera.setthree(True)
camera.length = 3
global shaderall
shaderall = shaderProg.allshader()
global colorMap
global hightMap
colorMap = loadtexture.Texture.loadmap("ground2.bmp")
#hight map for cpu to gpu
hightMap = loadtexture.Texture.loadmap("hight.gif")
#create terrain use cpu
hightimage = loadtexture.Texture.loadterrain("hight.gif")
image = open("ground2.bmp").convert("RGBA")
plane.setHeight(hightimage)
#global part
#part = particle.gpgpubasic(image)
part.createFBO()
pingpong.createFBO()
part.renderFBO(shaderall.gpgpuProgram)

传入数组。

  因为是混合在上文的代码中的,所以有一些多余代码,主要就是创建一个[112,128,132,141,152]的数组,然后把上文中相关传入图片的相关代码隐藏掉。原来着色器中增加vec4(0.2)改成vec4(1.0),并且在上文中的renderFBO添加如下代码:  

         glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
glReadBuffer(GL_COLOR_ATTACHMENT0)
data = glReadPixels(0, 0, this.width, this.height,GL_RGBA,GL_FLOAT)
print "fbo data:",type(data),len(data),data[0],data[1]#,data[2]
glPopAttrib()
glBindFramebuffer(GL_FRAMEBUFFER,0)
#close fbo
glBindTexture(GL_TEXTURE_2D, 0)
glEnable(GL_DEPTH_TEST)

输出经过GPU更改后的数据。

  显示效果:

  根据前面的代码,可以看到,返回的数据长度还是5,前二个分量由112,128变成113,129了。

  有了上面这些,我们已经知道如何把CPU的数据经过GPU处理后返回了,下面介绍一个在这基础之上的乒乓技术,是一个用来把渲染输出转换成为下一次运算的输入的技术,常见的如粒子系统里的运算如果放在GPU中,那么必需用乒乓技术来实现,因为GPU中数据是前次更新加上现在的结果。当然CPU中运算就简单了,直接每桢改变内存中的数据,然后更新VBO,VAO啥的就行了。

  实现原理很简单,用FBO关联二个纹理,在第一渲染时,用第一个纹理数据经GPU处理后放入FBO关联的第二个纹理中,然后下一桢,把第二个纹理中的数据经过GPU处理放入第一个纹理。代码如下:

 class gpgpupingpong(object):
def __init__(this,*args):
this.imageFormat = GL_FLOAT
this.index = 0
if len(args) == 1 :
if isinstance(args[0],Image):
image = args[0]
this.width = image.size[0]
this.height = image.size[1]
this.data = image.im
this.imageFormat = GL_UNSIGNED_BYTE
if isinstance(args[0],list):
listf = args[0]
this.width = len(listf) if len(listf) > 0 else 1
this.height = 1
this.data = listf
if len(args) == 3:
this.width = int(args[0])
this.height = int(args[1])
this.data = args[2]
this.imagedata = []
for i in range(this.height):
for j in range(this.width):
index = i * this.width + j
f4 = this.data[index] if index < len(this.data) else float(j) / float(this.width)
r,g,b,a = 0.0,0.0,0.0,0.0
if isinstance(f4,tuple):
if len(f4) == 2:
r,g = f4
elif len(f4) == 3:
r,g,b = f4
else:
r,g,b,a = f4
else:
r = f4
this.imagedata.append(r)
this.imagedata.append(g)
this.imagedata.append(b)
this.imagedata.append(a)
def createFBO(this):
this.fbo = glGenFramebuffers(1)
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
#create texture for fbotext,GPU->CPU
this.fbotext = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D,this.fbotext)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, this.width, this.height,0,GL_RGBA,GL_FLOAT,None)#GL_FLOAT
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
#relation fbo and fbotext(FBO and Texture2D)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,this.fbotext,0) #glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,this.fbotext,0) #save data to texture.CPU->GPU
this.fbodata = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D,this.fbodata)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, this.width, this.height, 0, GL_RGBA,GL_FLOAT,this.imagedata)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,GL_TEXTURE_2D,this.fbodata,0) glBindFramebuffer(GL_FRAMEBUFFER,0)
this.pingpong = [[GL_COLOR_ATTACHMENT0,this.fbodata],[GL_COLOR_ATTACHMENT1,this.fbotext]]
#cpu to gpu,and gpu compute result in fbo and fbotext.(cpu -(fbodata)> gpu
#-(fbotext)> cpu)
def renderFBO(this,shader):
ind = this.index % 2
this.index = this.index + 1
print "index",ind
pp = this.pingpong[ind]
glDisable(GL_DEPTH_TEST)
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
glDrawBuffer(pp[0])
glPushAttrib(GL_VIEWPORT_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluOrtho2D(0.0,this.width,0.0,this.height)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glViewport(0,0,this.width,this.height)
glActiveTexture(GL_TEXTURE0)
glBindTexture(GL_TEXTURE_2D, pp[1])
glUseProgram(shader)
glUniform1i(shader.tex0, 0)
glUniform1f(shader.xl, this.width)
glUniform1f(shader.yl, this.height)
glBegin(GL_QUADS)
glVertex2f(0.0, 0.0)
glVertex2f(this.width, 0.0)
glVertex2f(this.width, this.height)
glVertex2f(0.0, this.height)
glEnd()
glUseProgram(0)
glBindFramebuffer(GL_FRAMEBUFFER,0)
if ind == 0 :
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT)
data = glReadPixels(0, 0, this.width, this.height,GL_RGBA,GL_FLOAT)
print "fbo 0:",len(data),data[0]#,data[1],data[2]
glBindFramebuffer(GL_FRAMEBUFFER,0)
else :
glBindFramebuffer(GL_FRAMEBUFFER,this.fbo)
glReadBuffer(GL_COLOR_ATTACHMENT1_EXT)
data = glReadPixels(0, 0, this.width, this.height,GL_RGBA,GL_FLOAT)
print "fbo 1:",len(data),data[0]#,data[1],data[2]
glBindFramebuffer(GL_FRAMEBUFFER,0) glPopAttrib()
#close fbo
glBindTexture(GL_TEXTURE_2D, 0)
glEnable(GL_DEPTH_TEST)

乒乓技术

  整个过程和上面的差不多,但是需要注意的,在glBindFramebuffer后渲染前,需要指定输出到的FBO对应的绑定点,就是glDrawBuffer(pp[0])。

  我们可以看下效果。传入pingpong = particle.gpgpupingpong([10,20,30]),然后每桢每顶点在GPU中加一。效果哪下(跑的有点快,只拿到加到200后的数据):

  主要实践就到这个,因为上文中,顶点的着色器中处理不复杂,传入的顶点也不多,大家可能认为作用不大。但是大家如果想下,一张1000*1000像素的纹理,简单来算GPU只要能并行处理100个点,那也比CPU要高100倍。  

  附件:PythonGPU处理.zip

  今天这个特殊日子,祝大家马年吉祥,万事如意。

  

初试PyOpenGL三 (Python+OpenGL)GPGPU基本运算与乒乓技术的更多相关文章

  1. 初试PyOpenGL四 (Python+OpenGL)GPU粒子系统与基本碰撞

    这篇相当于是对前三篇的总结,基本效果如下: 在初试PyOpenGL一 (Python+OpenGL)讲解Pyopengl环境搭建,网格,球体,第一与第三人称摄像机的实现.在初试PyOpenGL二 (P ...

  2. 初试PyOpenGL一 (Python+OpenGL)

    很早就一直想学Python,看到一些书都有介绍,不管是做为游戏的脚本语言,还是做为开发项目的主要语言都有提及(最主要的CUDA都开始支持Python,CUDA后面一定要学),做为先熟悉一下Python ...

  3. 初试PyOpenGL二 (Python+OpenGL)基本地形生成与高度检测

    在上文中,讲述了PyOpenGL的基本配置,以及网格,球形的生成,以及基本的漫游.现在利用上一篇的内容,来利用高程图实现一个基本的地形,并且,利用上文中的第三人称漫游,以小球为视角,来在地形上前后左右 ...

  4. (Python OpenGL)【5】平移 PyOpenGL

    (Python OpenGL) 原文:http://ogldev.atspace.co.uk/www/tutorial06/tutorial06.html  (英文) 下面是我翻译过来的: 背景 在本 ...

  5. (Python OpenGL)【3】着色器 PyOpenGL

    (Python OpenGL)现在开始我们使用着色器来进行渲染.着色器是目前做3D图形最流行的方式. OpenGL的渲染管线流程: 数据传输到OpenGL—>顶点处理器—>细分着色—> ...

  6. (Python OpenGL)【4】Uniform变量 PyOpenGL

    (Python OpenGL) 原文:http://ogldev.atspace.co.uk/www/tutorial05/tutorial05.html(英文) __author__ = " ...

  7. 【Python OpenGL】【2】第一个三角形(Pyopengl)

    根据顶点缓存来生成图元(Python OpenGL) 原文(英文链接)http://ogldev.atspace.co.uk/www/tutorial03/tutorial03.html __auth ...

  8. (Python OpenGL)【1】你好顶点 PyOpenGL

    原文链接(C语言环境)(Python OpenGL) 我用python实现的代码: __author__ = "WSX" from OpenGL.GLUT.freeglut imp ...

  9. 三. Python基础(3)--语法

    三. Python基础(3)--语法 1. 字符串格式化的知识补充 tpl = "我是%s,年龄%d,学习进度100%" %('Arroz',18) print(tpl) # 会提 ...

随机推荐

  1. [SQL in Azure] Tutorial: AlwaysOn Availability Groups in Azure (GUI)

    http://msdn.microsoft.com/en-us/library/azure/dn249504.aspx Tutorial: AlwaysOn Availability Groups i ...

  2. 每日英语:Singles Day in China

    Singles Day in China is the celebration -- or mourning -- of being unattached. Started by students i ...

  3. python2/3 中删除字典中value为空的键值对方法

    python2 data_info = { 'account': 1, 'remark': 2, 'sort': '', 'weight': '', } for key in data_info.ke ...

  4. 【delphi】TStringList类常用属性方法详解

    TStringList 常用方法与属性 var List: TStringList; i: Integer; begin List := TStringList.Create; List.Add('S ...

  5. openfire聊天记录插件

    package com.sqj.openfire.chat.logs; import java.io.File; import java.util.Date; import java.util.Lis ...

  6. ffmpeg转码参数设置

    ffmpeg用了很久了,也没有想写点什么. 刚接触ffmpeg也是有大量的不理解的地方,不过慢慢的了解多了基本上都是可以使用的. 本文主要介绍如何使用ffmpeg.exe进行转码.编译好的ffmpeg ...

  7. 调整图像的灰度级数C++实现

    图像灰度级数我们见得最多的就是256了,如果想调整它的灰度级数,我们可以使用图像库的imadjust函数来作出调整,比如讲256个灰度级变成2个灰度级(也就是二值图了).再举一个例子,原来一幅256个 ...

  8. spring batch中MyBatisPagingItemReader分页使用介绍

    假如是mysql的话,SQL语句 <![CDATA[select * from ( SELECT so.* FROM t_tm_sales_order so where so.last_modi ...

  9. 3. 集成学习(Ensemble Learning)随机森林(Random Forest)

    1. 集成学习(Ensemble Learning)原理 2. 集成学习(Ensemble Learning)Bagging 3. 集成学习(Ensemble Learning)随机森林(Random ...

  10. 解决maven项目 maven install失败 错误 Failed to execute goal org.apache.maven.plugins

    1.Maven构建失败 Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin: 2.3.4 :compile ( ...