当前位置:网站首页>海思3559万能平台搭建:在截获的YUV图像上旋转操作
海思3559万能平台搭建:在截获的YUV图像上旋转操作
2022-07-02 22:09:00 【快跑bug来啦】
前言
为了进一步巩固yvu格式的存放方式以及应对更多应用场景,在补充下YUV图像怎么旋转
常规算法
一般的来说,要旋转的角度无非就是90,180,270,我们只要能转到90度后进行翻转或者镜像是可以做到其他角度的,参考下图来看,第一行变成了Y13Y9Y5Y1,对应的V4U4也提到了前面,但是很明显这样的运算量或者换个角度头得多大啊
void rotateYUV420SP(unsigned char src[],unsigned char des[],int width,int height)
{
int wh = width * height;
int k = 0;
for(int i=0;i<width;i++)
{
//旋转Y
for(int j=0;j<height;j++)
{
des[k] = src[width*j + i];
k++;
}
}
for(int i=0;i<width;i+=2)
{
//旋转vu
for(int j=0;j<height/2;j++)
{
des[k] = src[wh+ width*j + i];
des[k+1]=src[wh + width*j + i+1];
k+=2;
}
}
}
HI_MPI_VPSS_SetChnRotation
海思也自然考虑到了这点,自己底层有硬件加速,很方便的提供了相应的旋转函数HI_MPI_VPSS_SetChnRotation和任意角度的HI_MPI_VPSS_SetChnRotationEx,只需要提供vpss的group,chnl和旋转角度即可,用起来还是非常方便的,在vpss初始化之后调用
s32Ret = HI_MPI_VPSS_SetChnRotation(VpssGrp, VpssChn[1], ROTATION_90);
if(s32Ret != HI_SUCCESS)
{
printf("HI_MPI_VPSS_SetChnRotation failed with %#x\n", s32Ret);
return HI_FAILURE;
}
注意事项
需要注意的是保存输出的时候一定一定不要忘记修改编码通道的分辨率!不然会报获取不到图像的错误!当时刚好因为dump例程的问题,直接在vpss后保存了yuv,只有y分量正确,uv惨不忍睹。还好在大佬的点醒下,想到venc压根就没配这部分!(同理的还有下一篇记录的YUV422编码获取不到的问题)
通道 AUTO 模式下不支持。
仅支持 semi-planar 420 和单分量像素格式。
(后面两个注意事项手册上有,细心点很容易发现)
补充算法
参考博客
https://blog.csdn.net/huangjiazhi_/article/details/103960883
// Yuv420pRotate.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
// clockwise 顺时针
// contrarotate 逆时针旋转
// flip horizontal 镜像翻转/水平翻转
/** * 顺时针旋转90。 * 取元素:从左下方第一个点开始,从下往上,从左往右取点; * 放元素:从左上方第一个位置开始放,从左往右,从上往下;把每一列转换成每一行。 * * @param yuvFileaName 一帧YUV420P格式的文件 * @param width 图像的宽 * @param height 图像的高 * * @return 空 */
void clockwiseRotate90(const char* yuvFileaName, int width, int height){
FILE* fp = NULL;
fopen_s(&fp, yuvFileaName, "rb");
unsigned char* yuvbuf = new unsigned char[width*height * 3 / 2];
fread(yuvbuf, width*height * 3 / 2, 1, fp);
fclose(fp);
int idx = 0;
//Y
unsigned char* dstbuf = new unsigned char[width*height * 3 / 2];
for (int i = 0; i <= width - 1; i++){
for (int j = height - 1; j >= 0; j--){
dstbuf[idx++] = *(yuvbuf + (j*width + i));
}
}
//U
unsigned char* uheader = yuvbuf + width*height;
int hw = width / 2;
int hh = height / 2;
for (int i = 0; i <= hw - 1; i++){
for (int j = hh - 1; j >= 0; j--){
dstbuf[idx++] = *(uheader + (j*hw + i));
}
}
//V
unsigned char* vheader = uheader + width*height / 4;
for (int i = 0; i <= hw - 1; i++){
for (int j = hh - 1; j >= 0; j--){
dstbuf[idx++] = *(vheader + (j*hw + i));
}
}
FILE* fpout = NULL;
fopen_s(&fpout, "clockwiseRotate90.yuv", "wb");
fwrite(dstbuf, width*height * 3 / 2, 1, fpout);
fclose(fpout);
delete[] yuvbuf;
delete[] dstbuf;
}
/** * 逆时针旋转90。 * 取元素:从右上方第一个点开始,从上往下,从右往左取点; * 放元素:从左上方第一个位置开始放,从左往右,从上往下;把每一列转换成每一行。 * * @param yuvFileaName 一帧YUV420P格式的文件 * @param width 图像的宽 * @param height 图像的高 * * @return 空 */
void contrarotate90(const char* yuvFileaName, int width, int height)
{
int i, j, k, p;
FILE* fp = NULL;
fopen_s(&fp, yuvFileaName, "rb");
unsigned char* yuvbuf = new unsigned char[width*height * 3 / 2];
fread(yuvbuf, width*height * 3 / 2, 1, fp);
fclose(fp);
unsigned char* dstbuf = new unsigned char[width*height * 3 / 2];
char* dest = (char*)dstbuf;
char* src = (char*)yuvbuf;
// rotate Y
for (j = 0; j < width; j++){
for (i = 1; i <= height; i++){
*dest++ = *(src + i*width - j);
}
}
// rotate U
char *src_u = src + width*height;
for (p = 0; p < width / 2; p++){
for (k = 1; k <= height / 2; k++){
*dest++ = *(src_u + k*width / 2 - p);
}
}
// rotate V
char *src_v = src + width*height * 5 / 4;
for (p = 0; p < width / 2; p++){
for (k = 1; k <= height / 2; k++){
*dest++ = *(src_v + k*width / 2 - p);
}
}
FILE* fpout = NULL;
fopen_s(&fpout, "contrarotate90.yuv", "wb");
fwrite(dstbuf, width*height * 3 / 2, 1, fpout);
fclose(fpout);
delete[] yuvbuf;
delete[] dstbuf;
}
/** * 逆时针180。 * 取元素:从右下方第一个点开始,从右往左,从下往上取点; * 放元素:从左上方第一个位置开始放,从左往右,从上往下; * * @param yuvFileaName 一帧YUV420P格式的文件 * @param width 图像的宽 * @param height 图像的高 * * @return 空 */
void contrarotate180(const char* yuvFileaName, int width, int height){
FILE* fp = NULL;
fopen_s(&fp, yuvFileaName, "rb");
unsigned char* yuvbuf = new unsigned char[width*height * 3 / 2];
fread(yuvbuf, width*height * 3 / 2, 1, fp);
fclose(fp);
int idx = 0;
//Y
unsigned char* dstbuf = new unsigned char[width*height * 3 / 2];
for (int i = height - 1; i >= 0; i--){
for (int j = width - 1; j >= 0; j--){
dstbuf[idx++] = *(yuvbuf + (i*width + j));
}
}
//U
unsigned char* uheader = yuvbuf + width*height;
for (int i = height / 2 - 1; i >= 0; i--){
for (int j = width / 2 - 1; j >= 0; j--){
dstbuf[idx++] = *(uheader + (i*width / 2 + j));
}
}
unsigned char* vheader = uheader + width*height / 4;
//V
for (int i = height / 2 - 1; i >= 0; i--){
for (int j = width / 2 - 1; j >= 0; j--){
dstbuf[idx++] = *(vheader + (i*width / 2 + j));
}
}
FILE* fpout = NULL;
fopen_s(&fpout, "contrarotate180.yuv", "wb");
fwrite(dstbuf, width*height * 3 / 2, 1, fpout);
fclose(fpout);
delete[] yuvbuf;
delete[] dstbuf;
}
/** * 镜像翻转/水平翻转 * 取元素:将右上角的点作为第一个点,从右往左,从上往下取点; * 放元素:从左上方第一个位置开始放,从左往右,从上往下; * * @param yuvFileaName 一帧YUV420P格式的文件 * @param width 图像的宽 * @param height 图像的高 * * @return 空 */
void flipHorizontal(const char* yuvFileaName, int width, int height)
{
FILE* fp = NULL;
fopen_s(&fp, yuvFileaName, "rb");
unsigned char* yuvbuf = new unsigned char[width*height * 3 / 2];
fread(yuvbuf, width*height * 3 / 2, 1, fp);
fclose(fp);
int idx = 0;
//Y
unsigned char* dstbuf = new unsigned char[width*height * 3 / 2];
for (int i = 0; i < height; i++){
for (int j = width - 1; j >= 0; j--){
dstbuf[idx++] = *(yuvbuf + (i*width + j));
}
}
//U
unsigned char* uheader = yuvbuf + width*height;
for (int i = 0; i < height / 2; i++){
for (int j = width / 2 - 1; j >= 0; j--){
dstbuf[idx++] = *(uheader + (i*width / 2 + j));
}
}
//V
unsigned char* vheader = uheader + width*height / 4;
for (int i = 0; i < height / 2; i++){
for (int j = width / 2 - 1; j >= 0; j--){
dstbuf[idx++] = *(vheader + (i*width / 2 + j));
}
}
FILE* fpout = NULL;
fopen_s(&fpout, "flipHorizontal.yuv", "wb");
fwrite(dstbuf, width*height * 3 / 2, 1, fpout);
fclose(fpout);
delete[] yuvbuf;
delete[] dstbuf;
}
/** * 逆时针旋转180后,再水平翻转/镜像。 * 取元素:从左下方第一个点开始,从左往右,从下往上取点; * 放元素:从左上方第一个位置开始放,从左往右,从上往下; * * @param yuvFileaName 一帧YUV420P格式的文件 * @param width 图像的宽 * @param height 图像的高 * * @return 空 */
void contrarotate180AndFlipHorizontal(const char* yuvFileaName, int width, int height){
FILE* fp = NULL;
fopen_s(&fp, yuvFileaName, "rb");
unsigned char* yuvbuf = new unsigned char[width*height * 3 / 2];
fread(yuvbuf, width*height * 3 / 2, 1, fp);
fclose(fp);
int idx = 0;
//Y 宽
unsigned char* dstbuf = new unsigned char[width*height * 3 / 2];
for (int i = height - 1; i >= 0; i--){
for (int j = 0; j <= width - 1; j++){
dstbuf[idx++] = *(yuvbuf + (i*width + j));
}
}
//U
unsigned char* uheader = yuvbuf + width*height;
for (int i = height / 2 - 1; i >= 0; i--){
for (int j = 0; j <= width / 2 - 1; j++){
dstbuf[idx++] = *(uheader + (i*width / 2 + j));
}
}
//V
unsigned char* vheader = uheader + width*height / 4;
for (int i = height / 2 - 1; i >= 0; i--){
for (int j = 0; j <= width / 2 - 1; j++){
dstbuf[idx++] = *(vheader + (i*width / 2 + j));
}
}
FILE* fpout = NULL;
fopen_s(&fpout, "contrarotate180AndFlipHorizontal.yuv", "wb");
fwrite(dstbuf, width*height * 3 / 2, 1, fpout);
fclose(fpout);
delete[] yuvbuf;
delete[] dstbuf;
}
int _tmain(int argc, _TCHAR* argv[])
{
const char* yuvFileaName = "yuv420p.yuv";
int w = 160;
int h = 128;
clockwiseRotate90(yuvFileaName, w, h);
contrarotate90(yuvFileaName, w, h);
contrarotate180(yuvFileaName, w, h);
flipHorizontal(yuvFileaName, w, h);
contrarotate180AndFlipHorizontal(yuvFileaName, w, h);
return 0;
}
边栏推荐
- [ODX studio edit PDX] -0.1- how to quickly view the differences in supported diagnostic information between variant variants (service, sub function...)
- Jatpack------LiveData
- 【喜欢的诗词】好了歌
- [autosar-dcm] - 4.3-how UDS $22 and $2e services read and write NVM data
- Qt QScrollArea
- 'when to use const char * and when to use const char []' - when to use const char * and when to use const char []
- PHP implements querying the data matching the date of birth according to the entered age
- 数组进阶提高
- 杰理之直接触摸样机的顶针反应不正常【篇】
- Webrtc audio and video capture and playback examples and mediastream media stream analysis
猜你喜欢
#include errors detected. Please update your includePath.
MySQL查询附近的数据.并按距离进行排序.
百度智能云-创建人脸识别应用
wait解决僵尸进程
Objects and object variables
Dahua cloud native load balancing article - the passenger flow of small restaurants has increased
牛客网:最大子矩阵
Simpleitk use - 3 Common operations
Task and privilege level protection
SimpleITK使用——4. 奇怪的問題
随机推荐
`${}`的用法
Oracle cursor
Hanging mirror security won four global infosec awards on rsac2022
[NPUCTF2020]ezlogin xPATH注入
分享 10 个 JS 闭包面试题(图解),进来看看你能答对多少
Xiaopeng P7 had an accident and the airbag did not pop up. Is this normal?
从2022年Q1财报看携程的韧性和远景
Share 10 JS closure interview questions (diagrams), come in and see how many you can answer correctly
Notes on key vocabulary of the original English book biography of jobs (IX) [chapter seven]
[LeetCode] 存在重复元素【217】
Comprehensively analyze the logic of the shared purchase business model? How sharing purchase empowers Enterprises
MySQL reset password, forget password, reset root password, reset MySQL password
Baidu AI Cloud - create a face recognition application
MySQL查询附近的数据.并按距离进行排序.
Kubernetes uses the host name to allocate the pod on the specified node
Jielizhi, production line assembly link [chapter]
Qt QSplitter拆分器
【喜欢的诗词】好了歌
[leetcode] most elements [169]
对象与对象变量