直线的扫描转换算法

这是第一篇博客,顺便深入学习一下markdown语法。
之后的图形学文章都使用C++实现。

目录

数值微分法

数值微分法通过计算直线的斜率,计算每个x对应的y坐标,并取像素x,round(y)。本方法直观可行,但执行效率低。由于每步都需要进行浮点乘法运算与舍入运算,故需要增量的方法。

算法理论

首先计算直线的斜率:

k=Δy/Δx k = Δ y / Δ x
接着推导增量方式计算y坐标:
yi+1=kxi+1+B=k(xi+Δx)+B=kxi+kΔx+B=yi+kΔx(1) (1) y i + 1 = k x i + 1 + B = k ( x i + Δ x ) + B = k x i + k Δ x + B = y i + k Δ x
由于 Δx=1 Δ x = 1 ,所以每次 y y 递增k

算法实现

利用openCV+Cpp进行实现。考虑了 |k|[0,1]|k|[0,1] | k | ∈ [ 0 , 1 ] 与 | k | ∉ [ 0 , 1 ] 两种情形

#include<iostream>
#include<cmath>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>

using namespace cv;

void DDAline(Mat& m,const int x0, const int y0, const int x1, const int y1,const cv::Vec3b& v);

int main(){
    Mat imageROI = Mat(500, 1000, CV_8UC3, Scalar(255, 255, 255));
    DDAline(imageROI, 0, 0,400, 200, Vec3b(0, 0, 0));
    namedWindow("显示结果");
    imshow("显示结果", imageROI);
    waitKey();
}

void DDAline(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v){
    double k = double(y1 - y0) / (x1 - x0);
    if (k >= -1 && k <= 1){
        double y = y0;
        int x = x0;
        for (x = x0; x <= x1; x++){
            m.at<Vec3b>(int(y + 0.5),x ) = v;
            y += k;
        }
    }
    else{
        double x = x0;
        int y = y0;
        k = 1 / k;
        for (y = y0; y <= y1; y++){
            m.at<Vec3b>(y,int(x+0.5)) = v;
            x += k;
        }
    }
}

这里写图片描述
这里注意opencv中at(i0,i1)函数是操作第i0行第i1列,所以需要把xy反过来。
总体还是比较简单。cv配置、函数搞的比较麻烦。opencv还需多多熟练。

中点画线法

算法理论

首先假定 k[0,1] k ∈ [ 0 , 1 ] 之间。
根据 (x0,y0)(x1,y1) ( x 0 , y 0 ) ( x 1 , y 1 ) 两点坐标可以得到直线方程:

F(x,y)=ax+by+c F ( x , y ) = a x + b y + c
其中, a=y0y1,b=x1x0,c=x0y1x1y0 a = y 0 − y 1 , b = x 1 − x 0 , c = x 0 y 1 − x 1 y 0
F(x,y)>0 F ( x , y ) > 0 时,点在直线上方。当确定P点位置后,下一个像素点处的中点M的判别式为:
F(M)=F(x+1,y+0.5)=F(x,y)+a+0.5b F ( M ) = F ( x + 1 , y + 0.5 ) = F ( x , y ) + a + 0.5 b
此时判断符号。若M点在直线上方,则选M下方的像素点。那么下一个需要判断的中点的判别式为:
F(xp+2,yp+0.5)=F(M)+a F ( x p + 2 , y p + 0.5 ) = F ( M ) + a
如果M点在直线下方,则选上方的像素点,下一个中点的判别式为
F(xp+2,yp+1.5)=F(M)+a+b F ( x p + 2 , y p + 1.5 ) = F ( M ) + a + b
而F(M)的初始值:
F(M0)=F(x0,y0)+a+0.5b=a+0.5b F ( M 0 ) = F ( x 0 , y 0 ) + a + 0.5 b = a + 0.5 b

算法实现

void MidpointLine(Mat& m, const int x0, const int y0, const int x1, const int y1, const cv::Vec3b& v){
    int a = y0 - y1;
    int b = x1 - x0;
    int d0 = 2*a + b;
    int delta1 = 2*a;
    int delta2 = 2*(a + b);
    int x, y, d;
    for (x = x0, y = y0, d = d0; x <= x1; x++){
        if (d < 0){
            y++;
            d += delta2;
        }
        else{
            d += delta1;
        }
        m.at<Vec3b>(y, x) = v;
    }
}

由于把d的判别全部乘2了,可以避免小数的运算。斜率在其他范围内只要换换前后两点的顺序、换换x,y,就可以解决。

Bresenham画线算法

算法理论

还是首先假定 k[0,1] k ∈ [ 0 , 1 ]
假设x列的像素已确定,那么下一个像素列坐标为x+1,行下标为y或y+1,具体是哪个取决于误差项d大于0.5还是小于0.5。每次x增加1,则误差随之增加k。程序中设e=d-0.5,则可判断e的正负值。当 d>1 d > 1 时,则只需执行 d=d1 d = d − 1

算法实现

void BresenhamLine(Mat& m, const int x0, const int y0, const int x1, const int y1, const Vec3b& v){
    int x, y, dx, dy;
    float k, e;
    dx = x1 - x0;
    dy = y1 - y0;
    k = double(dy) / dx;
    e = -0.5;
    for (x = x0, y = y0; x <= x1; x++){
        m.at<Vec3b>(y, x) = v;
        e += k;
        if (e >= 0){
            y++;
            e--;
        }
    }
}

以上。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值