最近在植入模型时,遇到了模型精度不准的问题,为了定位精度下降的原因,采取一个个节点排除的方法。首先就是确保输入图片无误。所以就有了这篇文章,记录一下经验,欢迎来往各位不吝指教。
屁话不多说,直接上代码,一些基础的知识,比如海思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;
}
尽力截取了,如果跑不起来的话请知会我一声,谢谢各位。