海思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 开发工具
|
5月前
|
编解码 图形学 开发者
【unity小技巧】使用三种方式实现瞄准瞄具放大变焦效果
【unity小技巧】使用三种方式实现瞄准瞄具放大变焦效果
83 0
|
6月前
|
物联网 Python
最近被layerdiffusion分层生成透明图像技术刷屏了!
最近被layerdiffusion分层生成透明图像技术刷屏了!
192 1
|
传感器 Linux 开发工具
开源项目-十六进制协议传感器自适应缩放曲线显示终端(百问网imx6ull & 小熊派结合)
开源项目-十六进制协议传感器自适应缩放曲线显示终端(百问网imx6ull & 小熊派结合)
93 0
|
存储 编解码 算法
|
传感器 缓存 物联网
5_2_1_光照信息屏_软件详解|学习笔记
快速学习5_2_1_光照信息屏_软件详解。
146 0
5_2_1_光照信息屏_软件详解|学习笔记
|
传感器 物联网 开发者
5_2_1_光照信息屏_ 实验技术点及应用场景介绍|学习笔记
快速学习5_2_1_光照信息屏_实验技术点及应用场景介绍。
169 0
5_2_1_光照信息屏_ 实验技术点及应用场景介绍|学习笔记
|
人工智能 计算机视觉
【人工智能】人脸识别检测戴口罩实战之初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换【第一课】
初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换,读取视频。解决报错iCCP: cHRM chunk does not match sRGB
534 1
【人工智能】人脸识别检测戴口罩实战之初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换【第一课】
|
存储 编解码 监控
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
|
搜索推荐 Windows
Windows 技巧篇-电脑蓝光过滤,颜色校准调节蓝光,电脑源头过滤蓝光,保护眼睛,护眼软件原理
Windows 技巧篇-电脑蓝光过滤,颜色校准调节蓝光,电脑源头过滤蓝光,保护眼睛,护眼软件原理
402 0
Windows 技巧篇-电脑蓝光过滤,颜色校准调节蓝光,电脑源头过滤蓝光,保护眼睛,护眼软件原理