【数字图像处理】BMP图片的读取显示存储(C语言实现)

mac2024-11-16  9

(一)背景介绍

这段时间接到了一个新活,是关于图像处理的一个探地摄像头的项目。所以也差不多是时候开始学习一下数字图像处理的知识了。本来我们的方案是直接移植opencv,编译一下以后其他就基本啥都不用管了,非常方便。但是最后还是决定不适用这个方案。

为什么放弃了opencv

1.opencv的平台限制

opencv的平台依赖性太强,只能运行于X86电脑和嵌入式LINUX平台,这意味这一些底层永远都无法接触到。而且程序不可能跑在裸机平台上了。如果硬要一直到逻辑平台上的话代价比自己写一个程序要大。

2.自己想要搞清数字图像处理的底层实现

前段时间自己一直在看关于opencv的书,就看那些乱七八糟一堆一堆的函数,感觉啥都没有学到。 前段时间看到这样一句话,我们需要警惕开源软件。没错他们是很香,但是他们会让你失去自己的独立性,真正的程序员最好应该是不给自己任何可以依赖的东西的。

所以我决定除了C库什么都不依赖,所有底层全部自己敲一遍,同时尽可能为以后扩展跨平台的特性提供接口。

(二)框架设计

写代码写到了今天,不可能在向之前一样闭着眼睛就开始写代码。写代码之前理清思路可以给自己省去许多麻烦。

现在再来回顾一下项目需求。 1.只用C库实现。 2.为今后的跨平台特性提供接口 3.函数高内聚低耦合,具有较强的可移植性

下面是系统框图

实现这个最大的问题在于C语言并不支持类的使用,在重载,继承等特性上也没有,想要实现这种面对对象的程序设计我们得要另外再想方法。

在实现上面其实主要就是图片的读取,保存,加载,显示,拷贝,析构等功能。现在基本已经实现。

(三)程序设计

(1)BMP详解

BMP图片简介:

BMP图片是windows操作系统中的标准图像文件格式,可以分为两类:设备相关位图(DDB)和设备无关位图(DIB),使用广泛。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

BMP图片的存储:

1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息; 2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息; 3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板; 4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。

C 语言程序设计思路:

1、定义一个存储头文件数据结构体

typedef struct tagBITMAPFILEHEADER { unsigned short bfType; //保存图片类型。 'BM' unsigned long bfSize; //位图文件的大小,以字节为单位(3-6字节,低位在前) unsigned short bfReserved1;//位图文件保留字,必须为0(7-8字节) unsigned short bfReserved2;//位图文件保留字,必须为0(9-10字节) unsigned long bfOffBits; //RGB数据偏移地址,位图数据的起始位置,以相对于位图(11-14字节,低位在前) }BITMAPFILEHEADER;

2定义一个存储位图信息的结构体

typedef struct tagBITMAPINFOHEADER { unsigned long biSize; //本结构所占用字节数(15-18字节) unsigned long biWidth; //位图的宽度,以像素为单位(19-22字节) unsigned long biHeight; //位图的高度,以像素为单位(23-26字节) unsigned short biPlanes; //目标设备的级别,必须为1(27-28字节) unsigned short biBitCount; //每个像素所需的位数,必须是1(双色)(29-30字节),4(16色),8(256色)16(高彩色)或24(真彩色)之一 unsigned long biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节) //1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 unsigned long biSizeImage; //位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节) unsigned long biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节) unsigned long biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节) unsigned long biClrUsed; //位图实际使用的颜色表中的颜色数(47-50字节) unsigned long biClrImportant; //位图显示过程中重要的颜色数(51-54字节) }BITMAPINFOHEADER;

然后就是对于文件的一些读取写入的事情 在写程序是特别注意要将BMP的部分和图片有关处理的部分进行解耦。方便日后扩展其他的图片格式。

核心的文件主要是两个:bmp.c bmp.h

bmp.h

C图像类顶层头文件

/* * @Descripttion: 提供对于BMP文件的底层处理函数 * @version: V2.0 * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2020-10-26 19:35:49 * @LastEditors: Yueyang * @LastEditTime: 2020-10-31 12:06:11 */ #ifndef BMP_H_INCLUDED #define BMP_H_INCLUDED #include "stdio.h" #include "cv.h" /* * * 黑白:文件头(14字节)+信息头(40字节)+2个调色板(共8字节)+Height(图像高度)*(Width+8-Width%8)/8 16色:文件头(14字节)+信息头(40字节)+16个调色板(共64字节)+Height(图像高度)*(Width+4-Width%4)/2 256色:文件头(14字节)+信息头(40字节)+256个调色板(共1024字节)+Height(图像高度)*(Width+4-Width%4) 16位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*2 (由于每个像素由两个字节表示) 24位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*3 (由于每个像素由三个字节表示) * */ /** * 字 段 名 描 述 biSize(填40进去) 4 本结构的大小,根据不同的操作系统而不同,在Windows中,此字段的值总为28h字节=40字 biWidth 4 BMP图像的宽度,单位像素 biHeight 4 BMP图像的高度,单位像素 biPlanes(1) 2 总为1 biBitCount(DRP+1)*8 2 BMP图像的色深,即一个像素用多少位表示,常见有1、4、8、16、24和32,分别对应单色、16色、256色、16位高彩色、24位真彩色和32位增强型真彩色 biCompression(0) 4 压缩方式,0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定 biSizeImage 4 BMP图像数据大小,必须是4的倍数,图像数据大小不是4的倍数时用0填充补足 //可以决定在物理上图片的大小 biXPelsPerMeter(3780) 4 水平分辨率,单位像素/m biYPelsPerMeter(3780) 4 垂直分辨率,单位像素/m biClrUsed(0) 4 BMP图像使用的颜色,0表示使用全部颜色,对于256色位图来说,此值为100h=256 biClrImportant(0) 4 重要的颜色数,此值为0时所有颜色都重要,对于使用调色板的BMP图像来说, 当显卡不能够显示所有颜色时,此值将辅助驱动程序显示颜色 */ /* * 与BMP文件有关的变量 */ typedef struct { // bmih DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; }BITMAPINFOHEADER; /* *bfType:位图文件类型,必须是0x424D,即字符串“BM”,也就是说,所有的“*.bmp”文件的头两个字节都是“BM”。 *bfSize:位图文件大小,包括这14个字节。 *bfReserved1, bfReserved2:Windows保留字,暂不用。 *bfOffBits:(bfsize+bisize)从文件头到实际的位图数据的偏移字节数 */ typedef struct { // bmfh WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; }BITMAPFILEHEADER; //调色板 typedef struct { unsigned char rgbBlue; //该颜色的蓝色分量 unsigned char rgbGreen; //该颜色的绿色分量 unsigned char rgbRed; //该颜色的红色分量 unsigned char rgbReserved; //保留值 } RGBQuad; /** * @name: Read_bmp * @msg: 读取一个BMP图片 * @param char *filepath 读取文件的路径 * BITMAPFILEHEADER *bmf 与文件有关的信息 * BITMAPINFOHEADER *bmi 与图片有关的信息 * @return 数据指针 */ BYTE* Read_bmp(BYTE *filepath,BITMAPFILEHEADER *bmf,BITMAPINFOHEADER *bmi); /** * @name: * @msg: 写BMP图片,只负责写数据,没有图片的转换功能 * @param char *filepath 读取文件的路径 * BYTE *imgData 读到的数据 * BITMAPFILEHEADER *bmf 与文件有关的信息 * BITMAPINFOHEADER *bmi 与图片有关的信息 * PICTYPE pt 图片类型 * @return 0 (right) or -1(something wrong) */ BYTE Write_bmp(BYTE *filepath,BYTE *imgData,BITMAPFILEHEADER *bmf,BITMAPINFOHEADER *bmi,PICTYPE pt); void Print_bmp_FileHeader(BITMAPFILEHEADER *bmfh); void Print_bmp_InfoHeader(BITMAPINFOHEADER *bmih); #endif

bmp.c

/* * @Descripttion: BMP的底层函数 * @version: V 2.0 * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2020-11-04 01:22:01 * @LastEditors: Yueyang * @LastEditTime: 2020-11-10 22:40:10 */ #ifndef BMP_H #define BMP_H #include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include "string.h" #include "bmp.h" int Read_bmp_FileHeader(char *filepath,BITMAPFILEHEADER *bmfh) { FILE *fp; fp=fopen(filepath,"rb"); if(!fp) { return -1; } if(fread(&bmfh->bfType,sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmfh->bfSize,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmfh->bfReserved1,sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmfh->bfReserved2,sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmfh->bfOffBits,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } fclose(fp); return 0; } int Read_bmp_InfoHeader(char *filepath,BITMAPINFOHEADER *bmih) { FILE *fp; fp=fopen(filepath,"rb"); if(!fp) { return -1; } fseek(fp,14,SEEK_SET); if(fread(&bmih->biSize,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biWidth,sizeof(LONG),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biHeight,sizeof(LONG),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biPlanes,sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biBitCount,sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biCompression,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biSizeImage,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biXPelsPerMeter,sizeof(LONG),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biYPelsPerMeter,sizeof(LONG),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biClrUsed,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fread(&bmih->biClrImportant,sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } fclose(fp); return 0; } void Print_bmp_FileHeader(BITMAPFILEHEADER *bmfh) { printf("The contents in the file header of the BMP file:\n"); printf("bfOffBits: %ld\n",(long int)bmfh->bfOffBits); printf("bfReserved1: %ld\n",(long int)bmfh->bfReserved1); printf("bfReserved2: %ld\n",(long int)bmfh->bfReserved2); printf("bfSize: %ld\n",(long int)bmfh->bfSize); printf("bfType: %ld\n",(long int)bmfh->bfType); } void Print_bmp_InfoHeader(BITMAPINFOHEADER *bmih) { printf("The content in the info header of the BMP file:\n"); printf("biBitCount: %ld\n",(long int)bmih->biBitCount); printf("biClrImportant: %ld\n",(long int)bmih->biClrImportant); printf("biClrUsed: %ld\n",(long int)bmih->biClrUsed); printf("biCompression: %ld\n",(long int)bmih->biCompression); printf("biHeight: %ld\n",(long int)bmih->biHeight); printf("biPlanes: %ld\n",(long int)bmih->biPlanes); printf("biSize: %ld\n",(long int)bmih->biSize); printf("biSizeImage: %ld\n",(long int)bmih->biSizeImage); printf("biWidth: %ld\n",(long int)bmih->biWidth); printf("biXPelsPerMeter: %ld\n",(long int)bmih->biXPelsPerMeter); printf("biYPelsPerMeter: %ld\n",(long int)bmih->biYPelsPerMeter); } /** * @name: Read_bmp * @msg: 读取一个BMP图片 * @param char *filepath 读取文件的路径 * BITMAPFILEHEADER *bmf 与文件有关的信息 * BITMAPINFOHEADER *bmi 与图片有关的信息 * @return 数据指针 */ BYTE* Read_bmp(BYTE *filepath,BITMAPFILEHEADER *bmf,BITMAPINFOHEADER *bmi) { BYTE *imgData; BITMAPINFOHEADER bmih; BITMAPFILEHEADER bmfh; FILE *fp; u8 R, G, B; u16 pixel; int n,i; int width; int height; int bitCount; DWORD dwLineBytes; n=Read_bmp_FileHeader(filepath,&bmfh); if(n==-1) { printf("Can not read the file header of the BMP file.\n"); return NULL; } n=Read_bmp_InfoHeader(filepath,&bmih); if(n==-1) { printf("Can not read the info header of the BMP file.\n"); return NULL; } memcpy(bmi,&bmih,sizeof(BITMAPINFOHEADER)); memcpy(bmf,&bmfh,sizeof(BITMAPFILEHEADER)); #ifdef DEBUG Print_bmp_FileHeader(bmf); Print_bmp_InfoHeader(bmi); #endif // DEBUG width=bmih.biWidth; height=bmih.biHeight; bitCount=bmih.biBitCount; imgData=(BYTE*)malloc((bitCount/(8*sizeof(BYTE)))*width*height*sizeof(BYTE)); fp=fopen(filepath,"rb"); if(!fp) { printf("Can not open the file: %s\n",filepath); return NULL; } fseek(fp,bmfh.bfOffBits,SEEK_SET);//移动到数据开始的地方 if(fread(imgData,(bitCount/(8*sizeof(BYTE)))*width*height*sizeof(BYTE),1,fp)!=1) { free(imgData); fclose(fp); return NULL; } fclose(fp); return imgData; } /** * @name: * @msg: 写BMP图片,只负责写数据,没有图片的转换功能 * @param char *filepath 读取文件的路径 * BYTE *imgData 读到的数据 * BITMAPFILEHEADER *bmf 与文件有关的信息 * BITMAPINFOHEADER *bmi 与图片有关的信息 * PICTYPE pt 图片类型 * @return 0 (right) or -1(something wrong) */ BYTE Write_bmp(BYTE *filepath,BYTE *imgData,BITMAPFILEHEADER *bmf,BITMAPINFOHEADER *bmi,PICTYPE pt) { FILE *fp; int i; RGBQuad *IpRGBQuad; LONG height=bmi->biHeight; DWORD dwLineBytes=(bmi->biBitCount/(8*sizeof(BYTE)))*bmi->biWidth; fp=fopen(filepath,"wb"); if(!fp) { printf("Error: Can not open the file:%s\n",filepath); } switch (pt) { case LI_BMP_1://BW dwLineBytes=(bmi->biWidth +8-bmi->biWidth%8)/8; if(fwrite(&(bmf->bfType),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfSize),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved1),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved2),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfOffBits),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(bmi,40,1,fp)!=1) { fclose(fp); return -1; } /*图像的读取顺序是从下到上,从左到右*/ IpRGBQuad = (RGBQuad *)malloc(2*sizeof(RGBQuad)); IpRGBQuad[0].rgbRed = 0; IpRGBQuad[0].rgbGreen = 0; IpRGBQuad[0].rgbBlue = 0; IpRGBQuad[0].rgbReserved = 0; IpRGBQuad[1].rgbRed = 255; IpRGBQuad[1].rgbGreen = 255; IpRGBQuad[1].rgbBlue = 255; IpRGBQuad[0].rgbReserved = 0; fwrite(IpRGBQuad,8,1,fp); if(fwrite(imgData,dwLineBytes*height,1,fp)!=1) { fclose(fp); return -1; } break; case LI_BMP_8://GRAY LILOG("WRITE GRAY"); if(fwrite(&(bmf->bfType),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfSize),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved1),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved2),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfOffBits),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(bmi,40,1,fp)!=1) { fclose(fp); return -1; } /*图像的读取顺序是从下到上,从左到右*/ IpRGBQuad = (RGBQuad *)malloc(256*sizeof(RGBQuad));//灰度图为8位的调色板数据为256个结构,1024个字节 for(int i = 0;i < 256;i++){ IpRGBQuad[i].rgbRed = i; IpRGBQuad[i].rgbGreen = i; IpRGBQuad[i].rgbBlue = i; IpRGBQuad[i].rgbReserved = 0; } fwrite(IpRGBQuad,1024,1,fp); if(fwrite(imgData,height*dwLineBytes,1,fp)!=1) { fclose(fp); return -1; } break; case LI_BMP_565://BMP565 printf("暂不支持,实在不行自己写\n"); /* code */ break; case LI_BMP_888://BMP888 //因为存在字节对齐问题不能一次写入 if(fwrite(&(bmf->bfType),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfSize),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved1),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved2),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfOffBits),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(bmi,40,1,fp)!=1) { fclose(fp); return -1; } if(fwrite(imgData,height*dwLineBytes,1,fp)!=1) { fclose(fp); return -1; } break; case LI_BMP_32://BMP32 if(fwrite(&(bmf->bfType),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfSize),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved1),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfReserved2),sizeof(WORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(&(bmf->bfOffBits),sizeof(DWORD),1,fp)!=1) { fclose(fp); return -1; } if(fwrite(bmi,40,1,fp)!=1) { fclose(fp); return -1; } if(fwrite(imgData,height*dwLineBytes,1,fp)!=1) { fclose(fp); return -1; } break; default: break; } } #endif // !BMP_H

li_Image.h

图像类实现Li_Image

/* * @Descripttion: 实现图像类的初始化接口 * @version: * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2020-10-27 22:43:25 * @LastEditors: Yueyang * @LastEditTime: 2020-11-13 18:06:26 */ #ifndef LI_IMAGE_H #define LI_IMAGE_H #include "cv.h" #include "bmp.h" /** * @name: li_free_arr * @msg: 为LiArr释放内存 * @param {void} * @return {void} */ LI_API void li_free_arr(LiArr* arr); /** * @name: li_malloc_arr * @msg: 为LiArr申请内存 * @param {size}申请内存的大小 * @return {LiArr}数组类型 */ LI_API LiArr* li_malloc_arr(LONG size); /** * @name: Li_Create_Mat * @msg: * @param LONG width,LONG height, 高和宽 BYTE depth, 图像深度 * @return {Li_Mat} */ Li_Mat* Li_Create_Mat(LiArr* data,LONG width,LONG height,BYTE depth); /** * @name: Li_Destroy_Mat * @msg: 为LiMat释放内存 * @param {void} * @return {void} */ void Li_Destroy_Mat(Li_Mat* mat); /** * @name: Li_Load_Image * @msg: 用户接口函数,用来读取一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * PICTYPE pt 图片类型 * @return 0 (right) or -1(something wrong) */ LI_API Li_Image* Li_Load_Image(BYTE* filepath,PICTYPE pt); /** * @name: Li_Save_Image * @msg: 用户接口函数,用来保存一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * Li_Image* img Litecv图片类型 * @return 0 (right) or -1(something wrong) */ LI_API BYTE Li_Save_Image(BYTE* filepath,Li_Image* img); /** * @name: Li_Destroy_Image * @msg: 用户接口函数,用来删除一张图片 * @param Li_Image * img * @return 0 (right) or -1(something wrong) */ LI_API void Li_Destroy_Image(Li_Image * img); /** * @name: Li_Create_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Create_Image(LONG width,LONG height,BYTE depth,PICTYPE pth); /** * @name: Li_Copy_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Copy_Image(Li_Image *img); /** * @name: Li_CvtColor * @msg: 提供数组的类型转换不提供类型检查,目标指针在函数内不分配内存 * @param const LiArr* src 原数据 LiArr *dst 目标数据 BYTE cvtype 转换方式 * @return Li_Image* 一张图片 */ LI_API void Li_CvtColor(const LiArr* src,LiArr *dst,LONG width,LONG height,BYTE cvtype); /** * @name: Li_Convert_Image * @msg: 提供图片类型转化,图片类型指针在 * @param const LiArr* src 原数据 LiArr *dst 目标数据 BYTE cvtype 转换方式 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Convert_Image(const Li_Image* src,BYTE convertype); /** * @name: Li_Get_Roi * @msg: 获取感兴趣区域 * @param {Li_Image* img 原图像 * LONG x1 左下角所在列号 * LONG y1 左下角所在行号 * LONG x2 右上角所在列号 * LONG y2} 右上角所在行号 * @return {Li_Image*} */ LI_API Li_Image* Li_Get_Roi(Li_Image* img,LONG x1,LONG y1,LONG x2,LONG y2); /** * @name: Li_ReShape * @msg: 调整图像大小 * @param Li_Image* img 原图像 * LONG tag_width 图像宽度 * LONG tag_height图像高度 * @return {*} */ LI_API Li_Image* Li_ReShape(Li_Image* img,LONG tag_width,LONG tag_height); #endif // !LI_IMAGE_H

li_image.c

/* * @Descripttion: 图像类基本的初始化函数 * @version: V 2.0 * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2020-10-27 22:41:59 * @LastEditors: Yueyang * @LastEditTime: 2020-11-10 22:33:27 */ #include "cv.h" #include "stdlib.h" #include "string.h" #undef LOG #define LOG "LI_CV_CORE" #include "bmp.c" #include "png.c" #include "jpeg.c" /** * @name: li_malloc_arr * @msg: 为LiArr申请内存 * @param {size}申请内存的大小 * @return {LiArr}数组类型 */ LI_API LiArr* li_malloc_arr(LONG size) { return (LiArr*)malloc((size_t)size); } /** * @name: li_free_arr * @msg: 为LiArr释放内存 * @param {void} * @return {void} */ void li_free_arr(LiArr* arr) { return free((void *)arr); } /** * @name: Li_Destroy_Mat * @msg: 为LiMat释放内存 * @param {void} * @return {void} */ void Li_Destroy_Mat(Li_Mat* mat) { li_free_arr(mat->arr); free((void*)mat); } /** * @name: li_bgr_at * @msg: * @param {Li_Image* mat * LONG x 指针所在列号 * LONG y 所在行号} * @return {*} */ LiArr* li_bgr_at(Li_Image* mat,LONG x,LONG y) { if(x<mat->width&&y<mat->height&&x>=0&&y>=0) return mat->data+mat->width*(1+mat->imgdepth)*y+(1+mat->imgdepth)*x; else { LILOG("BEYOND THE MAT"); return NULL; } } LiArr* li_gray_at(Li_Image* mat,LONG x,LONG y) { if(x<mat->width&&y<mat->height&&x>=0&&y>=0) return mat->data+mat->width*1*y+1*x; else { //LILOG("BEYOND THE MAT"); return NULL; } } LiArr* li_rgba_at(Li_Image* mat,LONG x,LONG y) { if(x<mat->width&&y<mat->height&&x>=0&&y>=0) return mat->data+mat->width*4*y+4*x; else { LILOG("BEYOND THE MAT"); return NULL; } } /** * @name: Li_Create_Mat * @msg: * @param LONG width,LONG height, 高和宽 BYTE depth, 图像深度 * @return {Li_Mat} */ Li_Mat* Li_Create_Mat( LiArr* data, LONG width,LONG height, BYTE depth) { Li_Mat* li_mat=(Li_Mat*)malloc(sizeof(Li_Mat)); li_mat->datadepth= depth; li_mat->height = height; li_mat->width = width; li_mat->matsize=width*height; if(depth!=LI_DEP_1U) { li_mat->Bitcount =(depth+1)*8; li_mat->arrsize=li_mat->matsize*(depth+1); } else { li_mat->Bitcount=1; li_mat->arrsize=li_mat->matsize*1/8;//对于二值化图像一个BYTE代表8个像素点 } li_mat->arr=li_malloc_arr(li_mat->arrsize); memcpy(li_mat->arr,data,li_mat->arrsize); return li_mat; } /** * @name: ptr_li_image_create * @msg: 创建Li_image 类型指针 * @param * LiArr* data,(已经初始化过的数据指针) * LONG width,LONG height, * BYTE depth,PICTYPE pth(图片类型) * @return {Li_Image}一个图片 */ Li_Image* ptr_li_image_create( LiArr* dt, LONG width,LONG height, BYTE depth,PICTYPE pth) { Li_Image* img =(Li_Image*)malloc(sizeof(Li_Image)); Li_Mat* limt=NULL; img->pt=pth; img->data=dt; img->width=width; img->height=height; img->imgdepth=depth; switch (pth) { case LI_BMP_888: limt= Li_Create_Mat(dt,width,height,depth); memcpy(&img->limat,limt,sizeof(Li_Mat));//数据指针一并过来了,所以li_mat->arr不能释放 img->at=li_bgr_at; break; case LI_JPEG: limt= Li_Create_Mat(dt,width,height,depth); memcpy(&img->limat,limt,sizeof(Li_Mat));//数据指针一并过来了,所以li_mat->arr不能释放 img->at=li_bgr_at; break; case LI_BMP_8: limt= Li_Create_Mat(dt,width,height,depth); memcpy(&img->limat,limt,sizeof(Li_Mat));//数据指针一并过来了,所以li_mat->arr不能释放 img->at=li_gray_at; break; case LI_BMP_32: limt= Li_Create_Mat(dt,width,height,depth); memcpy(&img->limat,limt,sizeof(Li_Mat));//数据指针一并过来了,所以li_mat->arr不能释放 img->at=li_rgba_at; break; case LI_PNG: limt= Li_Create_Mat(dt,width,height,depth); memcpy(&img->limat,limt,sizeof(Li_Mat));//数据指针一并过来了,所以li_mat->arr不能释放 img->at=li_rgba_at; break; default: break; } return img; } /** * @name: ptr_li_image_destroy * @msg: 销毁Li_image 类型指针 * @param {Li_Image* }一个图片指针 * * @return void */ void ptr_li_image_destroy(Li_Image* img) { li_free_arr(img->limat.arr); free(img); } //获取默认的BMP文件头 BITMAPFILEHEADER get_default_file_head(Li_Mat m) { LONG DataSizePerLine = (m.width * m.Bitcount /8+ 3) / 4*4; BITMAPFILEHEADER bf; if(m.datadepth!=LI_DEP_8U&&m.datadepth!=LI_DEP_1U) bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)-2; else if(m.datadepth==LI_DEP_8U) bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+1024-2; else if(m.datadepth==LI_DEP_1U) bf.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+8-2; bf.bfSize =DataSizePerLine*m.height+bf.bfOffBits; bf.bfType =19778;//"BM" bf.bfReserved1=0; bf.bfReserved2=0; return bf; } //获取默认的BMP信息 BITMAPINFOHEADER get_default_info_head(Li_Mat m) { LONG DataSizePerLine = (m.width * m.Bitcount /8+ 3) / 4*4; BITMAPINFOHEADER bi; bi.biBitCount=m.Bitcount; bi.biSize =40; bi.biWidth =m.width; bi.biHeight =m.height; bi.biPlanes =1; bi.biClrImportant=0; bi.biClrUsed =0;//默认全都用 if(m.Bitcount!=32){ bi.biXPelsPerMeter=3780;//默认大小 bi.biYPelsPerMeter=3780; }else { bi.biXPelsPerMeter=0;//默认大小 bi.biYPelsPerMeter=0; } bi.biSizeImage=DataSizePerLine*bi.biHeight; bi.biCompression=0;//默认不压缩 return bi; } /** * @name: Li_Save_Image * @msg: 用户接口函数,用来保存一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * Li_Image* img Litecv图片类型 * @return 0 (right) or -1(something wrong) */ LI_API BYTE Li_Save_Image(BYTE* filepath,Li_Image* img) { if(img==NULL) { LILOG("A NULL IMG"); return -1; } BYTE sta; BITMAPFILEHEADER bf; BITMAPINFOHEADER bi; switch (img->pt) { case LI_BMP_888: bf= get_default_file_head(img->limat); bi= get_default_info_head(img->limat); sta=Write_bmp(filepath,img->data,&bf,&bi,img->pt); break; case LI_BMP_32: bf= get_default_file_head(img->limat); bi= get_default_info_head(img->limat); sta=Write_bmp(filepath,img->data,&bf,&bi,img->pt); break; case LI_BMP_8: bf= get_default_file_head(img->limat); bi= get_default_info_head(img->limat); sta=Write_bmp(filepath,img->data,&bf,&bi,img->pt); break; #ifdef USE_JPEG case LI_JPEG: LILOG("WRITE JPEG"); sta=Write_Jpeg(filepath,img->data,100,img->limat.width,img->limat.height); break; #endif #ifdef USE_PNG case LI_PNG: sta=Write_Png(filepath,img->data,img->limat.width,img->limat.height); break; #endif default: break; } return sta; } /** * @name: Li_Load_Image * @msg: 用户接口函数,用来读取一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * PICTYPE pt 图片类型 * @return 0 (right) or -1(something wrong) */ LI_API Li_Image* Li_Load_Image(BYTE* filepath,PICTYPE pt) { BYTE* data; LONG width,height; int depth; BITMAPFILEHEADER bf; BITMAPINFOHEADER bi; Li_Image* img=NULL; switch (pt) { case LI_BMP_888: LILOG("BMP888"); data=Read_bmp(filepath,&bf,&bi); width=bi.biWidth; height=bi.biHeight; depth=LI_DEP_24U; img=ptr_li_image_create(data,width,height,depth,pt); break; case LI_BMP_32: LILOG("BMP32"); data=Read_bmp(filepath,&bf,&bi); width=bi.biWidth; height=bi.biHeight; depth=LI_DEP_32U; img=ptr_li_image_create(data,width,height,depth,pt); break; case LI_BMP_8: LILOG("BMP8"); data=Read_bmp(filepath,&bf,&bi); width=bi.biWidth; height=bi.biHeight; depth=LI_DEP_8U; img=ptr_li_image_create(data,width,height,depth,pt); break; case LI_BMP_565: LILOG("BMP16"); data=Read_bmp(filepath,&bf,&bi); width=bi.biWidth; height=bi.biHeight; depth=LI_DEP_16U; img=ptr_li_image_create(data,width,height,depth,pt); break; #ifdef USE_JPEG case LI_JPEG: LILOG("JPEG"); data=Read_Jpeg(filepath,&width,&height); depth=LI_DEP_24U; img=ptr_li_image_create(data,width,height,depth,pt); break; #endif #ifdef USE_PNG case LI_PNG: LILOG("PNG"); data=Read_Png(filepath,&width,&height); depth=LI_DEP_32U; img=ptr_li_image_create(data,width,height,depth,pt); break; #endif default: break; } return img; } /** * @name: Li_Destroy_Image * @msg: 用户接口函数,用来删除一张图片 * @param Li_Image * img * @return 0 (right) or -1(something wrong) */ LI_API void Li_Destroy_Image(Li_Image * img) { ptr_li_image_destroy(img); } /** * @name: Li_Create_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Create_Image( LONG width,LONG height, BYTE depth,PICTYPE pth) { LiArr * data=li_malloc_arr(width*height*(depth+1)); memset(data,0xFF,width*height*(depth+1)); return ptr_li_image_create(data,width,height,depth,pth); } /** * @name: Li_Copy_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Copy_Image(Li_Image *img) { Li_Image * out=NULL; out=Li_Create_Image(img->width,img->height,img->imgdepth,img->pt); memcpy((void*)out->data,(void*)img->data,img->width*img->height*(img->imgdepth+1)); return out; } #include "li_convert.c" #include "li_painter.c"

怎样使用这个图片类读取各种类型的图片

main.c

/* * @Descripttion: 底层图片IO测试 * @version: V 2.0 * @Author: Yueyang * @email: 1700695611@qq.com * @Date: 2020-10-26 19:35:49 * @LastEditors: Yueyang * @LastEditTime: 2020-11-04 15:50:21 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <malloc.h> #include "bmp.h" #include "cv.h" #include "li_image.h" int main() { //加载保存销毁图片 Li_Image* out=Li_Load_Image("./picture/whu_rgb888.bmp",LI_BMP_888); Li_Save_Image("./picture/1.bmp",out); Li_Destroy_Image(out); Li_Image* out3=Li_Load_Image("./picture/whu_gray.bmp",LI_BMP_8); Li_Save_Image("./picture/2.bmp",out3); Li_Destroy_Image(out3); Li_Image* out4=Li_Load_Image("./picture/whu_rgba.bmp",LI_BMP_32); Li_Save_Image("./picture/3.bmp",out4); Li_Destroy_Image(out4); Li_Image* out1=Li_Load_Image("./picture/whu_png.png",LI_PNG); Li_Save_Image("./picture/1.png",out1); Li_Destroy_Image(out1); Li_Image* out2=Li_Load_Image("./picture/whu_jpg.jpg",LI_JPEG); Li_Save_Image("./picture/1.jpg",out2); Li_Destroy_Image(out2); //创建图片并操作像素 BYTE* ptr=NULL; Li_Image* out7 =Li_Create_Image(300,300,LI_DEP_24U,LI_BMP_888); ptr=out7->at(out7,10,10); if(ptr!=NULL){ memset(ptr,0xFF,1); memset(ptr+1,0,1); memset(ptr+2,0,1); } Li_Save_Image("./picture/1.bmp",out3); Li_Destroy_Image(out7); Li_Image* out8 =Li_Load_Image("./picture/whu_jpg.jpg",LI_JPEG); ptr=out8->at(out8,10,10); if(ptr!=NULL){ memset(ptr,0xFF,1); memset(ptr+1,0,1); memset(ptr+2,0,1); } Li_Save_Image("./picture/2.jpg",out8); Li_Destroy_Image(out8); Li_Image* out5 =Li_Load_Image("./picture/whu_png.png",LI_PNG); ptr=out5->at(out5,10,10); if(ptr!=NULL){ memset(ptr,0xFF,1); memset(ptr+1,0,1); memset(ptr+2,0,1); } Li_Save_Image("./picture/2.png",out5); Li_Image* out6=Li_Copy_Image(out5); Li_Save_Image("./picture/3.png",out6); LILOG("over"); return 0; }

(四)写在最后

其实重新造轮子这种事情看似没有意义,可是在这个过程中所能学到的东西绝不是会用opencv的几个函数所能相比的。 到目前为止笔者已经分别在WINDOWS,X86_LINUX,ARM_LINUX这三个平台上适配了这个图形系统。我会持续的在github中完善这个系统。 github地址:

Litecv github源码地址

开发手册:

(一)Core API

/** * @name: li_free_arr * @msg: 为LiArr释放内存 * @param {void} * @return {void} */ LI_API void li_free_arr(LiArr* arr); /** * @name: li_malloc_arr * @msg: 为LiArr申请内存 * @param {size}申请内存的大小 * @return {LiArr}数组类型 */ LI_API LiArr* li_malloc_arr(LONG size); /** * @name: Li_Create_Mat * @msg: * @param LONG width,LONG height, 高和宽 BYTE depth, 图像深度 * @return {Li_Mat} */ Li_Mat* Li_Create_Mat(LiArr* data,LONG width,LONG height,BYTE depth); /** * @name: Li_Destroy_Mat * @msg: 为LiMat释放内存 * @param {void} * @return {void} */ void Li_Destroy_Mat(Li_Mat* mat); /** * @name: Li_Load_Image * @msg: 用户接口函数,用来读取一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * PICTYPE pt 图片类型 * @return 0 (right) or -1(something wrong) */ LI_API Li_Image* Li_Load_Image(BYTE* filepath,PICTYPE pt); /** * @name: Li_Save_Image * @msg: 用户接口函数,用来保存一张图片 * 对于是否支持jpeg 与 png 可以通过cv.h中的宏定义调整 * @param * BYTE *filepath 图片路径 * Li_Image* img Litecv图片类型 * @return 0 (right) or -1(something wrong) */ LI_API BYTE Li_Save_Image(BYTE* filepath,Li_Image* img); /** * @name: Li_Destroy_Image * @msg: 用户接口函数,用来删除一张图片 * @param Li_Image * img * @return 0 (right) or -1(something wrong) */ LI_API void Li_Destroy_Image(Li_Image * img); /** * @name: Li_Create_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Create_Image(LONG width,LONG height,BYTE depth,PICTYPE pth); /** * @name: Li_Copy_Imgae * @msg: 用户接口函数,用来创建一张图片 * @param LONG width 图片宽度 LONG height 图片高度 BYTE depth 颜色深度 PICTYPE pth 图片类型 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Copy_Image(Li_Image *img); /** * @name: Li_CvtColor * @msg: 提供数组的类型转换不提供类型检查,目标指针在函数内不分配内存 * @param const LiArr* src 原数据 LiArr *dst 目标数据 BYTE cvtype 转换方式 * @return Li_Image* 一张图片 */ LI_API void Li_CvtColor(const LiArr* src,LiArr *dst,LONG width,LONG height,BYTE cvtype); /** * @name: Li_Convert_Image * @msg: 提供图片类型转化,图片类型指针在 * @param const LiArr* src 原数据 LiArr *dst 目标数据 BYTE cvtype 转换方式 * @return Li_Image* 一张图片 */ LI_API Li_Image* Li_Convert_Image(const Li_Image* src,BYTE convertype); /** * @name: Li_Get_Roi * @msg: 获取感兴趣区域 * @param {Li_Image* img 原图像 * LONG x1 左下角所在列号 * LONG y1 左下角所在行号 * LONG x2 右上角所在列号 * LONG y2} 右上角所在行号 * @return {Li_Image*} */ LI_API Li_Image* Li_Get_Roi(Li_Image* img,LONG x1,LONG y1,LONG x2,LONG y2); /** * @name: Li_ReShape * @msg: 调整图像大小 * @param Li_Image* img 原图像 * LONG tag_width 图像宽度 * LONG tag_height图像高度 * @return {*} */ LI_API Li_Image* Li_ReShape(Li_Image* img,LONG tag_width,LONG tag_height);

(二)IMGPROC 模块

/** * @name: Li_Split * @msg: 图像色道分离 * @param {Li_Image* img} * @return {Li_Image** 图像指针的指针} */ LI_API void Li_Split(Li_Image* img,Li_Image** out); /** * @name: Li_Split * @msg: 图像色道分离 * @param {Li_Image* img 原图像 * Li_Image** 色道分离以后的图像} * @return {} */ LI_API Li_Image* Li_Combine(Li_Image** out,BYTE depth); /** * @name: Li_Threshold * @msg: 图像阈值化 * @param {Li_Image* img 原图像 * double threshold 图像阈值0-255 * } * @return {Li_Image* 二值化后的灰白图像} */ LI_API Li_Image* Li_Threshold(Li_Image* img,double threshold); /** * @name: Li_Convolute * @msg: 计算图像卷积 * @param {Li_Image* img 卷积图像 * Li_Kernel* kernal 卷积核 } * @return {Li_Image*} */ LI_API Li_Image* Li_Convolute(Li_Image* img,Li_Kernel* kernal); /** * @name: Li_GetKernel * @msg: 得到卷积核矩阵 * @param {*} * @return {*} */ LI_API Li_Kernel* Li_GetKernel(double* data,BYTE KernalKind); /** * @name: Li_Smooth * @msg: 计算图像滤波 * @param {Li_Image* img 原图像 * BYTE smoothtype( Li_GAUSS, //高斯滤波 Li_AVERAGE, //均值滤波 Li_MEDIUM, //中值滤波)} * @return {Li_Image*} */ LI_API Li_Image* Li_Smooth(Li_Image* img,BYTE smoothtype); /** * @name: Li_Sharp * @msg: 图像锐化 * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_Sharp(Li_Image* img); /** * @name: Li_Emboss * @msg: 图像雕版 * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_Emboss(Li_Image* img); /** * @name: Li_Salt_Noise * @msg: 图像椒盐噪声 * @param {Li_Image* img * LONG num 噪点数量} * @return {Li_Image*} */ LI_API Li_Image* Li_Salt_Noise(Li_Image* img,LONG num); enum CannyType { LI_CANNY_ROBERTS, LI_CANNY_SOBEL, LI_CANNY_PREWITT, LI_CANNY_MYDEFINE }; /** * @name: Li_Double_Threshold * @msg: 图像双阈值化 * @param {Li_Image* img 原图像 * double min 小阈值 * double max 大阈值 * } * @return {Li_Image* 二值化后的灰白图像} */ LI_API Li_Image* Li_Double_Threshold(Li_Image* img,double min,double max); /** * @name: Li_Canny * @msg: 参考文章 https://blog.csdn.net/HUSTER_Gy/article/details/102942452?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160498444419724838560446%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160498444419724838560446&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-11-102942452.first_rank_ecpm_v3_pc_rank_v2&utm_term=canny%E8%BE%B9%E7%BC%98%E6%A3%80%E6%B5%8B%E7%AE%97%E6%B3%95c%E5%AE%9E%E7%8E%B0&spm=1018.2118.3001.4449 * 图像砍尼检测 * @param {Li_Image* img 原图像 * BYTE CannyType选择算子 * BYTE min 最大阈值 * BYTE max} 最小阈值 * @return {*} */ LI_API Li_Image* Li_Canny(Li_Image* img,BYTE CannyType,BYTE min,BYTE max); /** * @name: Li_Hough_Line * @msg: 霍夫直线变换 * @param {Li_Image* img 原图像 * LiLine* lines 返回的直线类型指针 * LONG maxthrea 允许的最大角度 * LONG maxr 语序的最大半径} * @return {*} */ LI_API void Li_Hough_Line(Li_Image* img,LiLine* lines, LONG maxthrea,LONG maxr); /** * @name: Li_Hough_Circle_R * @msg: 固定半径的Hough变换 * @param {Li_Image* img 原图像 * LiCircle* circles 返回的圆指针(不会再内部分配内存) * LONG R 变换半径 * LONG* range} 圆心得范围 * @return {*} */ LI_API LONG Li_Hough_Circle_R(Li_Image* img,LiCircle* circles, LONG R,LONG* range); /** * @name: Li_Hough_Circle * @msg: 给定范围的Hough变换 * @param {Li_Image* img 原图像 * LiCircle* circles 返回的圆指针(不会再内部分配内存) * LONG Rmin,LONG Rmax 最小最大半径 * LONG* range} 圆心得范围 * @return {*} */ LI_API void Li_Hough_Circle(Li_Image* img,LiCircle* circles, LONG Rmin,LONG Rmax,LONG* range); /** * @name: Li_Dilate * @msg: 图像膨胀(局部最小) * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_Dilate(Li_Image* img); /** * @name: Li_Erode * @msg: 图像腐蚀(局部最小) * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_Erode(Li_Image* img); /** * @name: Li_Add * @msg: 图像像素相加 * @param {Li_Image* img1,Li_Image* img2} * @return {Li_Image*} */ LI_API Li_Image* Li_Add(Li_Image* img1,Li_Image* img2); /** * @name: Li_Minus * @msg: 图像像素相加 * @param {Li_Image* img1,Li_Image* img2} * @return {Li_Image*} */ LI_API Li_Image* Li_Minus(Li_Image* img1,Li_Image* img2); /** * @name: Li_Grandient * @msg: 形态学梯度 * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_Grandient(Li_Image* img); /** * @name: Li_Mod_Open * @msg: 形态学开运算 * @param {Li_Image* img} * @return {Li_Image* } */ LI_API Li_Image* Li_Mod_Open(Li_Image* img); /** * @name: Li_Mod_Close * @msg: 形态学闭运算 * @param {Li_Image* img} * @return {Li_Image* } */ LI_API Li_Image* Li_Mod_Close(Li_Image* img); /** * @name: Li_TopHat * @msg: 图像顶帽运算 * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_TopHat(Li_Image* img); /** * @name: Li_BlackHat * @msg: 图像黑帽运算 * @param {Li_Image* img} * @return {Li_Image*} */ LI_API Li_Image* Li_BlackHat(Li_Image* img); /** * @name: Li_Visual_Hist * @msg: 直方图转化为图像 * @param {Li_Hist* hist} * @return {Li_Image*} */ LI_API Li_Image* Li_Visual_Hist(Li_Hist* hist); /** * @name: Li_Get_Hist * @msg: 绘制直方图 * @param {Li_Image* img 图像} * @return {Li_Hist* 直方图数据} */ LI_API Li_Hist* Li_Get_Hist(Li_Image* img); /** * @name: Li_Print_Hist * @msg: 打印直方图 * @param {Li_Hist* hist} * @return {*} */ LI_API void Li_Print_Hist(Li_Hist* hist); /** * @name: Li_Normalize_Hist * @msg: 直方图均衡化 * @param {Li_Image* img} * @return {*} */ LI_API Li_Image* Li_Normalize_Hist(Li_Image* img);
最新回复(0)