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

在这里插入图片描述

(一)背景介绍

这段时间接到了一个新活,是关于图像处理的一个探地摄像头的项目。所以也差不多是时候开始学习一下数字图像处理的知识了。本来我们的方案是直接移植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);
  • 21
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论
C语言可以通过以下步骤实现BMP图像添加高斯噪声: 1.首先,需要引入一个随机数生成器函数来生成高斯噪声。可以使用标准C库中的rand()函数来生成一个0到RAND_MAX(通常是32767)之间的随机整数。可以将其归一化为0到1之间的浮点数,并使用均值为0,标准差为sigma的高斯分布函数生成高斯噪声值。 float gaussNoise(float sigma) { float u1, u2, v1, v2, s; do { u1 = rand() / ((float)RAND_MAX); u2 = rand() / ((float)RAND_MAX); v1 = 2 * u1 - 1; v2 = 2 * u2 - 1; s = v1 * v1 + v2 * v2; } while (s >= 1 || s == 0); float mul = sqrt(-2.0 * log(s) / s); return v2 * mul * sigma; } 2.读取BMP图像的像素数据。可以使用图像处理库,如OpenCV来读取BMP图像的像素数据。 unsigned char* imageData; // 存储图像像素数据的指针 int width, height; // 图像的宽度和高度 int channels; // 图像的通道数 // 使用OpenCV读取BMP图像,将像素数据存储在imageData指针中 // 获取图像的宽度、高度和通道数 3.遍历图像的每个像素,并为每个像素添加高斯噪声。 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { for (int c = 0; c < channels; c++) { float noise = gaussNoise(sigma); // 根据设定的标准差生成高斯噪声 imageData[(i * width + j) * channels + c] += (unsigned char)noise; } } } 4.将处理后的像素数据保存为新的BMP图像。同样可以使用图像处理库来保存处理后的像素数据为BMP图像。 // 使用OpenCV保存像素数据为BMP图像 // 释放imageData的内存 这样,通过以上步骤,利用C语言实现了对BMP图像添加高斯噪声的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

与光同程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值