(一)背景介绍
这段时间接到了一个新活,是关于图像处理的一个探地摄像头的项目。所以也差不多是时候开始学习一下数字图像处理的知识了。本来我们的方案是直接移植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);