百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

[图像处理] YUV图像处理入门3(图象处理)

bigegpt 2024-08-02 10:53 8 浏览

5 yuv420格式的灰阶测试图

本程序中的函数主要是为YUV420P视频数据流的第一帧图像添加边框。函数的代码如下所示:


/**
 * @file 5 yuv_graybar.cpp
 * @author luohen
 * @brief gray scale bar of yuv
 * @date 2018-12-07
 *
 */

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>

using namespace std;

/**
 * @brief
 *
 * @param width        width of input yuv420p file
 * @param height    height of input yuv420p file
 * @param ymin        minimum value of y
 * @param ymax        maximum value of y
 * @param barnum     Number of bars
 * @param url        location of input yuv420p file
 * @return int
 */
int yuv420_graybar(int width, int height, int ymin, int ymax, int barnum, const char *url)
{
    //每个灰度条的宽度
    int barwidth;
    //每个灰度阶次范围
    float lum_inc;
    //计算Y值
    unsigned char lum_temp;
    //uv分量宽高
    int uv_width, uv_height;
    //reading yuv image
    FILE *input_fp;
    if ((input_fp = fopen(url, "rb")) == NULL)
    {
        printf("%s open error!\n", url);
        return -1;
    }
    else
    {
        printf("%s open.\n", url);
    }
    //writing yuv image
    FILE *output_fp = fopen("video_result/gray_test.yuv", "wb+");

    int t = 0, i = 0, j = 0;

    //每个灰度条的宽度
    barwidth = width / barnum;
    //每个灰度阶次范围
    lum_inc = ((float)(ymax - ymin)) / ((float)(barnum - 1));
    //uv分量宽高
    uv_width = width / 2;
    uv_height = height / 2;

    unsigned char *data_y = new unsigned char[width * height];
    unsigned char *data_u = new unsigned char[uv_width * uv_height];
    unsigned char *data_v = new unsigned char[uv_width * uv_height];

    //Output Info
    //输出信息
    printf("Y, U, V value from picture's left to right:\n");
    for (t = 0; t < (width / barwidth); t++)
    {
        //计算Y值
        lum_temp = ymin + (char)(t * lum_inc);
        printf("%3d, 128, 128\n", lum_temp);
    }
    //保存数据
    for (j = 0; j < height; j++)
    {
        for (i = 0; i < width; i++)
        {
            t = i / barwidth;
            lum_temp = ymin + (char)(t * lum_inc);
            data_y[j * width + i] = lum_temp;
        }
    }
    for (j = 0; j < uv_height; j++)
    {
        for (i = 0; i < uv_width; i++)
        {
            data_u[j * uv_width + i] = 128;
        }
    }
    for (j = 0; j < uv_height; j++)
    {
        for (i = 0; i < uv_width; i++)
        {
            data_v[j * uv_width + i] = 128;
        }
    }

    fwrite(data_y, width * height, sizeof(unsigned char), output_fp);
    fwrite(data_u, uv_width * uv_height, sizeof(unsigned char), output_fp);
    fwrite(data_v, uv_width * uv_height, sizeof(unsigned char), output_fp);
    fclose(input_fp);
    fclose(output_fp);

    delete[] data_y;
    delete[] data_u;
    delete[] data_v;
    return 0;
}

/**
 * @brief main
 *
 * @return int
 */
int main()
{
    int state = yuv420_graybar(640, 360, 0, 255, 10, "video/graybar.yuv");
    return 0;
}

调用函数为:


int yuv420_graybar(int width, int height, int ymin, int ymax, int barnum, const char *url);

实际上这部分代码和前面代码差不多,先取得YUV数据流,类似一个一维数组,读第一帧图像,然后依次读到y,u,v三个分量起始位置,再对y,u,v的像素值分别进行处理。

结果如图所示:

添加图片注释,不超过 140 字(可选)


6 两张yuv420p图像的峰值信噪比(psnr)计算

本程序中的函数主要是比较两张yuv420p图像的峰值信噪。函数的代码如下所示:


/**
 * @file 6 yuv420_psnr.cpp
 * @author luohen
 * @brief Compute the PSNR values of two yuv files
 * @date 2018-12-08
 *
 */

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>

using namespace std;

/**
 * @brief
 *
 * @param url1    location of input yuv420p file1
 * @param url2    location of input yuv420p file2
 * @param w        width of input yuv420p file
 * @param h        height of input yuv420p file
 * @return int
 */
int yuv420_psnr(const char *url1, const char *url2, int w, int h)
{
    //reading yuv iamges
    FILE *fp1 = fopen(url1, "rb+");
    FILE *fp2 = fopen(url2, "rb+");

    unsigned char *pic1 = new unsigned char[w * h];
    unsigned char *pic2 = new unsigned char[w * h];

    fread(pic1, 1, w * h, fp1);
    fread(pic2, 1, w * h, fp2);

    double mse_sum = 0, mse = 0, psnr = 0;
    //computing mse
    for (int j = 0; j < w * h; j++)
    {
        mse_sum += pow((double)(pic1[j] - pic2[j]), 2);
    }
    mse = mse_sum / (w * h);
    //computing psnr
    psnr = 10 * log10(255.0 * 255.0 / mse);
    printf("%5.3f\n", psnr);

    delete[] pic1;
    delete[] pic2;
    fclose(fp1);
    fclose(fp2);
    return 0;
}

/**
 * @brief main
 *
 * @return int
 */
int main()
{
    int state = yuv420_psnr("video/akiyo.yuv", "video/distort_akiyo.yuv", 352, 288);
    return 0;
}

调用函数为:


int yuv420_psnr(const char *url1, const char *url2, int w, int h);

这段代码主要是计算两张图像的接近程度,psnr值具体介绍可以见文章:

https://www.cnblogs.com/ranjiewen/p/6390846.html

本文所用的两张图像一张是akiyo视频流首帧图像,另外一张是前面为akiyo加上边框的图像。两张图像的psnr值为13.497。一般psnr值越大两张图像越接近。

7 yuv420图像顺时针旋转90度

本程序中的函数主要是将YUV420P视频数据流的第一帧图像顺时针旋转90度。函数的代码如下所示:


/**
 * @file 7 yuv_Rotation90.cpp
 * @author luohen
 * @brief 90 degree rotation of yuv420 images
 * @date 2018-12-08
 *
 */

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <iostream>

using namespace std;

/**
 * @brief Pre-defined image size
 *
 */
#define image_h 288
#define image_w 352

/**
  * @brief
  *
  * @param url location of input yuv420p file
  * @return int
  */
int yuv420_Rotation90(const char *url)
{
    //reading yuv files
    FILE *input_fp;
    //writingyuv files
    FILE *output_fp = fopen("video_result/output_rotation.yuv", "wb+");

    //reading yuv datas
    if ((input_fp = fopen(url, "rb")) == NULL)
    {
        printf("%s open error!\n", url);
        return -1;
    }
    else
    {
        printf("%s open.\n", url);
    }

    //Input image array definition
    unsigned char input_Y[image_h][image_w];
    unsigned char input_U[image_h / 2][image_w / 2];
    unsigned char input_V[image_h / 2][image_w / 2];

    //Output image array definition
    unsigned char output_Y[image_w][image_h];
    unsigned char output_U[image_w / 2][image_h / 2];
    unsigned char output_V[image_w / 2][image_h / 2];

    int w = image_w;
    int h = image_h;

    fread(input_Y, sizeof(unsigned char), w * h, input_fp);
    fread(input_U, sizeof(unsigned char), w / 2 * h / 2, input_fp);
    fread(input_V, sizeof(unsigned char), w / 2 * h / 2, input_fp);

    //Y 90 degree rotation
    for (int x = 0; x < h; x++)
    {
        for (int y = 0; y < w; y++)
        {
            //旋转之后,输出的x值等于输入的y坐标值
            //y值等于输入列高-输入x坐标值-1
            output_Y[y][h - x - 1] = input_Y[x][y];
        }
    }

    //u 90 degree rotation
    for (int x = 0; x < h / 2; x++)
    {
        for (int y = 0; y < w / 2; y++)
        {
            //旋转之后,输出的x值等于输入的y坐标值
            //y值等于输入列高-输入x坐标值-1
            output_U[y][h / 2 - x - 1] = input_U[x][y];
        }
    }

    //v 90 degree rotation
    for (int x = 0; x < h / 2; x++)
    {
        for (int y = 0; y < w / 2; y++)
        {
            //旋转之后,输出的x值等于输入的y坐标值
            //y值等于输入列高-输入x坐标值-1
            output_V[y][h / 2 - x - 1] = input_V[x][y];
        }
    }

    fwrite(output_Y, sizeof(unsigned char), w * h, output_fp);
    fwrite(output_U, sizeof(unsigned char), w / 2 * h / 2, output_fp);
    fwrite(output_V, sizeof(unsigned char), w / 2 * h / 2, output_fp);

    fclose(input_fp);
    fclose(output_fp);

    return 0;
}

/**
 * @brief main
 *
 * @return int
 */
int main()
{
    int state = yuv420_Rotation90("video/akiyo.yuv");
    return 0;
}

调用函数为:


int yuv420_Rotation90(const char *url);

这段代码主要是分别提取yuv分量,然后将y,u,v分量分别旋转90度。但是提取yuv分量和以前的代码有所不同。

首先是建立yuv三个分量输入的静态二维数组,相比使用动态数组,这种方式处理数据简单很多,但是需要实现确定输入图像的大小。


unsigned char input_Y[image_h][image_w];

unsigned char input_U[image_h / 2][image_w / 2];

unsigned char input_V[image_h / 2][image_w / 2];

然后建立旋转后的输出数组,输出数组定义是,由于是旋转90度,长宽进行了对调。


unsigned char output_Y[image_w][image_h];

unsigned char output_U[image_w / 2][image_h / 2];

unsigned char output_V[image_w / 2][image_h / 2];

其他旋转操作,就是图像赋值过程。旋转后akiyo图像尺寸变为(288,352)

结果如图所示:

添加图片注释,不超过 140 字(可选)


8 yuv420图像大小重置

本程序中的函数主要是对YUV420P视频数据流的第一帧图像进行缩放或者放大。类似opencv中的resize函数,函数的代码如下所示:


/**
 * @file 8 yuv_resize.cpp
 * @author luohen
 * @brief adjusting yuv image size
 * @date 2018-12-08
 *
 */

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <string.h>
#include <iostream>

using namespace std;

#define HEIGHT 288
#define WIDTH 352

/**
 * @brief 
 * 
 * @param url            location of input yuv420p file
 * @param out_width        output image width
 * @param out_height    output image height
 * @return int 
 */
int yuv420_resize(const char *url, int out_width, int out_height)
{
    //input array
    unsigned char yin[HEIGHT][WIDTH];
    unsigned char uin[HEIGHT / 2][WIDTH / 2];
    unsigned char vin[HEIGHT / 2][WIDTH / 2];
    //output array
    unsigned char *yout = new unsigned char[out_width * out_height];
    unsigned char *uout = new unsigned char[out_width / 2 * out_height / 2];
    unsigned char *vout = new unsigned char[out_width / 2 * out_height / 2];
    ///reading yuv file
    FILE *input_fp;
    //writing yuv file
    FILE *output_fp = fopen("video_result/output_resize.yuv", "wb+");

    if ((input_fp = fopen(url, "rb")) == NULL)
    {
        printf("%s open error!\n", url);
        return -1;
    }
    else
    {
        printf("%s open.\n", url);
    }

    fread(yin, sizeof(unsigned char), HEIGHT * WIDTH, input_fp);
    fread(uin, sizeof(unsigned char), HEIGHT * WIDTH / 4, input_fp);
    fread(vin, sizeof(unsigned char), HEIGHT * WIDTH / 4, input_fp);

    //Y
    for (int i = 0; i < out_height; i++)
    {
        for (int j = 0; j < out_width; j++)
        {
            int i_in = round(i * HEIGHT / out_height);
            int j_in = round(j * WIDTH / out_width);
            yout[i * out_width + j] = yin[i_in][j_in];
        }
    }

    //U
    for (int i = 0; i < out_height / 2; i++)
    {
        for (int j = 0; j < out_width / 2; j++)
        {
            int i_in = round(i * (HEIGHT / 2) / (out_height / 2));
            int j_in = round(j * (WIDTH / 2) / (out_width / 2));
            uout[i * out_width / 2 + j] = uin[i_in][j_in];
        }
    }

    //V
    for (int i = 0; i < out_height / 2; i++)
    {
        for (int j = 0; j < out_width / 2; j++)
        {
            int i_in = round(i * (HEIGHT / 2) / (out_height / 2));
            int j_in = round(j * (WIDTH / 2) / (out_width / 2));
            vout[i * out_width / 2 + j] = vin[i_in][j_in];
        }
    }

    fwrite(yout, sizeof(unsigned char), out_width * out_height, output_fp);
    fwrite(uout, sizeof(unsigned char), out_width * out_height / 4, output_fp);
    fwrite(vout, sizeof(unsigned char), out_width * out_height / 4, output_fp);

    delete[] yout;
    delete[] uout;
    delete[] vout;
    fclose(input_fp);
    fclose(output_fp);

    return 0;
}

/**
 * @brief main
 *
 * @return int
 */
int main()
{
    int state = yuv420_resize("video/akiyo.yuv", 288, 352);
    return 0;
}

调用函数为:


int yuv420_resize(const char *url, int out_width, int out_height);

这段代码也是通过事先设定yuv输入输出的静态二维数组来进行处理的。其中out_width, out_height

是输出图像的宽高,这段代码中输出图像的宽高可以设定为任意值。所用图像resize方法是最简单的最邻近插值法。

插值方法见文章:

https://blog.csdn.net/caomin1hao/article/details/81092134

当设置调整后的图像宽高为288,352时,结果如下:

添加图片注释,不超过 140 字(可选)


引用链接

[1] https://www.cnblogs.com/ranjiewen/p/6390846.html: https://www.cnblogs.com/ranjiewen/p/6390846.html

[2] https://blog.csdn.net/caomin1hao/article/details/81092134: https://blog.csdn.net/caomin1hao/article/details/81092134

相关推荐

当Frida来“敲”门(frida是什么)

0x1渗透测试瓶颈目前,碰到越来越多的大客户都会将核心资产业务集中在统一的APP上,或者对自己比较重要的APP,如自己的主业务,办公APP进行加壳,流量加密,投入了很多精力在移动端的防护上。而现在挖...

服务端性能测试实战3-性能测试脚本开发

前言在前面的两篇文章中,我们分别介绍了性能测试的理论知识以及性能测试计划制定,本篇文章将重点介绍性能测试脚本开发。脚本开发将分为两个阶段:阶段一:了解各个接口的入参、出参,使用Python代码模拟前端...

Springboot整合Apache Ftpserver拓展功能及业务讲解(三)

今日分享每天分享技术实战干货,技术在于积累和收藏,希望可以帮助到您,同时也希望获得您的支持和关注。架构开源地址:https://gitee.com/msxyspringboot整合Ftpserver参...

Linux和Windows下:Python Crypto模块安装方式区别

一、Linux环境下:fromCrypto.SignatureimportPKCS1_v1_5如果导包报错:ImportError:Nomodulenamed'Crypt...

Python 3 加密简介(python des加密解密)

Python3的标准库中是没多少用来解决加密的,不过却有用于处理哈希的库。在这里我们会对其进行一个简单的介绍,但重点会放在两个第三方的软件包:PyCrypto和cryptography上,我...

怎样从零开始编译一个魔兽世界开源服务端Windows

第二章:编译和安装我是艾西,上期我们讲述到编译一个魔兽世界开源服务端环境准备,那么今天跟大家聊聊怎么编译和安装我们直接进入正题(上一章没有看到的小伙伴可以点我主页查看)编译服务端:在D盘新建一个文件夹...

附1-Conda部署安装及基本使用(conda安装教程)

Windows环境安装安装介质下载下载地址:https://www.anaconda.com/products/individual安装Anaconda安装时,选择自定义安装,选择自定义安装路径:配置...

如何配置全世界最小的 MySQL 服务器

配置全世界最小的MySQL服务器——如何在一块IntelEdison为控制板上安装一个MySQL服务器。介绍在我最近的一篇博文中,物联网,消息以及MySQL,我展示了如果Partic...

如何使用Github Action来自动化编译PolarDB-PG数据库

随着PolarDB在国产数据库领域荣膺桂冠并持续获得广泛认可,越来越多的学生和技术爱好者开始关注并涉足这款由阿里巴巴集团倾力打造且性能卓越的关系型云原生数据库。有很多同学想要上手尝试,却卡在了编译数据...

面向NDK开发者的Android 7.0变更(ndk android.mk)

订阅Google官方微信公众号:谷歌开发者。与谷歌一起创造未来!受Android平台其他改进的影响,为了方便加载本机代码,AndroidM和N中的动态链接器对编写整洁且跨平台兼容的本机...

信创改造--人大金仓(Kingbase)数据库安装、备份恢复的问题纪要

问题一:在安装KingbaseES时,安装用户对于安装路径需有“读”、“写”、“执行”的权限。在Linux系统中,需要以非root用户执行安装程序,且该用户要有标准的home目录,您可...

OpenSSH 安全漏洞,修补操作一手掌握

1.漏洞概述近日,国家信息安全漏洞库(CNNVD)收到关于OpenSSH安全漏洞(CNNVD-202407-017、CVE-2024-6387)情况的报送。攻击者可以利用该漏洞在无需认证的情况下,通...

Linux:lsof命令详解(linux lsof命令详解)

介绍欢迎来到这篇博客。在这篇博客中,我们将学习Unix/Linux系统上的lsof命令行工具。命令行工具是您使用CLI(命令行界面)而不是GUI(图形用户界面)运行的程序或工具。lsoflsof代表&...

幻隐说固态第一期:固态硬盘接口类别

前排声明所有信息来源于网络收集,如有错误请评论区指出更正。废话不多说,目前固态硬盘接口按速度由慢到快分有这几类:SATA、mSATA、SATAExpress、PCI-E、m.2、u.2。下面我们来...

新品轰炸 影驰SSD多款产品登Computex

分享泡泡网SSD固态硬盘频道6月6日台北电脑展作为全球第二、亚洲最大的3C/IT产业链专业展,吸引了众多IT厂商和全球各地媒体的热烈关注,全球存储新势力—影驰,也积极参与其中,为广大玩家朋友带来了...