概述

在该例程中,我们将从底层设计一个直线绘制算法,并借助OpenCV将结果展示出来。

例程引入了模块化的设计,拆分了三个模块

  • 内存图像模块,封装了和内存图像相关的操作。
  • 直线模块,封装了直线元素的算法逻辑。
  • 主函数逻辑模块,封装了我们想要实现的功能逻辑。

绘制直线是经典计算机图形学的基础算法之一,是传统的格栅化渲染过程中的一个基础方法,当然我们不会过多阐述经典计算机图形学的理论,在例程最后,可以看到我们在内存图像中绘制出来了一条直线,尝试着改变一下直线的方向角度,还有颜色,很有意思。

核心内容实践

Step:1:模块化工程

  • Common.h用来存放一些宏定义和公有库
  • Line.h Line.cpp 是用来构建绘制直线相关的元素
  • MemoryImage.h MemoryImage.cpp 是用来构建内存图像相关的元素
  • main.cpp 整个工程的绘制逻辑在该文件中

opencv 直线 判断_算法

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算法实现。

opencv 直线 判断_opencv_02

bresenham算法是计算机图形学中为了“显示器(屏幕或打印机)系由像素构成”的这个特性而设计出来的算法,使得在求直线各点的过程中全部以整数来运算,因而大幅度提升计算速度。是计算机图形学领域使用最广泛的直线扫描转换方法。其原理是:

  1. 1.过各行、各列像素中心构造一组虚拟网格线,
  2. 2.按直线从起点到终点的顺序计算直线各垂直网格线的交点,
  3. 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);

opencv 直线 判断_opencv_03

绘制一条紫色直线

DrarLineAtImage(theMemoryImage, nIMGWidth, nIMGHeight, 50, 300, 540, 40, 0, 0, 0);

opencv 直线 判断_opencv_04