海思开发:海思api保存 yvu420sp 为 bmp图片文件

一、前言

最近在植入模型时,遇到了模型精度不准的问题,为了定位精度下降的原因,采取一个个节点排除的方法。首先就是确保输入图片无误。所以就有了这篇文章,记录一下经验,欢迎来往各位不吝指教。

二、代码

屁话不多说,直接上代码,一些基础的知识,比如海思api的介绍,bmp的介绍等等,自己去学习吧,此文不做展开了。
参考博文,可以先看看他们的:
海思 api 例子https://blog.csdn.net/mhsszm/article/details/104946840
bmp 代码https://blog.csdn.net/quantum7/article/details/82114750###
bmp详细解释
https://blog.csdn.net/weixin_34232744/article/details/86124618?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1
注:由于现实原因,不可能把代码全部贴出来,只能截取其中与主题相关的一部分,可能会有缺失,直接用时注意报错。

int main(IVE_DST_IMAGE_S *astDst)
{		
		unsigned int dst_width = 112;
		unsigned int dst_height = 112;
		// 先做格式转换,用 HI_MPI_IVE_CSC 将 yvu420sp --> U8C3_PLANAR(即 bgr/rgb planner 格式 NNIE支持的格式) 
		// astDst 是 yvu 图片的结构体指针,里面存放了 yvu 图片的必备属性
		IVE_DST_IMAGE_S csc_Dst = {0}; // 转换格式后的 bgr 图片必备属性结构体

		csc_Dst.au32Stride[0] = astDst[0].au32Stride[0]; // 给其步长赋值,步长二者肯定一致
		csc_Dst.au32Stride[1] = csc_Dst.au32Stride[0]; 
		// 文档规定,当输出数据为 U8C3_PLANAR 、 YUV420 SP 、 YUV422SP 类型时, 
		// 要求输出数据跨度一致。
		csc_Dst.au32Stride[2] = csc_Dst.au32Stride[0]; 
		
		csc_Dst.u32Width = astDst[0].u32Width;
		csc_Dst.u32Height = astDst[0].u32Height;
		csc_Dst.enType = IVE_IMAGE_TYPE_U8C3_PLANAR; // 选定图片格式,别填错了
		// 注意是 stride * height,不是 width * height, 海思像素对齐需要
		HI_U32 u32Size_csc = csc_Dst.au32Stride[0] * dst_height * 3; 
		// 给 bgr 图片分配内存空间,并将分配好的内存空间首地址传给 其结构体中的地址
		s32Ret = HI_MPI_SYS_MmzAlloc_Cached(&csc_Dst.au64PhyAddr[0], (HI_VOID**)&csc_Dst.au64VirAddr[0], "CSC image", HI_NULL, u32Size_csc);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("Mmz Alloc fail,Error(%#x) csc\n", s32Ret);
			IVE_MMZ_FREE(astDst.au64PhyAddr[0], astDst.au64VirAddr[0]);		
			
			continue;
        }
		// 保证 cache 与 内存 数据同步,与上面是套路写法
		s32Ret = HI_MPI_SYS_MmzFlushCache(csc_Dst.au64PhyAddr[0], (HI_VOID *)csc_Dst.au64VirAddr[0], u32Size_csc);
	    if(HI_SUCCESS!=s32Ret)
	    {
			SAMPLE_PRT("HI_MPI_SYS_MmzFlushCache fail,Error(%#x) csc\n", s32Ret);
			IVE_MMZ_FREE(csc_Dst.au64PhyAddr[0], csc_Dst.au64VirAddr[0]);
			IVE_MMZ_FREE(astDst.au64PhyAddr[0], astDst.au64VirAddr[0]);	
	    }
		// 地址赋值,au64VirAddr[0] au64VirAddr[1] au64VirAddr[2] 分别是 bgr 图片 三个分量的内存首地址,物理地址同
		csc_Dst.au64PhyAddr[1] = csc_Dst.au64PhyAddr[0] + csc_Dst.au32Stride[0] * dst_height;
		csc_Dst.au64VirAddr[1] = csc_Dst.au64VirAddr[0] + csc_Dst.au32Stride[0] * dst_height;

		csc_Dst.au64PhyAddr[2] = csc_Dst.au64PhyAddr[1] + csc_Dst.au32Stride[0] * dst_height;
		csc_Dst.au64VirAddr[2] = csc_Dst.au64VirAddr[1] + csc_Dst.au32Stride[0] * dst_height;

		IVE_CSC_CTRL_S csc_Ctrl = {0};
		csc_Ctrl.enMode = IVE_CSC_MODE_PIC_BT709_YUV2RGB; // 选择转换模式
		IVE_HANDLE Ive_csc_Handle;
		// 转换格式
		s32Ret = HI_MPI_IVE_CSC(&Ive_csc_Handle, &(astDst[0]), &csc_Dst, &csc_Ctrl, HI_TRUE);
		if(HI_SUCCESS!=s32Ret)
	    {
			SAMPLE_PRT("HI_MPI_IVE_CSC fail,Error(%#x) csc\n", s32Ret);
			IVE_MMZ_FREE(csc_Dst.au64PhyAddr[0], csc_Dst.au64VirAddr[0]);
			IVE_MMZ_FREE(astDst.au64PhyAddr[0], astDst.au64VirAddr[0]);
	    }
		// 如果想转换成 rgb planner ,取消注释
		/*HI_U64 rgb_u64PhyAddr[3] = {0};
		HI_U64 rgb_u64VirAddr[3] = {0};
	
		// 返回 物理地址 和 用户态虚拟地址指针。
		s32Ret = HI_MPI_SYS_MmzAlloc(&rgb_u64PhyAddr[0], (HI_VOID**)&rgb_u64VirAddr[0], "scale_face_image", HI_NULL, u32Size_csc);
        if (s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("Mmz Alloc fail,Error(%#x) down\n", s32Ret);
			IVE_MMZ_FREE(csc_Dst.au64PhyAddr[0], csc_Dst.au64VirAddr[0]);
			IVE_MMZ_FREE(astDst.au64PhyAddr[0], astDst.au64VirAddr[0]);
        }
		rgb_u64VirAddr[1] = rgb_u64VirAddr[0] + csc_Dst.au32Stride[0] * dst_height;
		rgb_u64VirAddr[2] = rgb_u64VirAddr[1] + csc_Dst.au32Stride[0] * dst_height;

		rgb_u64PhyAddr[1] = rgb_u64PhyAddr[0] + csc_Dst.au32Stride[0] * dst_height;
		rgb_u64PhyAddr[2] = rgb_u64PhyAddr[1] + csc_Dst.au32Stride[0] * dst_height;
		
		memcpy(rgb_u64VirAddr[0], csc_Dst.au64VirAddr[2], csc_Dst.au32Stride[0] * dst_height); // R
		memcpy(rgb_u64VirAddr[1], csc_Dst.au64VirAddr[1], csc_Dst.au32Stride[0] * dst_height); // G
		memcpy(rgb_u64VirAddr[2], csc_Dst.au64VirAddr[0], csc_Dst.au32Stride[0] * dst_height); // B
		*/
		unsigned char *img_data = (unsigned char *)(unsigned long)astDst[0].au64VirAddr[0];
 		int ret = rgbaToBmpFile(dst_width, dst_height, img_data);
		IVE_MMZ_FREE(astDst.au64PhyAddr[0], astDst.au64VirAddr[0]);
		IVE_MMZ_FREE(csc_Dst.au64PhyAddr[0], csc_Dst.au64VirAddr[0]);
		//IVE_MMZ_FREE(rgb_u64PhyAddr[0], rgb_u64VirAddr[0]); // 如果想转换成 rgb planner ,取消注释
 	}
 }

下面是bmp代码部分,基本拷贝自第二个链接。(出于方便,我把以下代码放在一个新建的头文件里给上面函数调用,虽然不正规但是方便!自己开心就好!)

#include 
#include 
#include 
 
 
typedef unsigned char  	BYTE;
typedef unsigned short  WORD;
typedef unsigned int  	DWORD;
typedef int  		LONG;
 
#define ALIGN_UP(x, a)           ( ( ((x) + ((a) - 1) ) / a ) * a )

#define BITS_PER_PIXCEL 24
#define FORMAT_RGBA 4
#define FORMAT_RGB  3
 
/** must needed. pack */
#pragma pack(1)
 
typedef struct
{
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} BMP_FILE_HEADER;
 
typedef struct{
    DWORD      biSize;
    LONG       biWidth;
    LONG       biHeight;
    WORD       biPlanes;
    WORD       biBitCount;
    DWORD      biCompression;
    DWORD      biSizeImage;
    LONG       biXPelsPerMeter;
    LONG       biYPelsPerMeter;
    DWORD      biClrUsed;
    DWORD      biClrImportant;
} BMP_INFO_HEADER;
 
#pragma pack()
// 将 bbbgggrrr 格式转化为 bgrbgrbgr 这种常见的格式
int bgr_planner2bgr_packed(int width, int height, unsigned char * img_data, unsigned char *img_data_conv)
{
	int channel = 3;
	//int width = scale_frame->stVFrame.u32Stride[0];
	//int height = scale_frame->stVFrame.u32Height;
	//unsigned char * img_data = (HI_U8 *)(HI_UL)scale_frame->stVFrame.u64VirAddr[0];
	//unsigned char *img_data_conv = NULL;
	//img_data_conv = (unsigned char *)malloc(sizeof(unsigned char) * width * height * channel);
	int stride = ALIGN_UP(width, 16); // 因为海思 IVE 模块像素对齐是 16 对齐
	for (int k = 0; k < channel; k++)
			for (int i = 0; i < height; i++)
				for (int j = 0; j < width; j++)
					img_data_conv[channel * (i * width + j) + k] = img_data[k * height * stride + i * stride + j]; ///* 如果想输出 rgb ,把等号左边里的 k ---> (2 - k) */
	
	return 1;
	
}

 
int rgbaToBmpFile(int Width, int Height, unsigned char *img_data)
{
    BMP_FILE_HEADER bmpHeader;
    BMP_INFO_HEADER bmpInfo;
 
    FILE* fp         = NULL;
    char* pBmpSource = NULL;
    char* pBmpData   = NULL;
 	char *pFileName = "yvu_image/test.bmp";
    int i = 0, j = 0;
	//int Width = scale_frame->stVFrame.u32Width;
	//int Height = scale_frame->stVFrame.u32Height;
	int channel = 3;
 	unsigned char *pRgbaData = NULL;
	unsigned char *RgbaData = NULL;
	pRgbaData = (unsigned char *)malloc(sizeof(unsigned char) * Width * Height * channel);
	RgbaData = pRgbaData;
	//unsigned char pRgbaData[Width * Height * channel] = {0};
	int ret = bgr_planner2bgr_packed(Width, Height, img_data, pRgbaData);
	
    //4 bytes pack. must be 4 times per line。
    int bytesPerLine = (Width * BITS_PER_PIXCEL + 31) / 32 * 4; // 31是保证32位对齐的填充值,这样输出的值肯定是32的倍数,乘上4是因为除了一个32位(4字节)
    int pixcelBytes  = bytesPerLine * Height;
 
    bmpHeader.bfType        = 0x4D42;
    bmpHeader.bfReserved1   = 0;
    bmpHeader.bfReserved2   = 0;
    bmpHeader.bfOffBits     = sizeof(BMP_FILE_HEADER) + sizeof(BMP_INFO_HEADER);
    bmpHeader.bfSize        = bmpHeader.bfOffBits     + pixcelBytes;
  
    bmpInfo.biSize          = sizeof(BMP_INFO_HEADER);
    bmpInfo.biWidth         = Width;
    /** 这样图片才不会倒置 */
    bmpInfo.biHeight        = -Height; 
    bmpInfo.biPlanes        = 1;
    bmpInfo.biBitCount      = BITS_PER_PIXCEL;
    bmpInfo.biCompression   = 0;
    bmpInfo.biSizeImage     = pixcelBytes;
    bmpInfo.biXPelsPerMeter = 100;
    bmpInfo.biYPelsPerMeter = 100;
    bmpInfo.biClrUsed       = 0;
    bmpInfo.biClrImportant  = 0;
 
 
    /** convert in memort, then write to file. */
    pBmpSource = malloc(pixcelBytes);
    if (!pBmpSource)
    {
        return -1;
    }
 
    /** open file */
    fp = fopen(pFileName, "wb+");
    if (!fp)
    {
        return -1;
    }
 	printf("pFileName = %s\n", pFileName);
    fwrite(&bmpHeader, sizeof(BMP_FILE_HEADER), 1, fp); // 写入文件头
    fwrite(&bmpInfo,   sizeof(BMP_INFO_HEADER), 1, fp);
    /** Here you should consider color format. RGBA ? RGB? BGR?
        Param format is RGBA, format for file is BGR */
    pBmpData = pBmpSource;
    for (i=0; i<Height; i++)
    {
        for (j=0; j<Width; j++) // bmp 也是 bgr 排列
        {
           pBmpData[0] = pRgbaData[0];
           pBmpData[1] = pRgbaData[1];
           pBmpData[2] = pRgbaData[2];
	       //pRgbaData += FORMAT_RGBA;
		   pRgbaData += FORMAT_RGB;
           pBmpData  += FORMAT_RGB;
        }
        //pack for 4 bytes
        pBmpData +=(bytesPerLine - Width * 3); // 跳过每行里填充的数据
    }
    fwrite(pBmpSource, pixcelBytes, 1, fp);
    /** close and release。 */
    fclose(fp);  
    free(pBmpSource);
 	free(RgbaData);
    return 0;
}

尽力截取了,如果跑不起来的话请知会我一声,谢谢各位。

你可能感兴趣的:(海思开发,量化部署,yvu420sp,bgr_planner,心得体会,经验记录)