音视频入门-10-使用libyuv对YUV数据进行缩放、旋转、镜像、裁剪、混合
libyuv
libyuv 是 Google 开源的实现各种 YUV 与 RGB 之间相互转换、旋转、缩放等的库。它是跨平台的,可在 Windows、Linux、Mac、Android 等操作系统,x86、x64、arm 架构上进行编译运行,支持 SSE、AVX、NEON 等 SIMD 指令加速。
准备工作
一张图片
下载 rainbow-700x700.bmp BMP 图片 或者 自己准备一张图片(知道分辨率,如:700x700)

FFmpeg 工具包
根据自己的系统,下载 FFmpeg Static 工具包。
得到所需的 yuv420p 文件
将上面准备的图片转换成 YUV420P 格式:
ffmpeg -i rainbow.bmp -video_size 700x700 -pix_fmt yuv420p rainbow-yuv420p.yuv
查看 YUV 文件
ffplay -f rawvideo -pixel_format yuv420p -video_size 700x700 rainbow-yuv420p.yuv
libyuv 操作 YUV
YUV 裁剪

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-clip-x-y.yuv]
#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"
void clip(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height, int cropX, int cropY, int cropWidth, int cropHeight) {
ConvertToI420(
srcYuvData,
width*height*3/2,
dstYuvData,
cropWidth,
dstYuvData+cropWidth*cropHeight,
(cropWidth+1)/2,
dstYuvData+cropWidth*cropHeight+((cropWidth+1)/2)*((cropHeight+1)/2),
(cropWidth+1)/2,
cropX,
cropY,
width,
height,
cropWidth,
cropHeight,
kRotate0,
FOURCC_YU12);
}
int main() {
uint32_t width = 700, height = 700;
uint32_t clipWidth = 200, clipHeight = 200;
uint8_t YUV[width*height*3/2];
uint8_t YUV_CLIP[clipWidth*clipHeight*3/2];
FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
fread(YUV, sizeof(YUV), 1, yuv420pFile);
clip(YUV, YUV_CLIP, width, height, 0, 0, clipWidth, clipHeight);
FILE *yuvClipFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-clip-0-0.yuv", "wb");
fwrite(YUV_CLIP, sizeof(YUV_CLIP), 1, yuvClipFile);
fclose(yuvClipFile);
fclose(yuv420pFile);
return 0;
}
YUV 缩放

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-scale-X.yuv]
#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"
void scale(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height, int dstWidth, int dstHeight) {
I420Scale(
srcYuvData,
width,
srcYuvData+width*height,
(width+1)/2,
srcYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
width,
height,
dstYuvData,
dstWidth,
dstYuvData+dstWidth*dstWidth,
(dstWidth+1)/2,
dstYuvData+dstWidth*dstHeight+((dstWidth+1)/2)*((dstHeight+1)/2),
(dstWidth+1)/2,
dstWidth,
dstHeight,
kFilterNone
);
}
int main() {
uint32_t width = 700, height = 700;
uint32_t dstWidth = 100, dstHeight = 100;
uint8_t YUV[width*height*3/2];
uint8_t YUV_SCALE[dstWidth*dstHeight*3/2];
FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
fread(YUV, sizeof(YUV), 1, yuv420pFile);
scale(YUV, YUV_SCALE, width, height, dstWidth, dstHeight);
FILE *yuvScaleFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-scale-6.yuv", "wb");
fwrite(YUV_SCALE, sizeof(YUV_SCALE), 1, yuvScaleFile);
fclose(yuvScaleFile);
fclose(yuv420pFile);
return 0;
}
YUV 旋转

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-rotation-90.yuv]
#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"
void rotation(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
I420Rotate(
srcYuvData,
width,
srcYuvData+width*height,
(width+1)/2,
srcYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
dstYuvData,
width,
dstYuvData+width*height,
(width+1)/2,
dstYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
width,
height,
kRotate90
);
}
int main() {
uint32_t width = 700, height = 700;
uint8_t YUV[width*height*3/2];
uint8_t YUV_ROTATION[width*height*3/2];
FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
fread(YUV, sizeof(YUV), 1, yuv420pFile);
rotation(YUV, YUV_ROTATION, width, height);
FILE *yuvRotationFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90.yuv", "wb");
fwrite(YUV_ROTATION, sizeof(YUV_ROTATION), 1, yuvRotationFile);
fclose(yuvRotationFile);
fclose(yuv420pFile);
return 0;
}
YUV 镜像

[rainbow-yuv420p-rotation-90.yuv] -> [rainbow-yuv420p-rotation-90-mirror.yuv]
#include <stdio.h>
#include <stdint.h>
#include "libyuv.h"
void mirror(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
I420Mirror(
srcYuvData,
width,
srcYuvData+width*height,
(width+1)/2,
srcYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
dstYuvData,
width,
dstYuvData+width*height,
(width+1)/2,
dstYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
width,
height
);
}
int main() {
uint32_t width = 700, height = 700;
uint8_t YUV[width*height*3/2];
uint8_t YUV_MIRROR[width*height*3/2];
FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90.yuv", "rb");
fread(YUV, sizeof(YUV), 1, yuv420pFile);
mirror(YUV, YUV_MIRROR, width, height);
FILE *yuvMirrorFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-rotation-90-mirror.yuv", "wb");
fwrite(YUV_MIRROR, sizeof(YUV_MIRROR), 1, yuvMirrorFile);
fclose(yuvMirrorFile);
fclose(yuv420pFile);
return 0;
}
YUV 混合

[rainbow-yuv420p.yuv] -> [rainbow-yuv420p-blend.yuv]
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include "libyuv.h"
void blend(uint8_t *srcYuvData, uint8_t *dstYuvData, int width, int height) {
uint8_t YUV_ROTATION[width*height*3/2];
I420Rotate(
srcYuvData,
width,
srcYuvData+width*height,
(width+1)/2,
srcYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
YUV_ROTATION,
width,
YUV_ROTATION+width*height,
(width+1)/2,
YUV_ROTATION+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
width,
height,
kRotate90
);
// 透明度
uint8_t alpha[width*height];
memset(alpha, 0X88, width*height);
I420Blend(
srcYuvData,
width,
srcYuvData+width*height,
(width+1)/2,
srcYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
YUV_ROTATION,
width,
YUV_ROTATION+width*height,
(width+1)/2,
YUV_ROTATION+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
alpha,
width,
dstYuvData,
width,
dstYuvData+width*height,
(width+1)/2,
dstYuvData+width*height+((width+1)/2)*((height+1)/2),
(width+1)/2,
width,
height
);
}
int main() {
uint32_t width = 700, height = 700;
uint8_t YUV[width*height*3/2];
uint8_t YUV_BLEND[width*height*3/2];
FILE *yuv420pFile = fopen("/Users/staff/Desktop/rainbow-yuv420p.yuv", "rb");
fread(YUV, sizeof(YUV), 1, yuv420pFile);
blend(YUV, YUV_BLEND, width, height);
FILE *yuvBlendFile = fopen("/Users/staff/Desktop/rainbow-yuv420p-blend.yuv", "wb");
fwrite(YUV_BLEND, sizeof(YUV_BLEND), 1, yuvBlendFile);
fclose(yuvBlendFile);
fclose(yuv420pFile);
return 0;
}
参考资料:
内容有误?联系作者:

音视频入门-10-使用libyuv对YUV数据进行缩放、旋转、镜像、裁剪、混合的更多相关文章
- 音视频入门-09-RGB&YUV互转-使用开源库
* 音视频入门文章目录 * 介绍开源库 使用第三方开源库来简化开发,屏蔽一些底层的复杂度,节省大量编写代码的时间. libyuv: Google 开源的实现各种 YUV 与 RGB 之间相互转换.旋转 ...
- 音视频入门-08-RGB&YUV
* 音视频入门文章目录 * YUV & RGB 相互转换公式 YCbCr 的 Y 与 YUV 中的 Y 含义一致,Cb 和 Cr 与 UV 同样都指色彩,Cb 指蓝色色度,Cr 指红色色度,在 ...
- 音视频入门-07-认识YUV
* 音视频入门文章目录 * YUV & YCbCr 简介 YUV,是一种颜色编码方法.常使用在各个视频处理组件中. YUV 在对照片或视频编码时,考虑到人类的感知能力,允许降低色度的带宽. Y ...
- 音视频入门-01-认识RGB
* 音视频入门文章目录 * RGB 简介 RGB 色彩模式是工业界的一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB 即是代表红 ...
- 音视频入门-02-RGB拼图
* 音视频入门文章目录 * 图片 & 像素点 & RGB 平时浏览的图片看不出像素点: 图片放大时,可以看出图片是一个个像素点组成的: 每个像素点的颜色可以用 RGB 表示: RGB ...
- 音视频入门-18-手动生成一张GIF图片
* 音视频入门文章目录 * GIF 编码知识 GIF 包含的数据块: 文件头(Header) 逻辑屏幕标识符(Logical Screen Descriptor) 全局颜色表(Global Color ...
- 堪称教科书级别的Android音视频入门进阶学习手册,开源分享!
概述 随着整个互联网的崛起,数据传递的形式也在不断升级变化,总的流行趋势如下: 纯文本的短信,QQ -> 空间,微博,朋友圈的图片文字结合 -> 微信语音 -> 各大直播软件 -&g ...
- 音视频入门-11-PNG文件格式详解
* 音视频入门文章目录 * PNG 文件格式解析 PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDAT.IEND)组成. PNG 文件包括 8 字节 ...
- 音视频入门-12-手动生成一张PNG图片
* 音视频入门文章目录 * 预热 上一篇 [PNG文件格式详解]详细介绍了 PNG 文件的格式. PNG 图像格式文件由一个 8 字节的 PNG 文件署名域和 3 个以上的后续数据块(IHDR.IDA ...
随机推荐
- cv2.fillConvexPoly()与cv2.fillPoly()填充多边形
cv2.fillConvexPoly() cv2.fillConvexPoly()函数可以用来填充凸多边形,只需要提供凸多边形的顶点即可. 我们来画一个三角形 img = np.zeros((1080 ...
- esxi上为基于LVM的centos7的根目录扩容
概念:据说默认centos都是基于LVM的 LVM:LVM是逻辑盘卷管理(Logical Volume Manager)的简称,它是Linux环境下对磁盘分区进行管理的一种机制. LVM 更加详细的说 ...
- yii2增删改查操作
https://www.yiichina.com/tutorial/996 https://www.yiichina.com/tutorial/834 一.新增 使用model::save()操作进行 ...
- colock
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...
- win cmd 设置代理
windows: HTTP(S)代理服务器:127.0.0.1:5783 SOCKS代理服务器:127.0.0.1:5789 set 2 set http_proxy=socks5://127.0.0 ...
- 终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题
终于解决了python 3.x import cv2 “ImportError: DLL load failed: 找不到指定的模块” 及“pycharm关于cv2没有代码提示”的问题 参考 :h ...
- selenium grid用法
一,selenium grid是什么? selenium是一个用于UI自动化测试的工具. selenium grid是selenium家族中的三大组件的一员.selenium grid有两个版本,gr ...
- 0.9.0.RELEASE版本的spring cloud alibaba sentinel实例
sentinel即哨兵,相比hystrix断路器而言,它的功能更丰富.hystrix仅支持熔断,当服务消费方调用提供方发现异常后,进入熔断:sentinel不仅支持异常熔断,也支持响应超时熔断,另外还 ...
- 报错:(未解决)NoReplicaOnlineException: No replica in ISR for partition __consumer_offsets-8 is alive. Live brokers are: [Set(50, 51, 52)], ISR brokers are: [68]
报错背景: CDH集成kafka插件之后,启动kafka时就报出此错误. 报错现象: -- ::, ERROR state.change.logger: [Controller epoch=] Ini ...
- Python - Django - session 的基本使用
urls.py: from django.conf.urls import url from app02 import views urlpatterns = [ # app02 url(r'^app ...