CG15
OpenGL缓冲区、读写操作以及混合(Blending)
一、OpenGL缓冲区概述
OpenGL中的缓冲区是用于存储像素数据的内存区域,主要包括以下类型:
- 颜色缓冲区(Color Buffer):存储每个像素的颜色值。
- 深度缓冲区(Depth Buffer):存储每个像素的深度信息,用于深度测试。
- 模板缓冲区(Stencil Buffer):用于复杂的像素操作,如遮罩和多重渲染通道控制。
- 累积缓冲区(Accumulation Buffer):用于图像的累积操作,如抗锯齿、模糊等。
- 辅助缓冲区(Auxiliary Buffer):提供额外的渲染目标。
- 覆盖缓冲区(Overlay Buffer):用于显示覆盖内容,如HUD界面。
这些缓冲区共同构成了帧缓冲区(Frame Buffer),是OpenGL渲染管线的核心部分。
二、缓冲区的定义
一个缓冲区由以下参数定义:
- 空间分辨率:宽度(n)×高度(m),例如1920×1080。
- 颜色深度(k):每个像素的位数,决定颜色或数据的精度。例如,8位/像素表示256种灰度级别,24位/像素(RGB各8位)表示16.7百万种颜色。
这些参数决定了缓冲区的存储容量和显示精度。
三、OpenGL帧缓冲区结构
帧缓冲区是OpenGL中用于存储最终渲染图像的内存区域,由多个子缓冲区组成:
- 前缓冲区(Front Buffer):当前显示在屏幕上的内容。
- 后缓冲区(Back Buffer):用于后台渲染,完成后与前缓冲区交换,避免闪烁。
- 深度缓冲区(Depth Buffer):用于深度测试,决定像素的可见性。
- 模板缓冲区(Stencil Buffer):用于控制像素是否被绘制,常用于复杂渲染效果。
通过这些缓冲区的协同工作,OpenGL实现了高效的图形渲染。
四、缓冲区的读写操作
写入前读取目标像素,使用组合函数:
d ′ = f ( s , d ) d' = f(s, d) d′=f(s,d)
- s s s:源像素(来自CPU或纹理)
- d d d:目标像素(帧缓冲区已有)
- f f f:如替换、加法、XOR等逻辑运算
1. 写入缓冲区
OpenGL提供了多种函数用于将数据写入缓冲区:
// 设置清除颜色
glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
// 清除颜色缓冲区
glClear(GL_COLOR_BUFFER_BIT);
对于更复杂的写入操作,可以使用以下函数:
// 写入像素数据
glDrawPixels(GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels);
2. 读取缓冲区
从缓冲区读取数据的函数包括:
// 读取像素数据
glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
在读取之前,可以指定读取的缓冲区:
// 指定读取的缓冲区
glReadBuffer(GLenum mode); // 例如:GL_FRONT, GL_BACK
这些操作允许在应用程序中获取渲染结果,用于后续处理或保存。
五、混合(Blending)技术
混合是指在渲染时将源颜色(即将要绘制的颜色)与目标颜色(即当前缓冲区中的颜色)按照一定规则进行组合的过程,常用于实现透明效果或颜色叠加。
1. 启用混合
在使用混合功能前,需要启用混合:
glEnable(GL_BLEND);
2. 设置混合函数
混合函数定义了源颜色和目标颜色的组合方式:
glBlendFunc(GLenum sfactor, GLenum dfactor);
常用的参数包括:
- GL_ZERO:因子为0。
- GL_ONE:因子为1。
- GL_SRC_ALPHA:源颜色的alpha值。
- GL_ONE_MINUS_SRC_ALPHA:1减去源颜色的alpha值。
例如,实现标准的alpha混合:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3. 混合公式
混合的数学公式如下:
C r e s u l t = C s r c × F s r c + C d s t × F d s t C_{result} = C_{src} \times F_{src} + C_{dst} \times F_{dst} Cresult=Csrc×Fsrc+Cdst×Fdst
其中:
- C r e s u l t C_{result} Cresult:混合后的颜色。
- C s r c C_{src} Csrc:源颜色。
- C d s t C_{dst} Cdst:目标颜色。
- F s r c F_{src} Fsrc:源因子。
- F d s t F_{dst} Fdst:目标因子。
通过调整混合函数的参数,可以实现多种视觉效果。
六、逻辑操作(Logical Operations)
XOR交换机制(三次异或可以无损交换两块数据):
S = S ⊕ M; M = S ⊕ M; S = S ⊕ M;
OpenGL提供了逻辑操作功能,用于在像素级别进行位操作,如异或(XOR)、与(AND)、或(OR)等。
1. 启用逻辑操作
在使用逻辑操作前,需要启用该功能:
glEnable(GL_COLOR_LOGIC_OP);
2. 设置逻辑操作模式
使用glLogicOp
函数设置逻辑操作模式:
glLogicOp(GLenum opcode);
常用的操作码包括:
- GL_COPY:直接复制源颜色。
- GL_XOR:源颜色与目标颜色进行异或操作。
- GL_AND:源颜色与目标颜色进行与操作。
- GL_OR:源颜色与目标颜色进行或操作。
例如,使用异或操作:
glLogicOp(GL_XOR);
需要注意,启用逻辑操作后,混合功能将被禁用。
七、位图(Bitmap)操作
位图(bitmap)是 1位图像,作为掩码使用:
- 0 → 不修改帧缓冲区
- 1 → 用当前光栅颜色绘制
典型用途:
- 文本渲染(GLUT字体)
- 光标显示
OpenGL提供了glBitmap
函数,用于绘制1位像素的位图,常用于渲染文本或简单图形。
1. 使用glBitmap
绘制位图
函数定义:
void glBitmap(GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap);
参数说明:
- width, height:位图的宽度和高度。
- xorig, yorig:位图原点相对于当前光栅位置的偏移。
- xmove, ymove:绘制位图后,光栅位置的移动量。
- bitmap:位图数据。
使用示例:
glRasterPos2f(x, y); // 设置光栅位置
glBitmap(width, height, xorig, yorig, xmove, ymove, bitmap);
这将在指定位置绘制位图,并更新光栅位置。
文本绘制与像素操作
一、OpenGL中文本绘制(第19页)
1. 文本渲染方式
OpenGL 支持两种主要文本渲染方式:
- 光栅文本(Raster Text):
- 使用位图(bitmap)表示字符;
- 渲染速度快,适合实时应用;
- 不能放大缩小(不具备矢量特性);
- 实现函数:
glutBitmapCharacter()
。
- 向量文本(Vector Text):
- 使用轮廓曲线或多边形定义字符;
- 可缩放和旋转,但渲染开销大;
- 常用于打印、高质量文本展示;
- 实现函数:如
glutStrokeCharacter()
。
2. 字体与字号
- 示例字体:
Times
,Courier
,Computer Modern
; - 字体大小使用 点(pt) 表示,1 英寸 = 72 点;
- 10pt、24pt 等常用;
- 24pt 在72DPI的显示器上约高1/3英寸。
二、位图图形与绘制示例
棋盘格位图示例
GLubyte wb[2] = {0x00, 0xFF}; // 黑白色像素
GLubyte check[512]; // 512字节,表示64×64位图
for (i = 0; i < 64; i++) {
for (j = 0; j < 8; j++) {
check[i * 8 + j] = wb[(i / 8 + j) % 2];
}
}
glBitmap(64, 64, 0.0, 0.0, 0.0, 0.0, check);
- 每字节表示8个像素(1个位 = 1像素);
- 使用
glBitmap
绘制此图案。
三、图像格式支持(第21~25页)
OpenGL与图像格式
OpenGL 只处理原始像素数据,不直接支持如 JPEG、TIFF 等格式。
格式 | 特点 | OpenGL处理方式 |
---|---|---|
JPEG | 有损压缩,压缩率高 | 解码为RGB数组 |
TIFF | 无损压缩,适合高质量图像 | 解码为像素数组 |
GIF | 256色索引图像,支持简单动画 | 解码为RGB或灰度 |
PS/EPS | 矢量格式,常用于出版 | 不能直接使用,需转为像素 |
PPM | 原始像素格式,易解析 | 适合教学或快速测试 |
结论:需借助第三方库如 SOIL、stb_image、FreeImage 进行图像加载。
四、图像扭曲与矢量化引导
图像的数学定义(第26页)
- 图像是二维函数 I ( x , y ) I(x, y) I(x,y),每个坐标点对应颜色值;
- 在计算机中是离散的,即 f ( i , j ) f(i, j) f(i,j);
- 这种定义为图像处理与分析提供数学基础。
五、图像大小与格式压缩对比(第32页)
- 示例图像:1200 × 1200 像素(RGB,每像素3字节);
- 原始未压缩大小 ≈ 4.12 MB;
- 使用 TIFF(无损压缩)后 ≈ 1.37 MB;
- 说明:压缩格式显著降低存储大小,但需转换为原始数据才能在 OpenGL 中使用。
六、OpenGL像素图像处理流程
像素基本概念
- OpenGL 支持的图像类型:
- 灰度图:1字节/像素;
- RGB 图:3字节/像素;
- 支持三种像素操作:
- 绘制像素(glDrawPixels)
- 读取像素(glReadPixels)
- 复制像素(glCopyPixels)
像素格式与类型
- 像素格式:
GL_RGB
,GL_RGBA
,GL_COLOR_INDEX
; - 数据类型:
GL_UNSIGNED_BYTE
,GL_FLOAT
; - 函数:
glPixelMap
可实现像素值的映射与替换(颜色替换、亮度调整等)。
像素读写函数
// 读取像素
glReadPixels(x, y, width, height, format, type, data);
// 绘制像素
glDrawPixels(width, height, format, type, data);
- 用于图像保存、截图或离屏处理。
图像数据缩放
glPixelTransferf(GL_RED_SCALE, s);
glPixelTransferf(GL_GREEN_SCALE, s);
glPixelTransferf(GL_BLUE_SCALE, s);
- 调整颜色通道的缩放因子 s s s,用于图像增强;
- 开启字节交换以适应不同平台字节序:
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
像素读取示例
glReadPixels(200, 200, 200, 200, GL_RGB, GL_UNSIGNED_BYTE, p);
- 将中心区域200×200像素读取到内存,存入
char p[120000]
。
像素绘制示例
glRasterPos2f(-1.0, -1.0);
glDrawPixels(200, 200, GL_RGB, GL_UNSIGNED_BYTE, p);
- 将刚才读取的数据绘制到窗口左下角,实现“像素复制”。
图像缩放(第39页)
glPixelZoom(xscale, yscale);
控制
glDrawPixels
的缩放比例;xscale/yscale:
-
1:放大;
<1:缩小;
<0:翻转;
-
示例:
glPixelZoom(2.0, 2.0); // 放大2倍
glPixelZoom(0.5, 0.5); // 缩小1/2
显示回调函数
void display() {
glClear(GL_COLOR_BUFFER_BIT);
glRasterPos2i(0, 0);
glDrawPixels(n, m, GL_RGB, GL_UNSIGNED_INT, image);
glFlush();
}
- 用于注册到 GLUT 显示回调;
- 核心流程包括:清屏 → 设置光栅位置 → 绘图 → 刷新
Convex and Compact Superpixels byEdge-Constrained Centroidal Power Diagram
一、基本背景:什么是“超像素”?
在图像处理中,**像素(pixel)**是图像的最小单元,每个像素表示一个颜色值。但是像素是按网格排列的,不携带高级结构信息。
于是人们提出了**超像素(superpixel)**的概念:
- 将相邻、颜色或纹理相似的像素聚合成一个区域。
- 每个区域就称为一个“超像素”。
- 好处是:更紧凑的表示、更少的计算、更易于分析。
举例:
- 100x100像素图像有1万个像素。
- 用200个超像素表示它,可以显著减少处理开销。
- 在目标检测、分割、压缩中,超像素是常用的预处理步骤。
二、动机:为什么要“凸且紧凑”的超像素?
“凸”(convex)意味着一个区域中任意两点之间的直线仍在区域内部,这种形状:
- 更规则,边界清晰。
- 更适合后续矢量化或对象检测。
“紧凑”(compact)意味着区域面积小,形状接近圆形或正方形:
- 降低冗余。
- 保持每个区域的信息集中。
三、传统方法:Voronoi图与质心Voronoi图(CVT)
**Voronoi图(Voronoi Diagram)**是一个经典的几何工具:
给定一组点(称为“站点”),它把空间划分为若干个区域,使得:
每个区域包含所有离某个站点最近的点。
数学定义:
v i = { x ∈ Ω ∣ ∥ x − x i ∥ ≤ ∥ x − x j ∥ , ∀ j ≠ i } v_i = \{ x \in \Omega \mid \|x - x_i\| \leq \|x - x_j\|,\ \forall j \neq i \} vi={x∈Ω∣∥x−xi∥≤∥x−xj∥, ∀j=i}
进一步优化后,就得到:
质心Voronoi图(Centroidal Voronoi Tessellation, CVT):
- 每个站点位于其区域的“质心”上。
- 实现区域形状优化,使区域更规则、对称。
但问题是:CVT无法直接控制区域大小,不利于图像复杂区域的自适应划分。
四、改进:功率图(Power Diagram)与质心功率图(CPD)
**功率图(Power Diagram)**是在Voronoi图上加权:
数学形式:
v i pow = { x ∈ Ω ∣ ∥ x − x i ∥ 2 − w i ≤ ∥ x − x j ∥ 2 − w j } v_i^{\text{pow}} = \{ x \in \Omega \mid \|x - x_i\|^2 - w_i \leq \|x - x_j\|^2 - w_j \} vipow={x∈Ω∣∥x−xi∥2−wi≤∥x−xj∥2−wj}
特点:
- 每个站点x_i有一个权重w_i。
- 权重越大,对应的区域越小。
- 可以控制每个超像素的大小。
结合CVT与功率图,就得到:
质心功率图(Centroidal Power Diagram, CPD)
- 站点位于其功率区域的质心。
- 引入权重,使区域大小可调节。
五、ECCPD算法:论文核心方法
ECCPD = Edge-Constrained Centroidal Power Diagram
(1)优化目标函数:
E ( X , W ) = ∑ i = 1 N ∫ P W i ∥ x − x i ∥ 2 d σ + λ ∑ i = 1 N ( w i − Weight ( x i ) ) 2 E(X, W) = \sum_{i=1}^{N} \int_{P_{W_i}} \|x - x_i\|^2 d\sigma + \lambda \sum_{i=1}^N (w_i - \text{Weight}(x_i))^2 E(X,W)=i=1∑N∫PWi∥x−xi∥2dσ+λi=1∑N(wi−Weight(xi))2
解释:
- 第一项:区域内点到站点的距离平方和 → 控制紧凑性。
- 第二项:当前权重与图像特征(如边缘、颜色)的偏差平方和 → 控制适应性。
- λ:平衡这两部分的系数。
最终目标:调整站点位置x_i和权重w_i,使E最小。
六、ECCPD算法流程
- 初始化:
- 输入图像。
- 设定初始站点位置(随机或边缘引导)。
- 分配初始权重(可设为常数或边缘相关)。
- 迭代优化:
- 计算每个区域的质心。
- 调整站点到质心。
- 更新权重。
- 后处理:
- 使边界与图像边缘对齐。
- 修正区域形状以提高凸性。
七、权重函数的设计:结合图像内容自适应
使用了距离场函数:
Dist ( p ) = v ⋅ Eudist ( p ) \text{Dist}(p) = v \cdot \text{Eudist}(p) Dist(p)=v⋅Eudist(p)
含义:
- Eudist§:点p到最近边缘的欧氏距离。
- v:缩放因子(越大则边缘区域更小)。
效果:
- 边缘区域(Eudist小)→ 超像素小 → 保留边缘细节。
- 内部区域(Eudist大)→ 超像素大 → 减少不必要的细节。
还结合了颜色差异,调整相邻区域颜色差异大的权重,增强内容敏感性。
八、实验分析与结果可视化
- 数据集:BSDS500, PASCAL-S。
- 评估指标:
- COM:紧凑性(越高越好)。
- BR:边界召回率。
- USE:未分割率(越低越好)。
- ASA:平均分割精度(越高越好)。
结果显示:ECCPD在保持高精度的同时,紧凑性领先于所有对比方法。
九、应用举例
1. 图像压缩:
- 用几千个凸超像素替代十几万像素。
- 减少存储或传输成本。
2. 多边形轮廓提取:
- 每个凸超像素是多边形 → 可直接提取对象轮廓。