海思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 开发工具
|
数据安全/隐私保护 块存储
全网首发:gstreamer如何接入RTSP流(IP摄像头)的代码范例
全网首发:gstreamer如何接入RTSP流(IP摄像头)的代码范例
1744 0
|
存储 人工智能 编解码
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
随着人工智能、高性能计算等领域的快速发展,GPU云服务器因其强大的计算能力和灵活的资源分配方式,成为越来越多企业和个人用户的首选。2024年,阿里云针对GPU云服务器推出了新的收费标准及活动,gn6v、gn7i、gn6i等实例的gpu云服务器有优惠,本文为大家介绍2024年,阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考。
阿里云gpu云服务器最新收费标准、活动价格与实例规格选择参考
|
传感器 API Android开发
Android摄像头采集选Camera1还是Camera2?
Camera1与Camera2是Android平台上的两种摄像头API。Camera1(API1)在Android 5.0后被标记为过时,新项目应优先选用Camera2(API2)。Camera2提供了更精细的控制选项,如曝光时间、ISO感光度等;支持多摄像头管理;采用异步操作提高应用响应速度;并支持RAW图像捕获及实时图像处理。此外,它还具备更好的适配性和扩展性,适用于各类应用场景,如相机应用开发、视频通话和计算机视觉等。因此,在现代Android开发中推荐使用Camera2。
503 0
|
算法 网络协议 Linux
Linux模块文件编译到内核与独立编译成.ko文件的方法
Linux模块文件编译到内核与独立编译成.ko文件的方法
4630 0
|
存储 并行计算 物联网
海思3559万能平台搭建:VGS的画线处理
海思3559万能平台搭建:VGS的画线处理
1898 0
海思3559万能平台搭建:VGS的画线处理
|
存储 网络协议 流计算
|
Ubuntu Linux 开发工具
阿里云主机的Milk-V Duo开发板python环境搭建
Milk-V Duo是一款基于算能CV1800B芯片的开发板。搭载阿里云平头哥玄铁C906处理器核心,最高频率为1GHz,视频解码、2D图形加速能力强,可应用于智能语音、智能汽车及高清显示等领域。开发板身材小巧,大小和树莓派Pico相当,引脚也基本一致,但是功能更强大一些,能够运行基于LINUX和RTOS的操作系统。
1469 0
|
传感器 编解码 缓存
海思3559 sample解析:venc
海思3559 sample解析:venc
1637 0
海思3559 sample解析:venc