用布雷森汉姆(Bresenham)算法直接在YUV图上绘制图形

Bresenham的线算法以Jack Elton Bresenham命名,他于1962年在IBM开发。是计算机图形学中的经典算法,恰好最近公司项目中需要实现类似的功能,于是就实践了一把,为了以后爬坑方便,记录在此。

图形转YUV

我们是在YUV裸数据上去绘,而通常的图像都是代容器格式的JPG或者PNG等等,所以需要首先将图片转换为YUV裸数据,我们用FFMPEG这款视频处理领域的瑞士军刀完成此功能。

常用的YUV格式有NV12和NV21两种,,NV12 分两个平面,Y 平面和 UV 平面存储,UV 在同一个平面交叉存储,所以也被称为interleaved,NV12格式也叫IOS模式,同FFMPEG 中的YUV420SP,Y和UV分成两个部分分别存储,NV21格式是Android上的常用格式,格式类似于NV12,只是U和V的顺序相反。

转换命令:

ffmpeg -i car.jpeg -pix_fmt nv12 car.nv12.yuv

原图:

命令不会改变图像的分辨率符合,原图分辨率多少,转换出的YUV分辨率也是多少,如果你需要作SCALE,则事先在原来的图像格式上作SCALE再做YUV转换即可,或者YUV转换过程中通过-s选项也可以达到目的。

ffmpeg -i car.jpeg -pix_fmt nv12 -s 1280x720 scale.nv12.yuv

转换后的YUV用如下命令查看:

ffplay -pix_fmt nv12 -f rawvideo -video_size 3840x2160 ./car.nv12.yuv

如果错把NV12写成了NV21,则图形颜色会发生变化,见下图:

并且文件大小应该是:3840x2160*3/2 = 12441600,和实际值相符。

YUView分析:

放大后看细节每四个Y对应1个U和1个V.

画线

YUV文件已经生成,现在我们可以用Bresenham算法写程序在上面画线了,比如我们绘制从[0,0]开始到[3840,2160]的对角线,程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#define DBG(fmt, ...)   do { printf("%s line %d, "fmt"\n", __func__, __LINE__, ##__VA_ARGS__); } while (0)
static int yuv_width, yuv_height;
static unsigned char *yuvbuf = NULL;

void setpixel(int x,int y)
{
	if(x < 0 || x >= yuv_width)
	{
        DBG("x is not valied %d.", x);
        return;
	}

	if(y < 0 || y >= yuv_height)
	{
        DBG("y is not valied %d.", y);
        return;
	}

	// green yuv 0x96, 0x2c, 0x15
	// red   yuv 0x4c, 0x55, 0xff
	yuvbuf[y * yuv_width + x] = 0x96;
	yuvbuf[yuv_height*yuv_width + (y/2) * yuv_width + x/2 * 2]  = 0x2c;
	yuvbuf[yuv_height*yuv_width + (y/2) * yuv_width + x/2 * 2 + 1]  = 0x15;

    return;
}

void draw_line(int x0, int y0, int x1, int y1)
{
   int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
   int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1; 

   // error value e_xy 
   int err = dx + dy, e2;
 
   for( ; ; )
   {
        setpixel(x0,y0);

        if (x0 == x1 && y0 == y1)
	    {
            break;
	    }

        e2 = 2 * err;

        // e_xy+e_x > 0 
        // e_xy+e_y < 0
        if (e2 >= dy)
        {
            err += dy;
            x0 += sx;
        }

        if (e2 <= dx)
        {
            err += dx;
            y0 += sy;
        }
   }

   return;
}

int main(int argc, char **argv)
{
	FILE *file, *filewrite;
    int width  = atoi(argv[1]);
    int height = atoi(argv[2]);

	yuv_width = width;
	yuv_height = height;

	int size = width * height * 3 / 2;

	yuvbuf = malloc(size);

	if(yuvbuf == NULL)
	{
		DBG("malloc yuvbuf failure.");
		return -1;
	}

	memset(yuvbuf, 0x00, size);

	file = fopen(argv[3], "rb");
	if(file == NULL)
	{
		DBG("fatal error, open file %s failure, please check the file status.", argv[3]);
		return -1;
	}

	filewrite = fopen("lined.yuv", "wb+");
	if(filewrite == NULL)
	{
		DBG("fatal error, open file lined.yuv failure, please check the file status.");
		return -1;
	}

	fseek(file, 0, SEEK_END);
	int filelen = ftell(file);

	DBG("file %s len %d byets.", argv[3], filelen);
	if(filelen != size)
	{
        DBG("yuvdata has been corrupted.size %d", size);
        /*return -1;*/
	}
    
    fseek(file, 0, SEEK_SET);
	if(fread(yuvbuf, 1, filelen, file) != filelen)
	{
		DBG("read file failure, size wrong.");
		return -1;
	}

	draw_line(0, 0, width-1, height-1);

    fseek(filewrite, 0, SEEK_SET);

	if(fwrite(yuvbuf, 1, filelen, filewrite) != filelen)
	{
		DBG("write file failure.");
		return -1;
	}

	fflush(filewrite);
	fsync(fileno(filewrite));

	fclose(file);
	fclose(filewrite);

	return 0;
}
ffplay -pix_fmt nv12 -f rawvideo -video_size 3840x2160 ./lined.yuv

可以看到一条绿色的对角线:

颜色修改为红色:

绘制巴塞尔曲线:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stddef.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <assert.h>

#define DBG(fmt, ...)   do { printf("%s line %d, "fmt"\n", __func__, __LINE__, ##__VA_ARGS__); } while (0)
static int yuv_width, yuv_height;
static unsigned char *yuvbuf = NULL;

void setpixel(int x,int y)
{
	if(x < 0 || x >= yuv_width)
	{
        DBG("x is not valied %d.", x);
        return;
	}

	if(y < 0 || y >= yuv_height)
	{
        DBG("y is not valied %d.", y);
        return;
	}

	// green yuv 0x96, 0x2c, 0x15
	// red   yuv 0x4c, 0x55, 0xff
	yuvbuf[y * yuv_width + x] = 0x96;
	yuvbuf[yuv_height*yuv_width + (y/2) * yuv_width + x/2 * 2]  = 0x2c;
	yuvbuf[yuv_height*yuv_width + (y/2) * yuv_width + x/2 * 2 + 1]  = 0x15;

    return;
}

void draw_line(int x0, int y0, int x1, int y1)
{
   int dx =  abs(x1-x0), sx = x0<x1 ? 1 : -1;
   int dy = -abs(y1-y0), sy = y0<y1 ? 1 : -1; 

   // error value e_xy 
   int err = dx + dy, e2;
 
   for( ; ; )
   {
        setpixel(x0,y0);

        if (x0 == x1 && y0 == y1)
	    {
            break;
	    }

        e2 = 2 * err;

        // e_xy+e_x > 0 
        // e_xy+e_y < 0
        if (e2 >= dy)
        {
            err += dy;
            x0 += sx;
        }

        if (e2 <= dx)
        {
            err += dx;
            y0 += sy;
        }
   }

   return;
}

void plot_quad_bezierseg(int x0, int y0, int x1, int y1, int x2, int y2)
{                            
  int sx = x2-x1, sy = y2-y1;
  long xx = x0-x1, yy = y0-y1, xy;         /* relative values for checks */
  double dx, dy, err, cur = xx*sy-yy*sx;                    /* curvature */

  assert(xx*sx <= 0 && yy*sy <= 0);  /* sign of gradient must not change */

  if (sx*(long)sx+sy*(long)sy > xx*xx+yy*yy) { /* begin with longer part */ 
    x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur;  /* swap P0 P2 */
  }  
  if (cur != 0) {                                    /* no straight line */
    xx += sx; xx *= sx = x0 < x2 ? 1 : -1;           /* x step direction */
    yy += sy; yy *= sy = y0 < y2 ? 1 : -1;           /* y step direction */
    xy = 2*xx*yy; xx *= xx; yy *= yy;          /* differences 2nd degree */
    if (cur*sx*sy < 0) {                           /* negated curvature? */
      xx = -xx; yy = -yy; xy = -xy; cur = -cur;
    }
    dx = 4.0*sy*cur*(x1-x0)+xx-xy;             /* differences 1st degree */
    dy = 4.0*sx*cur*(y0-y1)+yy-xy;
    xx += xx; yy += yy; err = dx+dy+xy;                /* error 1st step */    
    do {                              
      setpixel(x0,y0);                                     /* plot c
  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

papaofdoudou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值