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



相关文章
|
6月前
|
人工智能 Linux API
OpenCV这么简单为啥不学——1.1、图像处理(灰度图、模糊图片、GaussianBlur函数、提取边缘、边缘膨胀、边缘细化)
OpenCV这么简单为啥不学——1.1、图像处理(灰度图、模糊图片、GaussianBlur函数、提取边缘、边缘膨胀、边缘细化)
105 0
|
编解码 API 开发工具
|
5月前
|
编解码 图形学 开发者
【unity小技巧】使用三种方式实现瞄准瞄具放大变焦效果
【unity小技巧】使用三种方式实现瞄准瞄具放大变焦效果
96 0
|
6月前
|
物联网 Python
最近被layerdiffusion分层生成透明图像技术刷屏了!
最近被layerdiffusion分层生成透明图像技术刷屏了!
216 1
|
编解码 监控 算法
转:图像拼接算法在电脑屏幕监控软件中的优势与应用场景
图像拼接算法在电脑屏幕监控软件中有着广泛的优势和应用场景。这种算法可以将多个部分的图像合并成一个整体,从而提供更大范围的监控视野和更全面的信息。
117 0
|
算法 数据安全/隐私保护 计算机视觉
基于Aidlux平台实现手机摄像头实时Canny检测
基于Aidlux平台实现手机摄像头实时Canny检测
|
存储 编解码 算法
|
人工智能 计算机视觉
【人工智能】人脸识别检测戴口罩实战之初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换【第一课】
初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换,读取视频。解决报错iCCP: cHRM chunk does not match sRGB
539 1
【人工智能】人脸识别检测戴口罩实战之初识OpenCV简单操作,图片的读取和显示以及BGR与RGB之间的转换【第一课】
|
存储 编解码 监控
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
案例分享:Qt+Arm+Fpga医疗肾镜(又名内窥镜)(实时影像、冻结、拍照、白平衡、九宫格、录像、背光调整、硬件光源调整、光源手动自动调整、物理按键)
|
机器学习/深度学习 存储 人工智能
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
552 0
能去码也能打码!OpenCV实时检测视频流人脸并马赛克之,视频后期福音
下一篇
无影云桌面