概述
在该例程中,我们将从底层设计一个直线绘制算法,并借助OpenCV将结果展示出来。
例程引入了模块化的设计,拆分了三个模块
- 内存图像模块,封装了和内存图像相关的操作。
- 直线模块,封装了直线元素的算法逻辑。
- 主函数逻辑模块,封装了我们想要实现的功能逻辑。
绘制直线是经典计算机图形学的基础算法之一,是传统的格栅化渲染过程中的一个基础方法,当然我们不会过多阐述经典计算机图形学的理论,在例程最后,可以看到我们在内存图像中绘制出来了一条直线,尝试着改变一下直线的方向角度,还有颜色,很有意思。
核心内容实践
Step:1:模块化工程
- Common.h用来存放一些宏定义和公有库
- Line.h Line.cpp 是用来构建绘制直线相关的元素
- MemoryImage.h MemoryImage.cpp 是用来构建内存图像相关的元素
- main.cpp 整个工程的绘制逻辑在该文件中
Step:2:设计内存图片清空逻辑
背景清空方法将我们的内存图像至为了指定的颜色,我们通过逐个访问内存图像的像素,将其各个通道置为指定的RGB颜色。
void ClearBGRMemoryImage(unsigned char* pImage, int nWidth, int nHeight, unsigned char R, unsigned char G, unsigned char B)
{
//内存图像通道数
int nImgChannel = 3;
//内存图像遍历索引
int nPixelIndex = 0;
unsigned char* pMemoryImage = pImage;
//逐像素访问内存图像
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
//更新内存图像BGR通道的值
pMemoryImage[nPixelIndex++] = B; // B channel
pMemoryImage[nPixelIndex++] = G; // G channel
pMemoryImage[nPixelIndex++] = R; // R channel
}
}
Step:3:直线绘制算法
Line.h文件封装了直线的绘制方法,有兴趣的读者可以查阅相关资料,理解一下直线的绘制的过程。该算法采用Bresenham算法实现。
bresenham算法是计算机图形学中为了“显示器(屏幕或打印机)系由像素构成”的这个特性而设计出来的算法,使得在求直线各点的过程中全部以整数来运算,因而大幅度提升计算速度。是计算机图形学领域使用最广泛的直线扫描转换方法。其原理是:
- 1.过各行、各列像素中心构造一组虚拟网格线,
- 2.按直线从起点到终点的顺序计算直线各垂直网格线的交点,
- 3.然后确定该列像素中与此交点最近的像素。
该算法的优点在于可以采用增量计算,使得对于每一列,只要检查一个误差项的符号,就可以确定该列所求的像素。
核心算法实现:
//pImage 指定的内存图像
//startX 直线的起点x坐标
//startY 直线的起点y坐标
//endX 直线的结束点x坐标
//endY 直线的结束点y坐标
bool DrarLineAtImage(unsigned char* pImage,int nWidth,int nHeight, int startX, int startY, int endX, int endY,unsigned char R,unsigned char G,unsigned char B)
{
int dx = 0;
int dy = 0;
int s1 = 0;
int s2 = 0;
int e = 0;
dx = abs(endX - startX);
dy = abs(endY - startY);
s1 = Sign(endX - startX);
s2 = Sign(endY - startY);
bool InterChange = false;
int nTemp = 0;
if (dx < dy)
{
nTemp = dx;
dx = dy;
dy = nTemp;
InterChange = true;
}
e = -dx;
int tempX = startX;
int tempY = startY;
for (int i = 0; i<dx; i++)
{
SetBGRPixel(pImage, tempX, tempY, nWidth, nHeight, R, G, B);
if (InterChange)
tempY += s2;
else
tempX += s1;
e += 2 * dy;
if (e>0)
{
if (InterChange)
tempX += s1;
else
tempY += s2;
e -= 2 * dx;
}
}
return true;
}
主函数逻辑为:
/***************************************************************************
*
* 本例程是教大家如何在内存图像中绘制一条直线
* 2018-7-21 in Xi'an
* by hzhy
*
*****************************************************************************/
#include <iostream>
#include "MemoryImage.h"
#include "Line.h"
using namespace std;
int main(int argc, char ** argv)
{
int nIMGWidth = 640;
int nIMGHeight = 480;
//create memory image buffer
unsigned char* theMemoryImage = CreateBGRMemoryImage(nIMGWidth, nIMGHeight);
ClearBGRMemoryImage(theMemoryImage, nIMGWidth, nIMGHeight, 255, 255, 255);
DrarLineAtImage(theMemoryImage, nIMGWidth, nIMGHeight, 50, 300, 540, 40, 255, 0, 255);
//依次点亮像素
ShowBGRImage(theMemoryImage, nIMGWidth, nIMGHeight);
cvWaitKey(0);
delete[] theMemoryImage;
return 0;
}
最终效果展示:
绘制一条黑色直线
DrarLineAtImage(theMemoryImage, nIMGWidth, nIMGHeight, 50, 300, 540, 40, 0, 0, 0);
绘制一条紫色直线
DrarLineAtImage(theMemoryImage, nIMGWidth, nIMGHeight, 50, 300, 540, 40, 0, 0, 0);