海思3559万能平台搭建:在截获的YUV图像上旋转操作

简介: 在截获的YUV图像上旋转操作

前言

 为了进一步巩固yvu格式的存放方式以及应对更多应用场景,在补充下YUV图像怎么旋转

常规算法

 一般的来说,要旋转的角度无非就是90,180,270,我们只要能转到90度后进行翻转或者镜像是可以做到其他角度的,参考下图来看,第一行变成了Y13Y9Y5Y1,对应的V4U4也提到了前面,但是很明显这样的运算量或者换个角度头得多大啊

568bec95f24245b8adc8a5181900fc27.png

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;
}



相关文章
|
编解码 API 开发工具
|
计算机视觉 Python
Jetson 学习笔记(六):cv2调用CSI摄像头(jetson nx/nano)、打开海康摄像头、打开电脑摄像头
这篇文章介绍了在不同平台上接入并显示摄像头视频流的方法,包括海康摄像头的RTSP连接、电脑内置摄像头的直接读取、Jetson NX/Nano通过CSI接口和USB接口的操作,以及Jetson Nano通过Gstreamer管道和jetcam库的使用,并提供了相应的代码示例。
1664 1
|
NoSQL MongoDB
MongoDB因服务器异常断电,无法启动异常的修复
本文是博主遇到MongoDB启动异常的解决方法记录,希望对大家有所帮助。
2007 0
|
NoSQL 小程序 C语言
GDB调试学习(四):段错误
GDB调试学习(四):段错误
622 0
|
Kubernetes Java 容器
如何获取k8s容器里运行的jar包
如何获取k8s容器里运行的jar包
943 0
|
存储 NoSQL
MongoDB无法启动,如何恢复数据?
近日有 MongoDB 用户遇到一个问题,使用 Wiredtiger 存储引擎的 MongoDB 无法启动,咨询我数据能否恢复回来,能恢复多少是多少 ... 问题出现的场景据用户描述是「mongod磁盘写满了,导致进程 crash」,尝试重新启动,结果 wiredtiger 报错,错误信息类似如下,类似的问题 mongodb jira 上也有人提过,可以参考 SERVER-26924,说明此时 MongoDB 数据文件已经损坏。
|
存储 人工智能 编解码
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
随着人工智能、高性能计算等领域的快速发展,GPU云服务器因其强大的计算能力和灵活的资源分配方式,成为越来越多企业和个人用户的首选。2024年,阿里云针对GPU云服务器推出了新的收费标准及活动,gn6v、gn7i、gn6i等实例的gpu云服务器有优惠,本文为大家介绍2024年,阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考。
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
|
域名解析 Web App开发 缓存
在浏览器上输入一个网址后,发生了什么?/HTTP的工作流程/DNS域名解析过程
在浏览器上输入一个网址后,发生了什么?/HTTP的工作流程/DNS域名解析过程
|
Ubuntu Linux 开发工具
阿里云主机的Milk-V Duo开发板python环境搭建
Milk-V Duo是一款基于算能CV1800B芯片的开发板。搭载阿里云平头哥玄铁C906处理器核心,最高频率为1GHz,视频解码、2D图形加速能力强,可应用于智能语音、智能汽车及高清显示等领域。开发板身材小巧,大小和树莓派Pico相当,引脚也基本一致,但是功能更强大一些,能够运行基于LINUX和RTOS的操作系统。
1686 0