数字图像处理——细胞识别(C++)

前言

顺手写的一个基于数字图像处理的细胞识别,其主要内容包括:

1、一点点window api应用,

  • 开一幅图片
  • 保存图片
  • 在指定位置输出一个像素点。
  • 增加一个线程,在主线程忙碌时输出…

数字图像处理的知识有点多,包括但不限于,

2、二值化图像操作:

  • 击中保留,像素点周围与模板相同,则保留
  • 击中删除,像素点周围与模板相同,则删除
  • 膨胀,将像素点周围变成和模板一样
  • 腐蚀,如果像素点周围和模板一样,则删除
  • 边缘计算,如果像素点周围与模板一样,则删除,最后只剩边缘
  • 与,对应像素计算,全1则1,否则为0
  • 或,对应像素计算,全0则0,否则为1
  • 非,对应像素计算,取反
  • 减,对应像素计算,只有1-0为1,其他1-1,0-0,0-1都为0
  • 加,对应像素计算,全0则0,否则为1
  • bit转gray灰度图,对二值化图像进行t*t的模板积分(加)操作,得到灰度图。

3、灰度图操作

  • 定制操作,传递三个函数名称:像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数。
  • sobel,索贝尔算法计算图像边缘
  • 均值滤波
  • 提亮,像素点灰度值比例放大
  • 图像均衡化
  • ostu,大津法动态阈值
  • 转为二值化图像,通过阈值,划分为二值化图像

4、RGB图片操作

  • 定制操作,传递三个函数:像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数
  • 转为灰度图,RGB求均值
  • 指定颜色通道sobel,索贝尔算法计算边缘
  • 指定颜色通道均值滤波
  • 指定颜色通道提亮,像素点倍乘
  • 指定颜色通道均衡化
  • 指定颜色通道ostu,大津法动态阈值
  • rgb图片转二值化图片

5、其他图片操作

  • 加,对应位置像素叠加
  • 种子生长,使用二值化图片像素点作为种子,在rgb图像上判断,如果相邻点像素相近,则扩展对应的二值化图片像素。
  • 图片转连通域,对图片计算连通域,并保存到容器
  • 连通域转图片,将连通域转为图片
  • 显示连通域

6、main.c中记录了所有函数使用方法

注意事项

该程序运行在win10电脑上,win11电脑上运行时,会显示不出图片,原因是win11电脑获取当前窗口句柄函数GetForegroundWindow()失效,一共两处,替换成NULL即可。例如 hdc = GetDC(GetForegroundWindow());替换为 hdc = GetDC(NULL)

程序

image.h

/********************************************************************************
* 文件名称:image.h
* 功    能:相关头文件包含,部分函数定义,函数声明,类定义
* 作    者:陈亮
* 版    本:version1
* 日    期:2022.9.24
* 版权所属:陈亮,侵权必究
********************************************************************************/

#pragma once
#ifndef IMAGE_H
#define IMAGE_H

//注意******************************************模板定义*********************************************
//vector<uint8> v({0});是1*1模板
//vector<uint8> v({1,255,1,255,255,255,1,255,1});是3*3模板(9个数从第一行到第三行)
//1表示忽略该点,0表示该点不能有像素,255表示该点必须有像素
//也可以定义成{1,255,1,255,255,255,1,255,1}
//如果模板元素全部相同,则可以快速声明vector<uint8>(9,255),即创建3*3的255模板
//vector<uint8>(9,0),即创建3*3的0模板

//注意******************************************图像定义*********************************************
//像素点rgb不一定相同为彩色图
//像素点rgb相同为灰度图
//像素点r要么为0,要么为255的是二值化图
//彩色图操作可以用于灰度图和二值化图,灰度图可以当做特殊彩色图,二值化图可以当做特殊彩色图
//灰度图操作可以用于二值化图片,二值化图片可以当做特殊灰度图

//注意******************************************代码定义*********************************************
//代码中行line列row一般指的是从图像左上开始到右下结束从0-line,从0到row
//代码中高height,宽width一般指的是图像像素宽度高度
//所以一般一幅图高h,宽w,从0到h-1行,从0到w-1列,h=line+1,w=row+1
//图像计算区间指的是[linemin,linemax)、[rowmin,rowmax)矩形区间

//******************************************所用头文件包含*******************************************
#include "fstream"
#include "iostream"
#include "Windows.h"
#include "vector"
#include "string"
#include "thread"

//******************************************类型定义*************************************************
typedef char int8;
typedef unsigned char uint8;
typedef short int16;
typedef unsigned short uint16;
typedef int int32;
typedef unsigned int uint32;

//******************************************内联函数定义*********************************************
#define ABS(x)	((x)<0?-(x):(x))
#define LIMIT(x,a,b)	(	((x)<(a))	?	(a)	:	((x)>(b)?(b):(x))	)

//******************************************一个像素内,颜色与首地址偏移*****************************
#define R	2
#define G	1
#define B	0

//******************************************使用标准域名*********************************************
#ifndef STD
#define STD
using namespace std;
#endif
//******************************************int 数最大值定义*****************************************
#ifndef INT_MAX
#define INT_MAX 0x7fffffff
#endif

#define SCREEN_X	700
#define SCREEN_Y	80

//******************************************点定义(line,row)*****************************************
struct MPOINT {	//点
	int line;
	int row;

	//作用:用line,row新建一个MPOINT点
	MPOINT(int lline = 0, int rrow = 0) { line = lline; row = rrow; }
};

//******************************************单个连通域定义vector<MPOINT>点容器***********************
struct AREA {	//连通域
	vector<MPOINT> a;
	int linemin;
	int linemax;
	int rowmin;
	int rowmax;
	int leftup;
	int leftdown;
	int rightup;
	int rightdown;
	void init(){
		linemin = 0;
		linemax = 0;
		rowmin = 0;
		rowmax = 0;
		leftup = 0;
		leftdown = 0;
		rightup = 0;
		rightdown = 0;
		a.clear();
	}

	//作用:新建一个空连通域
	AREA() { init(); }

	//作用:新建一个点的连通域
	AREA(MPOINT p) { init(); a.push_back(p); linemax = linemin = p.line; rowmax = rowmin = p.row; }

	//作用:新建一个和aa一样的连通域
	AREA(vector<MPOINT>& aa) { init(); a = aa; }

	//输入:无
	//输出:无
	//作用:对该连通域求line,row区间
	//作者:陈亮
	//日期:2022.9.18
	void get_area()
	{
		if (a.size() == 0)
		{
			linemax = -1;
			linemin = -1;
			rowmax = -1;
			rowmin = -1;
			return;
		}
		linemax = a[0].line;
		linemin = a[0].line;
		rowmax = a[0].row;
		rowmin = a[0].row;
		for (auto iter = a.begin(); iter != a.end(); iter++)
		{
			if (iter->line > linemax)		linemax = iter->line;
			if (iter->line < linemin)		linemin = iter->line;
			if (iter->row > rowmax)			rowmax = iter->row;
			if (iter->row < rowmin)			rowmin = iter->row;
		}
	}

	//输入:无
	//输出:无
	//作用:对连通域求左上截距,右上截距,左下截距,右下截距(也就是连通域斜着的区间)
	//作者:陈亮
	//时间:9.23
	//注意:坐标系为y=line,x=row,右上为正
	void get_intercept()
	{
		leftup = linemin - rowmax;
		leftdown = linemax + rowmax;
		rightup = linemin + rowmin;
		rightdown = linemax - rowmin;
		for (auto iter = a.begin(); iter != a.end(); iter++)
		{
			if (iter->line - iter->row > leftup)
				leftup = iter->line - iter->row;
			if (iter->line + iter->row < leftdown)
				leftdown = iter->line + iter->row;
			if (iter->line + iter->row > rightup)
				rightup = iter->line + iter->row;
			if (iter->line - iter->row < rightdown)
				rightdown = iter->line - iter->row;
		}
	}
};

//******************************************图像处理大类*********************************************
class IMG_TYPE {
private:	//私有成员********************************************************
	BITMAPFILEHEADER file_head;	//位图文件头
	BITMAPINFOHEADER info_head;	//位图信息头
	HDC hdc;					//绘图窗口句柄

	//输入:无
	//输出:无
	//作用:默认赋值
	//作者:陈亮
	//日期:2022.9.18
	void init();

public:		//变量定义********************************************************
	//该图像类是否有效标志位
	int flag;

	//图像宽度
	int width;

	//图像高度
	int height;

	//图像数组
	uint8* image;

	//非真彩图的颜色表
	RGBQUAD* table;

public:		//构造函数********************************************************
	//创建空图像
	IMG_TYPE() { init(); }

	//打开图片创建图像
	IMG_TYPE(const char* add) { init(); open(add); }

	//创建指定大小图像
	IMG_TYPE(int line_size, int row_size) { init(); set_size(line_size, row_size); }

	//创建新图像,copy另一个图像
	IMG_TYPE(IMG_TYPE& other) { init();	copy(other); }

	//清空图像内存
	~IMG_TYPE() { close(); }

public:		//接口函数********************************************************

	//输入:无
	//输出:无
	//作用:清空图片
	//作者:陈亮
	//日期:2022.9.18
	void close();

	//输入:无
	//输出:无
	//作用:图片像素置0
	//作者:陈亮
	//日期:2022.9.18
	void clear();

	//输入:打开文件地址
	//输出:0成功,-1失败
	//作用:打开图片
	//作者:陈亮
	//日期:2022.9.18
	int open(const char* add);

	//输入:另一副图像,有效区间
	//输出:-1拷贝失败,0拷贝成功
	//作用:拷贝图片
	//作者:陈亮
	//日期:2022.9.18
	int copy(IMG_TYPE& other, int linemin = -1,int linemax=INT_MAX,int rowmin=-1,int rowmax=INT_MAX);

	//输入:高度,宽度,像素值
	//输出:0成功 -1失败
	//作用:设置图像宽度高度,原图像被清空了
	//作者:陈亮
	//日期:2022.9.18
	int set_size(int line_size = -1, int row_size = -1, uint8 r = 0, uint8 g = 0, uint8 b = 0);

	//输入:保存地址
	//输出:-1保存失败,0保存成功
	//作用:保存图片
	//作者:陈亮
	//日期:2022.9.18
	int save(const char* add = nullptr);
	int save(string s) { return save(s.c_str()); }
	
	//输入:图像行,列,颜色信息
	//输出:该点某颜色值
	//作用:返回图片像素点颜色引用
	//作者:陈亮
	//日期:2022.9.18
	//注意:该函数需求效率,没有入参检测,调用之前应该检测入参
	uint8& get_RGB(int line, int row, int color);

	//输入:图片左上点在屏幕xy处,仅显示图片line,row处像素点
	//输出:无
	//作用:打印图片像素点
	//作者:陈亮
	//日期:2022.9.18
	//注意:该函数需求效率,没有入参检测,调用之前应该检测入参
	void show_point(int line, int row, int x = SCREEN_X, int y = SCREEN_Y);

	//输入:图片左上点在屏幕xy处,图像显示区域
	//输出:0显示成功,-1显示失败
	//作用:打印图片
	//作者:陈亮
	//日期:2022.9.18
	int show(int x = SCREEN_X, int y = SCREEN_Y, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:统计区域内非零像素,图像计算区域
	//输出:-1统计错误,否则返回图像非0像素点个数
	//作用:返回图像非0像素,一般用于求位图像素
	//作者:陈亮
	//日期:2022.9.18
	int count(int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

public:		//二值化图像操作********************************************************
	
	//前提:src原图像必须是二值化图像
	//输入:原图像src,目的图像drt,用于击中的模板(默认3*3),图像计算区域
	//输出:满足击中模板的数量
	//作用:保留src原图像中满足模板的点到drt目的图像中(击中保留)
	//作者:陈亮
	//日期:2022.9.18
	static int bit_hit(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v=vector<uint8>(9,255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:原图像src,目的图像drt,用于击中的模板(默认3*3),图像计算区域
	//输出:满足击中模板的数量
	//作用:去除原图像src满足匹配条件的像素点,剩余点保留到drt(击中删除)
	//作者:陈亮
	//日期:2022.9.18
	static int bit_nhit(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(9, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:二值化原图像src,目的图像drt,用于扩展的模板(默认3*3),图像计算区间
	//输出:新扩展点的数量
	//作用:如果像素中心和模板中心匹配,则将像素点附近扩展为模板
	//作者:陈亮
	//日期:2022.9.18
	static int bit_extend(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(9, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:二值化原图像src,目的图像drt,用于保留的模板(默认3*3),图像计算区间
	//输出:剩余像素点数量
	//作用:如果像素和模板匹配,则将像素保留,否则删除(收缩)
	//作者:陈亮
	//日期:2022.9.18
	static int bit_shrink(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(9, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:二值化原图像src,目的图像drt,用于删除的模板(默认3*3),图像计算区间
	//输出:删除点的数量
	//作用:如果像素和模板匹配,则将像素删除,否则保留(只留边缘)
	//作者:陈亮
	//日期:2022.9.18
	static int bit_edge(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(9, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);


	//前提:img、src原图像必须是二值化图像
	//输入:用于计算的二值化图像,二值化原图像src,目的图像drt
	//输出:最终图像总像素
	//作用:drt=src&img
	//作者:陈亮
	//日期:2022.9.18
	static int bit_and(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:img、src原图像必须是二值化图像
	//输入:用于计算的二值化图像,原图像src,目的图像drt,图像计算区间
	//输出:drt总像素
	//作用:drt=src|img
	//作者:陈亮
	//日期:2022.9.18
	static int bit_or(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:img、src原图像必须是二值化图像
	//输入:用于计算的二值化图像,原图像src,目的图像drt,图像计算区间
	//输出:剩余像素数目
	//作用:drt=!src
	//作者:陈亮
	//日期:2022.9.18
	static int bit_not(IMG_TYPE* src, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:img、src原图像必须是二值化图像
	//输入:用于计算的二值化图像,二值化原图像src,目的图像drt,图像计算区间
	//输出:减掉的像素数目
	//作用:drt=src-img
	//作者:陈亮
	//日期:2022.9.18
	static int bit_sub(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:img、src原图像必须是二值化图像
	//输入:用于计算的二值化图像,二值化原图像src,目的图像drt,图像计算区间
	//输出:drt总像素
	//作用:drt=src+img
	//作者:陈亮
	//日期:2022.9.18
	static int bit_add(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:二值化原图像src,目的图像drt,用于计算的模板(默认3*3),图像计算区间
	//输出:-1错误,0计算完成,drt为灰度图
	//作用:对二值化图像src进行t*t的积分操作,得到灰度图存入drt
	//作者:陈亮
	//日期:2022.9.18
	static int bit_to_gray(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(9, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

public:		//灰度图操作********************************************************
	//输入:灰度图片src,目的图片地址drt,像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数,图像计算区间
	//输出:满足条件的像素点数量
	//作用:像素点满足if_fun函数,执行if_do_fun,否则执行else_do_fun,结果保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_sift(IMG_TYPE* src, IMG_TYPE* drt,
		bool(*if_fun)(uint8& src_r, uint8& src_g, uint8& src_b),
		void(*if_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b), 
		void(*else_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
		int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,目的图片地址drt,放大倍数(差分不够明显时可以增加放大倍数),图像计算区间
	//输出:-1失败,0成功
	//作用:对灰度图片计算边缘,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_sobel(IMG_TYPE* src, IMG_TYPE* drt, float magnification = 1, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,目的图片地址drt,滤波模板,图像计算区间
	//输出:-1失败,0成功
	//作用:对灰度图片进行模板均值滤波(默认7*7),保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_mean(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v = vector<uint8>(49, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,目的图片地址drt,图像计算区间
	//输出:-1失败,0成功
	//作用:对灰度图片进行像素倍乘(提亮),保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_bright(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,目的图片地址drt,图像计算区间
	//输出:-1失败,0成功
	//作用:对灰度图片图像均衡化,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_equalization(IMG_TYPE* src, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,图像计算区间
	//输出:-1失败,返回动态阈值
	//作用:对灰度图片图像求动态阈值
	//作者:陈亮
	//日期:2022.9.18
	static int gray_ostu(IMG_TYPE* src, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:灰度图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,返回二值化图像像素点数量
	//作用:对灰度图片图像二值化,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int gray_to_bit(IMG_TYPE* src, IMG_TYPE* drt, int threshold = 128, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

public:		//RGB图操作********************************************************

	//输入:彩色图片src,目的图片地址drt,像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数,图像计算区间
	//输出:满足条件的像素点数量
	//作用:像素点满足if_fun函数,执行if_do_fun,否则执行else_do_fun,结果保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_sift(IMG_TYPE* src, IMG_TYPE* drt,
		bool(*if_fun)(uint8& src_r, uint8& src_g, uint8& src_b),
		void(*if_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
		void(*else_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
		int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:彩色图片src,目的图片地址drt,图像计算区间
	//输出:-1失败,0成功
	//作用:对彩色图片src灰度化,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_to_gray(IMG_TYPE* src, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//输入:彩色图片src,目的图片地址drt,放大倍数(差分不够明显时可以增加放大倍数),图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,0成功
	//作用:对彩色图片计算边缘,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_sobel(IMG_TYPE* src, IMG_TYPE* drt, float magnification = 1, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:彩色图片src,目的图片地址drt,滤波模板,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,0成功
	//作用:对彩色图片进行模板均值滤波(默认7*7),保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_mean(IMG_TYPE* src, IMG_TYPE* drt , const vector<uint8>& v = vector<uint8>(49, 255), int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,0成功
	//作用:对彩色图片进行像素倍乘(提亮),保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_bright(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,0成功
	//作用:对彩色图片图像均衡化,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_equalization(IMG_TYPE* src, IMG_TYPE* drt, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:彩色图片src,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,返回动态阈值
	//作用:对彩色图片图像求动态阈值
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_ostu(IMG_TYPE* src, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
	//输出:-1失败,返回二值化图像像素点数量
	//作用:对彩色图片图像二值化,保存到drt
	//作者:陈亮
	//日期:2022.9.18
	static int rgb_to_bit(IMG_TYPE* src, IMG_TYPE* drt, int threshold = 128, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX, int flag_r = 1, int flag_g = 1, int flag_b = 1);
	
public:		//其他函数

	//输入:图片src,图片img,目的图片地址drt,图像计算区间,选择颜色(0未选中,1选中)
	//输出:-1失败,0成功
	//作用:像素值叠加,src+img=drt,一般用于图像标注
	//作者:陈亮
	//日期:2022.9.18
	static int img_add(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, 
		int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX,
		int flag_r = 1, int flag_g = 1, int flag_b = 1);

	//输入:rgb图像,二值化种子图像src,目的图像drt,相邻连续像素差值,图像计算区间
	//输出:生长扩张出的像素点数量
	//作用:用src图像像素作为种子,在rgb图像上用种子8邻域生长(生长条件:rgb像素和种子像素差值在3以内)
	//作者:陈亮
	//日期:2022.9.18
	//注意:一般用于对颜色提取的扩张,由于颜色提取要求苛刻,很多像素不能满足,通过生长补全该颜色提取
	static int img_grow(IMG_TYPE* rgb, IMG_TYPE* src, IMG_TYPE* drt, int pixel_sub = 3, int linemin = -1, int linemax = INT_MAX, int rowmin = -1, int rowmax = INT_MAX);

	//前提:src原图像必须是二值化图像
	//输入:存储连通域的容器v,二值化原图像src,伪彩色图像保存地址
	//输出:连通域容器里面连通域个数
	//作用:对src求连通域,存入v中,同时对src伪彩色处理保存到drt中,最大支持65535个连通域
	//作者:陈亮
	//日期:2022.9.18
	static int img_to_connected(vector<AREA>& v, IMG_TYPE* src, IMG_TYPE* drt, int linemin = -1,int linemax=INT_MAX,int rowmin=-1,int rowmax= INT_MAX);

	//输入:连通域a,像素值rgb
	//输出:连通域内像素点数量
	//作用:将连通域转为固定颜色图像,颜色默认为白色
	//作者:陈亮
	//日期:2022.9.18
	static int connected_to_img(AREA& a, IMG_TYPE* drt, uint8 r = 255, uint8 g = 255, uint8 b = 255);

	//输入:连通域容器v,像素值rgb(默认是伪彩色,如果设置颜色,则所有连通域会显示该颜色)
	//输出:总连通域内像素点数量
	//作用:将连通域转为图片(先将目的图片清空),默认转为伪彩色,可以修改为固定颜色
	//作者:陈亮
	//日期:2022.9.18
	static int connected_to_img(vector<AREA>& v, IMG_TYPE* drt, uint8 r = 1, uint8 g = 1, uint8 b = 1);

};

//输入:连通域a,连通域所在图片在屏幕上的位置xy,连通域a显示的颜色rgb(默认黑色)
//输出:无
//作用:显示连通域(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)(默认黑色)
//作者:陈亮
//日期:2022.9.18
void show(AREA& a, int x = SCREEN_X, int y = SCREEN_Y, uint8 r = 255, uint8 g = 255, uint8 b = 255);

//输入:连通域容器v,连通域所在图片在屏幕上的位置xy,显示的颜色rgb(默认伪彩色,可设置为固定颜色)
//输出:无
//作用:显示连通域(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)(默认伪彩色,可设置为固定颜色)
//作者:陈亮
//日期:2022.9.18
void show(vector<AREA>& v, int x = SCREEN_X, int y = SCREEN_Y, uint8 r = 1, uint8 g = 1, uint8 b = 1);

//输入:一幅彩色RGB细胞图片,目的图片,一个存储细胞连通域的容器
//输出:-1失败,0成功
//作用:计算图片src中细胞位置,对原图像标注后保存到目的图片,将细胞连通域存入连通域容器中
//作者:陈亮
//日期:2022.9.18
int cells_sift(vector<AREA>& v, vector<AREA>& cells);

//等待线程所运行的函数
void wait_thread_handle();

//等待线程开启标志,及数据读取次数控制
extern int run_flag;
extern double get_times;

#endif

image.c

/********************************************************************************
* 文件名称:main.cpp
* 功    能:函数使用实例,细胞识别方案
* 作    者:陈亮
* 版    本:version1
* 日    期:2022.9.24
* 版权所属:陈亮,侵权必究
********************************************************************************/

#include "image.h"
#include <cmath>

//等待线程开启标志,及数据读取次数控制
int run_flag = 1;
double get_times = 0;

//等待线程所运行的函数
void wait_thread_handle()
{
	while (run_flag)
	{
		if (get_times > 5000)
		{
			get_times = 0;
			cout << ".";
		}
		Sleep(500);
	}
}

//输入:无
//输出:无
//作用:默认赋值
//作者:陈亮
//日期:2022.9.18
void IMG_TYPE::init()
{
	int i = 0;
	*((uint8*)(&file_head) + i++) = 66;
	*((uint8*)(&file_head) + i++) = 77;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0; 
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 54; 
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	*((uint8*)(&file_head) + i++) = 0;
	i = 0;
	*((uint8*)(&info_head) + i++) = 40;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 3;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 64;
	*((uint8*)(&info_head) + i++) = 2;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 1;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 24;
	*((uint8*)(&info_head) + i++) = 0; 
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 18;
	*((uint8*)(&info_head) + i++) = 11;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 18;
	*((uint8*)(&info_head) + i++) = 11;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	*((uint8*)(&info_head) + i++) = 0;
	hdc = GetDC(GetForegroundWindow());
	flag = 0;
	width = 0;
	height = 0;
	image = nullptr;
	table = nullptr;
}

//输入:无
//输出:无
//作用:清空类
//作者:陈亮
//日期:2022.9.18
void IMG_TYPE::close()
{
	flag = 0;
	width = 0;
	height = 0;
	delete image;
	delete table;

	table = nullptr;
	image = nullptr;
}

//输入:无
//输出:无
//作用:图片像素置0
//作者:陈亮
//日期:2022.9.18
void IMG_TYPE::clear()
{
	for (int line = 0; line < height; line++)
		for (int row = 0; row < width; row++)
		{
			get_RGB(line, row, R) = 0;
			get_RGB(line, row, G) = 0;
			get_RGB(line, row, B) = 0;
		}
}

//输入:打开文件地址
//输出:0成功,-1失败
//作用:打开图片
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::open(const char* add)
{
	if (flag)
	{
		cout << "打开失败,缓冲中已有图片" << endl;
		return -1;
	}

	close();	//清空所以内存
	ifstream ifile(add, std::ios::binary);
	flag = 1;				//默认打开成功
	if (ifile.is_open())
	{
		ifile.read((char*)(&file_head), sizeof(BITMAPFILEHEADER));	//读取BITMAPFILEHEADER
		if (file_head.bfType != 0x4D42)								//校验:bfType是否为“BM” 0x424D
		{
			cout << "打开失败,文件格式错误" << endl;
			flag = 0;
		}
		else
		{

			ifile.read((char*)(&info_head), sizeof(BITMAPINFOHEADER));	//读取BITMAPINFOHEADER并解析
			width = info_head.biWidth;
			height = info_head.biHeight;

			int m_byteCount = info_head.biBitCount / 8;					//定义待会用的的数
			int lineByte = (width * m_byteCount + 3) / 4 * 4;

			if (m_byteCount != 3)//判断是否为真彩色图像,若不是则读取颜色表
			{
				table = new RGBQUAD[(uint32)pow(2, info_head.biBitCount)];//建立RGBQUAD数组存储数据
				for (int i = 0; i < pow(2, info_head.biBitCount); i++)
				{
					ifile.read((char*)(table + i), sizeof(RGBQUAD));//读取颜色表
				}
			}

			image = new uint8[(long long)width * (long long)height * (long long)m_byteCount];//new 足够大的unsigned char 数组,开始读入数据
			for (int i = height - 1; i > -1; i--)
			{
				ifile.read((char*)(image + (long long)i * (long long)width * (long long)m_byteCount), 
					(long long)width * (long long)m_byteCount);//按行读入数据
				//ifile.seekg((std::streamoff)lineByte - (std::streamoff)width * (std::streamoff)m_byteCount, std::ios::cur);//跳过每行尾部填充区
			}
		}
	}
	else
	{
		cout << "打开失败,未找到" << add << endl;
		flag = 0;
	}

	if (flag == 1)		//打开成功
	{
		ifile.close();
		return 0;
	}
	else
	{
		close();
		ifile.close();
		return -1;
	}
}

//输入:另一副图像,有效区间
//输出:-1拷贝失败,0拷贝成功
//作用:拷贝图片
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::copy(IMG_TYPE& other,int linemin,int linemax,int rowmin,int rowmax)			//拷贝图像
{
	if (&other == this)
		return 0;

	if (other.flag)
	{
		if (linemin < 0)					linemin = 0;
		if (linemax > other.height)			linemax = other.height;
		if (rowmin < 0)						rowmin = 0;
		if (rowmax > other.width)			rowmax = other.width;

		close();
		*this = other;			//赋值数据

		height = linemax - linemin;
		width = rowmax - rowmin;

		//地址数据需要重新创建
		if (height == other.height && width == other.width)
		{	//拷贝颜色表
			int m_byteCount = info_head.biBitCount / 8;
			if (m_byteCount != 3)				//判断是否为真彩色图像,若不是则拷贝颜色表
			{
				table = new RGBQUAD[(uint32)pow(2, info_head.biBitCount)];//建立RGBQUAD数组存储数据
				memcpy(table, other.table, (uint32)pow(2, info_head.biBitCount) * sizeof(RGBQUAD));//读取颜色表
			}

			//拷贝图像
			image = new uint8[(long long)width * (long long)height * (long long)m_byteCount];			//建立图像数据存储空间
			memcpy(image, other.image, (long long)height * (long long)width * (long long)m_byteCount);	//拷贝图像
		}
		else
		{// ***************************************该处可能有错误,颜色表不应该全部拷贝,但是不知道该拷贝那一段*********
			//拷贝颜色表
			int m_byteCount = info_head.biBitCount / 8;
			if (m_byteCount != 3)				//判断是否为真彩色图像,若不是则拷贝颜色表
			{
				table = new RGBQUAD[(uint32)pow(2, info_head.biBitCount)];//建立RGBQUAD数组存储数据
				memcpy(table, other.table, (uint32)pow(2, info_head.biBitCount) * sizeof(RGBQUAD));//读取颜色表
			}

			//拷贝图像
			image = new uint8[(long long)width * (long long)height * (long long)m_byteCount];			//建立图像数据存储空间

			for (int line = 0; line < height; line++)
				for (int row = 0; row < width; row++)
				{
					get_RGB(line, row, R) = other.get_RGB(line + linemin, row + rowmin, R);
					get_RGB(line, row, G) = other.get_RGB(line + linemin, row + rowmin, G);
					get_RGB(line, row, B) = other.get_RGB(line + linemin, row + rowmin, B);
				}
		}	
		return 0;
	}
	else
	{
		cout << "拷贝失败,目标图片无效" << endl;
		return -1;
	}
}

//输入:高度,宽度,像素值
//输出:0成功 -1失败
//作用:设置图像宽度高度,原图像被清空了
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::set_size(int height_size, int width_size, uint8 r, uint8 g, uint8 b)
{
	if (height_size == -1 && width_size == -1)
		return 0;

	//默认值处理
	if (flag)
	{
		if (height_size == -1)			height_size = height;
		if (width_size == -1)			width_size = width;
	}
	else
	{
		if (height_size == -1)			height_size = 0;
		if (width_size == -1)			width_size = 0;
	}

	//创建新内存
	height = height_size;
	width = width_size;
	delete image;
	image = nullptr;
	if (r == g && g == b)	//默认初始化
	{
		image = new uint8[(long long)height_size * (long long)width_size * 3]();
	}
	else
	{
		image = new uint8[(long long)height_size * (long long)width_size * 3];
		for (int line = 0; line < height; line++)
			for (int row = 0; row < width; row++)
			{
				get_RGB(line, row, R) = r;
				get_RGB(line, row, G) = g;
				get_RGB(line, row, B) = b;
			}
	}
	flag = 1;
	return 0;
}

static int save_num = 0;
//输入:保存地址
//输出:-1保存失败,0保存成功
//作用:保存图片
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::save(const char* add)
{
	if (!flag)
	{
		cout << "保存失败,图片无效" << endl;
		return -1;
	}

	char buff[32] = { 0 };
	if (add == nullptr)
	{
		sprintf_s(buff, "./save/%d.bmp", save_num++);
		add = buff;
	}

	ofstream ofile(add, std::ios::binary);
	if (ofile.is_open())
	{
		ofile.write((char*)(&file_head), sizeof(BITMAPFILEHEADER));	//写入BITMAPFILEHEADER

		info_head.biWidth = width;
		info_head.biHeight = height;
		ofile.write((char*)(&info_head), sizeof(BITMAPINFOHEADER));	//写入BITMAPINFOHEADER

		int m_byteCount = info_head.biBitCount / 8;					//定义待会用的的数
		int lineByte = (width * m_byteCount + 3) / 4 * 4;

		if (m_byteCount != 3)//判断是否为真彩色图像,若不是则写入颜色表
		{
			for (int i = 0; i < pow(2, info_head.biBitCount); i++)
			{
				ofile.write((char*)(table + i), sizeof(RGBQUAD));//写入颜色表
			}
		}

		for (int i = height - 1; i > -1; i--)
		{
			ofile.write((char*)(image + (long long)i * (long long)width * (long long)m_byteCount), 
				(long long)width * (long long)m_byteCount);//按行写入数据
			ofile.write((char*)(image + (long long)i * (long long)width * (long long)m_byteCount),
				(long long)lineByte - (long long)width * (long long)m_byteCount);//每行尾部填充区
		}
	}
	else
	{
		cout << "保存失败,未找到" << add << ",请先手动创建save文件夹" << endl;
		return -1;
	}

	ofile.close();
	return 0;
}


//输入:图像行,列,颜色信息
//输出:该点某颜色值
//作用:返回图片像素点颜色信息
//作者:陈亮
//日期:2022.9.18
//注意:该函数需求效率,没有入参检测,调用之前应该检测入参
uint8& IMG_TYPE::get_RGB(int line, int row, int color)
{
	get_times++;
	return *(image + (long long)line * (long long)width * 3 + (long long)row * 3 + (long long)color);
}

//输入:图片左上点在屏幕xy处,仅显示图片line,row处像素点
//输出:无
//作用:打印图片像素点
//作者:陈亮
//日期:2022.9.18
//注意:该函数需求效率,没有入参检测,调用之前应该检测入参
void IMG_TYPE::show_point(int line, int row, int x, int y)
{
	SetPixel(hdc, row + x, line + y, RGB(get_RGB(line, row, R), get_RGB(line, row, G), get_RGB(line, row, B)));
}

//输入:图片左上点在屏幕xy处,图像显示区间
//输出:0显示成功,-1显示失败
//作用:打印图片
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::show(int x, int y, int linemin, int linemax, int rowmin, int rowmax)
{
	if (!flag)
	{
		cout << "显示失败,图像无效" << endl;
		return -1;
	}

	if (linemin < 0)					linemin = 0;
	if (linemax > height)				linemax = height;
	if (rowmin < 0)						rowmin = 0;
	if (rowmax > width)					rowmax = width;

	//显示图像
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
			show_point(line, row, x, y);

	return 0;
}

//输入:图像计算区间
//输出:-1统计错误,否则返回图像非0像素点个数
//作用:返回图像非0像素,一般用于求位图像素
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::count(int linemin, int linemax, int rowmin, int rowmax)
{
	if (flag == 0)
	{
		cout << "统计错误,图像无效" << endl;
		return -1;
	}

	if (linemin < 0)					linemin = 0;
	if (linemax > height)				linemax = height;
	if (rowmin < 0)						rowmin = 0;
	if (rowmax > width)					rowmax = width;
	
	int num = 0;
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
			if (get_RGB(line, row, R) != 0 || get_RGB(line, row, G) != 0 || get_RGB(line, row, B) != 0)
				num++;

	return num;
}

//前提:src原图像必须是二值化图像
//输入:原图像src,目的图像drt,用于击中的模板(默认3*3),图像计算区域
//输出:满足击中模板的数量
//作用:保留src原图像中满足模板的点到drt目的图像中(击中保留)
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_hit(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{	
	int temp_size = 0;									//据中心最远步进
	if (v.size() == 1 * 1)			temp_size = 0;
	else if (v.size() == 3 * 3)		temp_size = 1;
	else if (v.size() == 5 * 5)		temp_size = 2;
	else if (v.size() == 7 * 7)		temp_size = 3;
	else if (v.size() == 9 * 9)		temp_size = 4;
	else
	{
		cout << "击中失败,只支持1*1,3*3,5*5,7*7,9*9模板" << endl;
		return -1;
	}
	auto iter = v.begin() + (v.size() - 1) / 2;			//模板中心
	int line_size = 2 * temp_size + 1;					//模板行数

	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "击中失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "击中失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	int num = 0;										//击中数

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			int x = LIMIT(line, linemin + temp_size, linemax - temp_size - 1);
			int y = LIMIT(row, rowmin + temp_size, rowmax - temp_size - 1);
			if (*iter == 1 || src->get_RGB(line, row, R) == *iter)	//中心像素满足条件
			{
				int flag_temp = 1;			//模板匹配成功标志位

				for (int i = -temp_size; flag_temp && i <= temp_size; i++)
					for (int j = -temp_size; j <= temp_size; j++)
					{
						if (*(iter + (long long)i * (long long)line_size + (long long)j) != 1 &&
							src->get_RGB(x + i, y + j, R) != *(iter + (long long)i * (long long)line_size + (long long)j))
							flag_temp = 0;
					}
			
				if (flag_temp)		//匹配成功
				{
					drt->get_RGB(line, row, R) = 255;
					drt->get_RGB(line, row, G) = 255;
					drt->get_RGB(line, row, B) = 255;
					num++;
				}
				else
				{
					drt->get_RGB(line, row, R) = 0;
					drt->get_RGB(line, row, G) = 0;
					drt->get_RGB(line, row, B) = 0;
				}
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
				;
			else
			{
				cout << "击中失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:src原图像必须是二值化图像
//输入:原图像src,目的图像drt,用于击中的模板(默认3*3),图像计算区域
//输出:满足击中模板的数量
//作用:去除原图像src满足匹配条件的像素点,剩余点保留到drt(击中删除)
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_nhit(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{	
	int temp_size = 0;									//据中心最远步进
	if (v.size() == 1 * 1)			temp_size = 0;
	else if (v.size() == 3 * 3)		temp_size = 1;
	else if (v.size() == 5 * 5)		temp_size = 2;
	else if (v.size() == 7 * 7)		temp_size = 3;
	else if (v.size() == 9 * 9)		temp_size = 4;
	else
	{
		cout << "击中失败,只支持1*1,3*3,5*5,7*7,9*9模板" << endl;
		return -1;
	}
	auto iter = v.begin() + (v.size() - 1) / 2;			//模板中心
	int line_size = 2 * temp_size + 1;					//模板行数

	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "击中失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "击中失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->copy(*src);				//目的地址图片等于原图片
	//***************************源地址目地址处理******************************

	int num = 0;										//击中数

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			int x = LIMIT(line, linemin + temp_size, linemax - temp_size - 1);
			int y = LIMIT(row, rowmin + temp_size, rowmax - temp_size - 1);
			if (*iter == 1 || src->get_RGB(line, row, R) == *iter)		//本像素点满足模板
			{
				int flag_temp = 1;											//匹配成功标志
				for (int i = -temp_size; flag_temp && i <= temp_size; i++)
				{
					for (int j = -temp_size; j <= temp_size; j++)
					{
						if (*(iter + (long long)i * (long long)line_size + (long long)j) != 1 &&
							src->get_RGB(x + i, y + j, R) != *(iter + (long long)i * (long long)line_size + (long long)j))
							flag_temp = 0;
					}
				}
				if (flag_temp == 1)
				{
					drt->get_RGB(line, row, R) = 0;
					drt->get_RGB(line, row, G) = 0;
					drt->get_RGB(line, row, B) = 0;
					num++;
				}
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
				;
			else
			{
				cout << "击中失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:src原图像必须是二值化图像
//输入:二值化原图像src,目的图像drt,用于扩展的模板(默认3*3),图像计算区间
//输出:新扩展点的数量
//作用:如果像素中心和模板中心匹配,则将像素点附近扩展为模板
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_extend(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{	
	int temp_size = 0;									//据中心最远步进
	if (v.size() == 1 * 1)			temp_size = 0;
	else if (v.size() == 3 * 3)		temp_size = 1;
	else if (v.size() == 5 * 5)		temp_size = 2;
	else if (v.size() == 7 * 7)		temp_size = 3;
	else if (v.size() == 9 * 9)		temp_size = 4;
	else if (v.size() == 11 * 11)		temp_size = 5;
	else
	{
		cout << "扩展失败,只支持1*1,3*3,5*5,7*7,9*9,11*11模板" << endl;
		return -1;
	}
	auto iter = v.begin() + (v.size() - 1) / 2;			//模板中心
	int line_size = 2 * temp_size + 1;					//模板行数
														
	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "扩展失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "扩展失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->copy(*src);				//目的地址图片=原图片
	//***************************源地址目地址处理******************************

	int num = 0;										//击中数

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			int x = LIMIT(line, linemin + temp_size, linemax - temp_size - 1);
			int y = LIMIT(row, rowmin + temp_size, rowmax - temp_size - 1);
			if (*iter == 1 || src->get_RGB(line, row, R) == *iter)	//中心匹配
			{//扩展
				for (int i = -temp_size; i <= temp_size; i++)
					for (int j = -temp_size; j <= temp_size; j++)
					{
						if (*(iter + (long long)i * (long long)line_size + j) == 255 && drt->get_RGB(x + i, y + j, R) == 0)
						{//按照模板扩展
							drt->get_RGB(x + i, y + j, R) = 255;
							num++;
						}
					}
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
				;
			else
			{
				cout << "扩展失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:src原图像必须是二值化图像
//输入:二值化原图像src,目的图像drt,用于收缩的模板(默认3*3),图像计算区间
//输出:剩余像素点数量
//作用:如果像素和模板匹配,则将像素保留,否则删除(收缩)
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_shrink(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{
	return IMG_TYPE::bit_hit(src, drt, v, linemin, linemax, rowmin, rowmax);
}

//前提:src原图像必须是二值化图像
//输入:二值化原图像src,目的图像drt,用于删除的模板(默认3*3),图像计算区间
//输出:删除点的数量
//作用:如果像素和模板匹配,则将像素删除,否则保留(只留边缘)
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_edge(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{
	return IMG_TYPE::bit_nhit(src, drt, v, linemin, linemax, rowmin, rowmax);
}

//前提:img、src原图像必须是二值化图像
//输入:用于计算的二值化图像,二值化原图像src,目的图像drt
//输出:最终图像总像素
//作用:drt=src&img
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_and(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{	//***************************源地址目地址处理******************************
	if (img == nullptr || img->flag == 0)
	{
		cout << "图与失败,img图像无效" << endl;
		return -1;
	}
	if (src == nullptr || src->flag == 0)
	{
		cout << "图与失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图与失败,drt保存地址为空" << endl;
		return -1;
	}
	if (img->height != src->height || img->width != src->width)
	{
		cout << "图与失败,img和src图像大小不匹配" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (linemax > img->height)				linemax = img->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;
	if (rowmax > img->width)				rowmax = img->width;

	int num = 0;
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
		{
			if (src->get_RGB(line, row, R) == 255 && img->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 255;
				drt->get_RGB(line, row, G) = 255;
				drt->get_RGB(line, row, B) = 255;
				num++;
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 0;
				drt->get_RGB(line, row, G) = 0;
				drt->get_RGB(line, row, B) = 0;
			}
			else
			{
				cout << "图与失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:img、src原图像必须是二值化图像
//输入:用于计算的二值化图像,原图像src,目的图像drt,图像计算区间
//输出:drt总像素
//作用:drt=src|img
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_or(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt,int linemin,int linemax,int rowmin,int rowmax)
{//***************************源地址目地址处理******************************
	if (img == nullptr || img->flag == 0)
	{
		cout << "图或失败,img图像无效" << endl;
		return -1;
	}
	if (src == nullptr || src->flag == 0)
	{
		cout << "图或失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图或失败,drt保存地址为空" << endl;
		return -1;
	}
	if (img->height != src->height || img->width != src->width)
	{
		cout << "图或失败,img和src图像大小不匹配" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (linemax > img->height)				linemax = img->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;
	if (rowmax > img->width)				rowmax = img->width;

	int num = 0;
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
		{
			if (src->get_RGB(line, row, R) == 255 || img->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 255;
				drt->get_RGB(line, row, G) = 255;
				drt->get_RGB(line, row, B) = 255;
				num++;
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 0;
				drt->get_RGB(line, row, G) = 0;
				drt->get_RGB(line, row, B) = 0;
			}
			else
			{
				cout << "图或失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:img、src原图像必须是二值化图像
//输入:用于计算的二值化图像,二值化原图像src,目的图像drt,图像计算区间
//输出:剩余像素数目
//作用:drt=!src
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_not(IMG_TYPE* src, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{
	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "图非失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图非失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->copy(*src);				//目的地址图片=原图片
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	int num = 0;
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
		{
			if (src->get_RGB(line, row, R) == 0)
			{
				drt->get_RGB(line, row, R) = 255;
				drt->get_RGB(line, row, G) = 255;
				drt->get_RGB(line, row, B) = 255;
				num++;
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 0;
				drt->get_RGB(line, row, G) = 0;
				drt->get_RGB(line, row, B) = 0;
			}
			else
			{
				cout << "图非失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:img、src原图像必须是二值化图像
//输入:用于计算的二值化图像,二值化原图像src,目的图像drt,图像计算区间
//输出:减掉的像素数目
//作用:drt=src-img
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_sub(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{
	//***************************源地址目地址处理******************************
	if (img == nullptr || img->flag == 0)
	{
		cout << "图减失败,img图像无效" << endl;
		return -1;
	}
	if (src == nullptr || src->flag == 0)
	{
		cout << "图减失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图减失败,drt保存地址为空" << endl;
		return -1;
	}
	if (img->height != src->height || img->width != src->width)
	{
		cout << "图减失败,img和src图像大小不匹配" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->copy(*src);				//目的地址图片=原图片
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (linemax > img->height)				linemax = img->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > img->width)				rowmax = img->width;

	int num = 0;
	for (int line = linemin; line < linemax; line++)
		for (int row = rowmin; row < rowmax; row++)
		{
			if (img->get_RGB(line, row, R) == 255 && src->get_RGB(line, row, R) == 255)
			{
				drt->get_RGB(line, row, R) = 0;
				drt->get_RGB(line, row, G) = 0;
				drt->get_RGB(line, row, B) = 0;
				num++;
			}
			else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
			{
			}
			else
			{
				cout << "图减失败,src不是位图" << endl;
				if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
					delete src;
				return -1;
			}
		}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//前提:img、src原图像必须是二值化图像
//输入:用于计算的二值化图像,二值化原图像src,目的图像drt,图像计算区间
//输出:drt总像素
//作用:drt=src+img
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_add(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{
	return bit_or(src, img, drt, linemin, linemax, rowmin, rowmax);
}

//前提:src原图像必须是二值化图像
//输入:二值化原图像src,目的图像drt,用于计算的模板(默认3*3),图像计算区间
//输出:-1错误,0计算完成,drt为灰度图
//作用:对二值化图像src进行t*t的积分操作,得到灰度图存入drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::bit_to_gray(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{
	int temp_size = 0;									//据中心最远步进
	if (v.size() == 1 * 1)			temp_size = 0;
	else if (v.size() == 3 * 3)		temp_size = 1;
	else if (v.size() == 5 * 5)		temp_size = 2;
	else if (v.size() == 7 * 7)		temp_size = 3;
	else if (v.size() == 9 * 9)		temp_size = 4;
	else
	{
		cout << "扩展操作只支持1*1,3*3,5*5,7*7,9*9模板" << endl;
		return -1;
	}
	auto iter = v.begin() + (v.size() - 1) / 2;			//模板中心
	int line_size = 2 * temp_size + 1;					//模板行数

	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "二值化图转灰度图失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "二值化图转灰度图失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)		//遍历图像
		for (int row = rowmin; row < rowmax; row++)
		{
			int count = 0;
			int x = LIMIT(line, linemin + temp_size, linemax - temp_size - 1);
			int y = LIMIT(row, rowmin + temp_size, rowmax - temp_size - 1);
			for (int i = -temp_size; i <= temp_size; i++)			//遍历模板
				for (int j = -temp_size; j <= temp_size; j++)
				{
					if (*(iter + (long long)i * (long long)line_size + (long long)j) == 255)			//该点在模板中有效
					{
						if (src->get_RGB(x + i, y + j, R) == 255)			//统计数量
							count++;
					}
					else if (src->get_RGB(line, row, R) == 0 || src->get_RGB(line, row, R) == 255)
						;
					else
					{
						cout << "二值化图转灰度图失败,src不是位图" << endl;
						if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
							delete src;
						return -1;
					}
				}
			drt->get_RGB(line, row, R) = count;
			drt->get_RGB(line, row, G) = count;
			drt->get_RGB(line, row, B) = count;
		}

	if (src != src_temp)		//删除缓冲
		delete src;
	return 0;
}

//输入:灰度图片src,目的图片地址drt,像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数,图像计算区间
//输出:满足条件的像素点数量
//作用:像素点满足if_fun函数,执行if_do_fun,否则执行else_do_fun,结果保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_sift(IMG_TYPE* src, IMG_TYPE* drt,
	bool(*if_fun)(uint8& src_r, uint8& src_g, uint8& src_b),
	void(*if_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
	void(*else_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
	int linemin, int linemax, int rowmin, int rowmax)
{
	if (src == nullptr || src->flag == 0)
	{
		cout << "灰度图像像素筛选失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "灰度图像像素筛选失败,drt保存地址为空" << endl;
		return -1;
	}
	if (if_fun == nullptr || if_do_fun == nullptr || else_do_fun == nullptr)
	{
		cout << "灰度图像像素筛选失败,传入函数无效" << endl;
		return -1;
	}
	return rgb_sift(src, drt, if_fun, if_do_fun, else_do_fun, linemin, linemax, rowmin, rowmax);
}

//输入:灰度图片src,目的图片地址drt,放大倍数(差分不够明显时可以增加放大倍数),图像计算区间
//输出:-1失败,0成功
//作用:对灰度图片计算边缘,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_sobel(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin, int linemax, int rowmin, int rowmax)
{
	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "灰度图边缘计算失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "灰度图边缘计算失败,drt保存地址为空" << endl;
		return -1;
	}
	//***************************源地址目地址处理******************************
	int num = rgb_sobel(src, drt, magnification, linemin, linemax, rowmin, rowmax, 1, 0, 0);

	for (int line = 0; line < drt->height; line++)
		for (int row = 0; row < drt->width; row++)
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);

	return num;
}

//输入:灰度图片src,目的图片地址drt,滤波模板,图像计算区间
//输出:-1失败,0成功
//作用:对灰度图片进行模板均值滤波(默认7*7),保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_mean(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax)
{
	int num = rgb_mean(src, drt, v, linemin, linemax, rowmin, rowmax, 1, 0, 0);

	for (int line = 0; line < drt->height; line++)
		for (int row = 0; row < drt->width; row++)
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);

	return num;
}

//输入:灰度图片src,目的图片地址drt,图像计算区间
//输出:-1失败,0成功
//作用:对灰度图片进行像素倍乘(提亮),保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_bright(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin, int linemax, int rowmin, int rowmax)
{
	int num = rgb_bright(src, drt, magnification, linemin, linemax, rowmin, rowmax, 1, 0, 0);

	for (int line = 0; line < drt->height; line++)
		for (int row = 0; row < drt->width; row++)
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);

	return num;
}

//输入:灰度图片src,目的图片地址drt,图像计算区间
//输出:-1失败,0成功
//作用:对灰度图片图像均衡化,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_equalization(IMG_TYPE* src, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{
	int num = rgb_equalization(src, drt, linemin, linemax, rowmin, rowmax, 1, 0, 0);

	for (int line = 0; line < drt->height; line++)
		for (int row = 0; row < drt->width; row++)
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);

	return num;
}

//输入:灰度图片src,图像计算区间
//输出:-1失败,返回动态阈值
//作用:对灰度图片图像求动态阈值
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_ostu(IMG_TYPE* src, int linemin, int linemax, int rowmin, int rowmax)
{
	return rgb_ostu(src, linemin, linemax, rowmin, rowmax, 1, 0, 0);
}

//输入:灰度图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,返回二值化图像像素点数量
//作用:对灰度图片图像二值化,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::gray_to_bit(IMG_TYPE* src, IMG_TYPE* drt, int threshold, int linemin, int linemax, int rowmin, int rowmax)
{
	int num = rgb_to_bit(src, drt, threshold, linemin, linemax, rowmin, rowmax, 1, 0, 0);

	for (int line = 0; line < drt->height; line++)
		for (int row = 0; row < drt->width; row++)
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);

	return num;
}

//输入:彩色图片src,目的图片地址drt,像素点所满足的条件函数,满足条件后执行的函数,不满足条件执行的函数,图像计算区间
//输出:满足条件的像素点数量
//作用:像素点满足if_fun函数,执行if_do_fun,否则执行else_do_fun,结果保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_sift(IMG_TYPE* src, IMG_TYPE* drt,
	bool(*if_fun)(uint8& src_r, uint8& src_g, uint8& src_b),
	void(*if_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
	void(*else_do_fun)(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b),
	int linemin, int linemax, int rowmin, int rowmax)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "RGB图像像素筛选失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "RGB图像像素筛选失败,drt保存地址为空" << endl;
		return -1;
	}
	if (if_fun == nullptr || if_do_fun == nullptr || else_do_fun == nullptr)
	{
		cout << "RGB图像像素筛选失败,传入函数无效" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	int num = 0;		//满足条件像素数量

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			if (if_fun(src->get_RGB(line, row, R), src->get_RGB(line, row, G), src->get_RGB(line, row, B)))
			{
				if_do_fun(src->get_RGB(line, row, R), src->get_RGB(line, row, G), src->get_RGB(line, row, B),
					drt->get_RGB(line, row, R), drt->get_RGB(line, row, G), drt->get_RGB(line, row, B));
				num++;
			}
			else
			{
				else_do_fun(src->get_RGB(line, row, R), src->get_RGB(line, row, G), src->get_RGB(line, row, B),
					drt->get_RGB(line, row, R), drt->get_RGB(line, row, G), drt->get_RGB(line, row, B));
			}
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//输入:彩色图片src,目的图片地址drt,图像计算区间
//输出:-1失败,0成功
//作用:对彩色图片src灰度化,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_to_gray(IMG_TYPE* src, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "RGB图转灰度图失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "RGB图转灰度图失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			drt->get_RGB(line, row, R) = ((int)src->get_RGB(line, row, R) + src->get_RGB(line, row, G) + src->get_RGB(line, row, B)) / 3;
			drt->get_RGB(line, row, G) = drt->get_RGB(line, row, R);
			drt->get_RGB(line, row, B) = drt->get_RGB(line, row, R);
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:彩色图片src,目的图片地址drt,放大倍数(差分不够明显时可以增加放大倍数),图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:对彩色图片计算边缘,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_sobel(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin, int linemax, int rowmin, int rowmax, int flag_r, int flag_g, int flag_b)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "RGB边缘计算失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "RGB边缘计算失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	//sobel交叉算子计算
	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			int x = LIMIT(line, linemin, linemax - 2);
			int y = LIMIT(row, rowmin, rowmax - 2);

			if (flag_r)
			{
				float r = (ABS((float)src->get_RGB(x, y, R) - src->get_RGB(x + 1, y + 1, R)) +
					ABS(src->get_RGB(x + 1, y, R) - src->get_RGB(x, y + 1, R))) * magnification / 2;
				drt->get_RGB(line, row, R) = (uint8)(LIMIT(r, 0, 255));
			}
			if (flag_g)
			{
				float g = (ABS((float)src->get_RGB(x, y, G) - src->get_RGB(x + 1, y + 1, G)) +
					ABS(src->get_RGB(x + 1, y, G) - src->get_RGB(x, y + 1, G))) * magnification / 2;
				drt->get_RGB(line, row, G) = (uint8)(LIMIT(g, 0, 255));
			}
			if (flag_b)
			{
				float b = (ABS((float)src->get_RGB(x, y, B) - src->get_RGB(x + 1, y + 1, B)) +
					ABS(src->get_RGB(x + 1, y, B) - src->get_RGB(x, y + 1, B))) * magnification / 2;
				drt->get_RGB(line, row, B) = (uint8)(LIMIT(b, 0, 255));
			}
		}
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:彩色图片src,目的图片地址drt,滤波模板,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:对彩色图片进行模板均值滤波(默认3*3),保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_mean(IMG_TYPE* src, IMG_TYPE* drt, const vector<uint8>& v, int linemin, int linemax, int rowmin, int rowmax, int flag_r, int flag_g, int flag_b)
{
	int temp_size = 0;									//据中心最远步进
	if (v.size() == 1 * 1)			temp_size = 0;
	else if (v.size() == 3 * 3)		temp_size = 1;
	else if (v.size() == 5 * 5)		temp_size = 2;
	else if (v.size() == 7 * 7)		temp_size = 3;
	else if (v.size() == 9 * 9)		temp_size = 4;
	else
	{
		cout << "均值滤波操作只支持1*1,3*3,5*5,7*7,9*9模板" << endl;
		return -1;
	}
	auto iter = v.begin() + (v.size() - 1) / 2;			//模板中心
	int line_size = 2 * temp_size + 1;					//模板行数

	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "滤波失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "滤波失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	int num = 0;										//击中数

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	int r, g, b, count;
	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			int x = LIMIT(line, linemin + temp_size, linemax - temp_size - 1);
			int y = LIMIT(row, rowmin + temp_size, rowmax - temp_size - 1);

			if (flag_r)
			{
				count = 0;
				r = 0;
				for (int i = -temp_size; i <= temp_size; i++)
					for (int j = -temp_size; j <= temp_size; j++)
						if (*(iter + (long long)i * (long long)line_size + (long long)j) == 255)
						{
							r += src->get_RGB(x + i, y + j, R);
							count++;
						}
				drt->get_RGB(line, row, R) = (uint8)(r / count);
			}
			if (flag_g)
			{
				count = 0;
				g = 0;
				for (int i = -temp_size; i <= temp_size; i++)
					for (int j = -temp_size; j <= temp_size; j++)
						if (*(iter + (long long)i * (long long)line_size + (long long)j) == 255)
						{
							g += src->get_RGB(x + i, y + j, G);
							count++;
						}
				drt->get_RGB(line, row, G) = (uint8)(g / count);
			}
			if (flag_b)
			{
				count = 0;
				b = 0;
				for (int i = -temp_size; i <= temp_size; i++)
					for (int j = -temp_size; j <= temp_size; j++)
						if (*(iter + (long long)i * (long long)line_size + (long long)j) == 255)
						{
							b += src->get_RGB(x + i, y + j, B);
							count++;
						}
				drt->get_RGB(line, row, B) = (uint8)(b / count);
			}
		}
	}
	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return num;
}

//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:对彩色图片进行像素倍乘(提亮),保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_bright(IMG_TYPE* src, IMG_TYPE* drt, float magnification, int linemin, int linemax, int rowmin, int rowmax,int flag_r,int flag_g,int flag_b)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
	
		cout << "提亮失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "提亮失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	for (int line = linemin; line < linemax; line++)
	{
		for (int row = rowmin; row < rowmax; row++)
		{
			if(flag_r)		drt->get_RGB(line, row, R) = (uint8)(LIMIT(src->get_RGB(line, row, R) * magnification, 0, 255));
			if(flag_g)		drt->get_RGB(line, row, G) = (uint8)(LIMIT(src->get_RGB(line, row, G) * magnification, 0, 255));
			if(flag_b)		drt->get_RGB(line, row, B) = (uint8)(LIMIT(src->get_RGB(line, row, B) * magnification, 0, 255));
		}
	}
	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:对彩色图片图像均衡化,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_equalization(IMG_TYPE* src, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax, int flag_r, int flag_g, int flag_b)
{//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{

		cout << "均衡化失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "均衡化失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	if (flag_r)
	{	//求直方图
		float temp[256] = { 0 };
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				temp[src->get_RGB(line, row, R)] += 1;
		//求统计概率
		int sum = src->height * src->width;
		for (int i = 0; i < 256; i++)
			temp[i] /= sum;

		//求累加概率
		for (int i = 1; i < 256; i++)
			temp[i] += temp[i - 1];

		//求新灰度级
		for (int i = 1; i < 256; i++)
			temp[i] = (float)((int)(temp[i] * 255));

		//用新灰度级替换原来图像中像素灰度级
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, R) = (uint8)temp[(int)(src->get_RGB(line, row, R))];
	}

	if (flag_g)
	{	//求直方图
		float temp[256] = { 0 };
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				temp[src->get_RGB(line, row, G)] += 1;
		//求统计概率
		int sum = src->height * src->width;
		for (int i = 0; i < 256; i++)
			temp[i] /= sum;

		//求累加概率
		for (int i = 1; i < 256; i++)
			temp[i] += temp[i - 1];

		//求新灰度级
		for (int i = 1; i < 256; i++)
			temp[i] = (float)((int)(temp[i] * 255));

		//用新灰度级替换原来图像中像素灰度级
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, G) = (uint8)temp[(int)(src->get_RGB(line, row, G))];
	}

	if (flag_b)
	{	//求直方图
		float temp[256] = { 0 };
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				temp[src->get_RGB(line, row, B)] += 1;
		//求统计概率
		int sum = src->height * src->width;
		for (int i = 0; i < 256; i++)
			temp[i] /= sum;

		//求累加概率
		for (int i = 1; i < 256; i++)
			temp[i] += temp[i - 1];

		//求新灰度级
		for (int i = 1; i < 256; i++)
			temp[i] = (float)((int)(temp[i] * 255));

		//用新灰度级替换原来图像中像素灰度级
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, B) = (uint8)temp[(int)(src->get_RGB(line, row, B))];
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:彩色图片src,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,返回动态阈值
//作用:对彩色图片图像求动态阈值
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_ostu(IMG_TYPE* src, int linemin, int linemax, int rowmin, int rowmax, int flag_r, int flag_g, int flag_b)
{
	#define GrayScale   256     //灰度级
	#define K           8       //步进每K*K个点取一个样点
	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "动态阈值计算失败,src图像无效" << endl;
		return -1;
	}
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	int pixelCount[GrayScale] = { 0 };      //每个灰度级点数
	int pixelSum = 0;			//总点数
	int gray_sum = 0;			//总灰度数
	uint8 threshold = 0;		//阈值

	if (flag_r)
	{	//求直方图
		for (int line = linemin; line < linemax; line += K)
			for (int row = rowmin; row < rowmax; row += K)
			{
				pixelCount[src->get_RGB(line, row, R)]++;  //将当前的点的像素值作为计数数组的下标
				gray_sum += src->get_RGB(line, row, R);       //灰度值总和
				pixelSum++;
			}
	}

	if (flag_g)
	{	//求直方图
		for (int line = linemin; line < linemax; line += K)
			for (int row = rowmin; row < rowmax; row += K)
			{
				pixelCount[src->get_RGB(line, row, G)]++;  //将当前的点的像素值作为计数数组的下标
				gray_sum += src->get_RGB(line, row, G);       //灰度值总和
				pixelSum++;
			}
	}

	if (flag_b)
	{	//求直方图
		for (int line = linemin; line < linemax; line += K)
			for (int row = rowmin; row < rowmax; row += K)
			{
				pixelCount[src->get_RGB(line, row, B)]++;  //将当前的点的像素值作为计数数组的下标
				gray_sum += src->get_RGB(line, row, B);       //灰度值总和
				pixelSum++;
			}
	}

	float u0Sum = 0;            //前景总灰度级0
	float N0 = 0;               //前景总点数0
	float w0 = 0;               //前景比例0
	float u0 = 0;               //前景平均灰度0

	float u1Sum = (float)gray_sum;     //背景总灰度级
	float N1 = (float)pixelSum;        //背景总点数
	float w1 = 1;               //背景比例
	float u1 = u1Sum / N1;		//背景平均灰度

	double g = 0;				//类间方差
	double gMax = 0;			//类间方差最大值

	//遍历灰度级[0,255]找最大类间方差
	for (int i = 0; i < GrayScale; i++)
	{
		u0Sum += i * pixelCount[i]; //前景总灰度
		N0 += pixelCount[i];		//前景总点数
		w0 = N0 / pixelSum;         //前景比例
		if (N0 != 0)				//前景平均灰度
			u0 = u0Sum / N0;
		else u0 = 0;

		u1Sum -= i * pixelCount[i]; //背景总灰度
		N1 -= pixelCount[i];		//背景总点数
		w1 = N1 / pixelSum;         //背景比例
		if (N1 != 0)				//背景平均灰度
			u1 = u1Sum / N1;
		else u1 = 0;

		g = (double)w0 * w1 * ((double)u0 - u1) * ((double)u0 - u1);

		if (g > gMax)
		{
			gMax = g;//最大类间方差
			threshold = i;
		}
	}

	return threshold;

	#undef GrayScale
	#undef K
}

//输入:彩色图片src,目的图片地址drt,图像计算区间,操作的颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:对彩色图片图像二值化,保存到drt
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::rgb_to_bit(IMG_TYPE* src, IMG_TYPE* drt, int threshold, int linemin, int linemax, int rowmin, int rowmax, int flag_r, int flag_g, int flag_b)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "二值化失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "二值化失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;

	int num = 0;
	if (flag_r)
	{	
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				if (src->get_RGB(line, row, R) > threshold)
				{
					drt->get_RGB(line, row, R) = 255;
					num++;
				}
				else
					drt->get_RGB(line, row, R) = 0;
	}

	if (flag_g)
	{
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				if (src->get_RGB(line, row, G) > threshold)
				{
					drt->get_RGB(line, row, G) = 255;
					num++;
				}
				else
					drt->get_RGB(line, row, G) = 0;
	}

	if (flag_b)
	{
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				if (src->get_RGB(line, row, B) > threshold)
				{
					drt->get_RGB(line, row, B) = 255;
					num++;
				}
				else
					drt->get_RGB(line, row, B) = 0;
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:图片src,图片img,目的图片地址drt,图像计算区间,选择颜色(0未选中,1选中)
//输出:-1失败,0成功
//作用:像素值叠加,src+img=drt,一般用于图像标注
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::img_add(IMG_TYPE* src, IMG_TYPE* img, IMG_TYPE* drt, int linemin, int linemax, int rowmin, int rowmax,
	int flag_r, int flag_g, int flag_b)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "RGB图加失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "RGB图加失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->set_size(src->height, src->width);				//目的地址图片清空
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > src->height)				linemax = src->height;
	if (linemax > img->height)				linemax = img->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > src->width)				rowmax = src->width;
	if (rowmax > img->width)				rowmax = img->width;

	if (flag_r)
	{
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, R) = (uint8)(LIMIT((int)src->get_RGB(line, row, R) + img->get_RGB(line, row, R),0,255));
	}

	if (flag_g)
	{
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, G) = (uint8)(LIMIT((int)src->get_RGB(line, row, G) + img->get_RGB(line, row, G),0,255));
	}

	if (flag_b)
	{
		for (int line = linemin; line < linemax; line++)
			for (int row = rowmin; row < rowmax; row++)
				drt->get_RGB(line, row, B) = (uint8)(LIMIT((int)src->get_RGB(line, row, B) + img->get_RGB(line, row, B),0,255));
	}

	if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
		delete src;
	return 0;
}

//输入:rgb图像,二值化种子图像src,目的图像drt,相邻连续像素差值,图像计算区间
//输出:生长扩张出的像素点数量
//作用:用src图像像素作为种子,在rgb图像上用种子8邻域生长(生长条件:rgb像素和种子像素差值在3以内)
//作者:陈亮
//日期:2022.9.18
//注意:一般用于对颜色提取的扩张,由于颜色提取要求苛刻,很多像素不能满足,通过生长补全该颜色提取
int IMG_TYPE::img_grow(IMG_TYPE* rgb, IMG_TYPE* src, IMG_TYPE* drt, int pixel_sub, int linemin, int linemax, int rowmin, int rowmax)
{	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "图像生长失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图像生长失败,drt保存地址为空" << endl;
		return -1;
	}
	IMG_TYPE* src_temp = src;
	if (src == drt)		//源地址目地址同源时,加缓冲
	{
		src = nullptr;
		src = new IMG_TYPE(*src_temp);
	}
	drt->copy(*src);	//生长操作直接在原图像上操作,目的图像=原图像
	//***************************源地址目地址处理******************************

	if (linemin < 0)						linemin = 0;
	if (linemax > drt->height)				linemax = drt->height;
	if (linemax > rgb->height)				linemax = rgb->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > drt->width)				rowmax = drt->width;
	if (rowmax > rgb->width)				rowmax = rgb->width;

	int num = 0;		//满足条件像素数量
	while (1)
	{
		int count = 0;	//当次连通域生长像素点数量

		for (int line = linemin; line < linemax; line++)
		{
			for (int row = rowmin; row < rowmax; row++)
			{
				int x = LIMIT(line, linemin + 1, linemax - 2);
				int y = LIMIT(row, rowmin + 1, rowmax - 2);
				if (drt->get_RGB(line, row, R) == 255)		//如果该点初步为细胞像素
				{//判断邻域是否是相同像素
					for (int i = -1; i < 2; i++)
						for (int j = -1; j < 2; j++)
						{
							if (drt->get_RGB(x + i, y + j, R) == 0	//邻域像素在表中不存在,且边缘像素点RGB连续
								&& (ABS(rgb->get_RGB(x + i, y + j, R) - rgb->get_RGB(x, y, R)) < pixel_sub
									&& ABS(rgb->get_RGB(x + i, y + j, G) - rgb->get_RGB(x, y, G)) < pixel_sub
									&& ABS(rgb->get_RGB(x + i, y + j, B) - rgb->get_RGB(x, y, B)) < pixel_sub)
								)
							{//则生长一个像素
								drt->get_RGB(x + i, y + j, R) = 255;
								drt->get_RGB(x + i, y + j, G) = 255;
								drt->get_RGB(x + i, y + j, B) = 255;
								count++;
								num++;
							}
						}
				}
				else if (drt->get_RGB(line, row, R) == 0 || drt->get_RGB(line, row, R) == 255)
					;
				else
				{
					cout << "图像生长失败,src不是位图" << endl;
					if (src != src_temp)		//如果当前源地址是新建的缓冲,则删除
						delete src;
					return -1;
				}
			}
		}
		if (0 == count)		//单次生长像素点数量为0
			break;
	}
	return num;
}

//前提:src原图像必须是二值化图像
//输入:存储连通域的容器v,二值化原图像src,伪彩色图像保存地址
//输出:连通域容器里面连通域个数
//作用:对src求连通域,存入v中,同时对src伪彩色处理保存到drt中,最大支持65535个连通域
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::img_to_connected(vector<AREA>& v, IMG_TYPE* src, IMG_TYPE* drt,int linemin,int linemax,int rowmin,int rowmax)
{
//line,row点处像素
#define BIT(line,row)	(drt->get_RGB((line),(row),R))	

//line,row点处连通域值
#define CON(line,row)	(*(uint16*)(&drt->get_RGB((line), (row), B)))	

//line,row新建一个连通域
#define NEW(line,row)	v.push_back(AREA(MPOINT(line,row)));

//压一个点到连通域c中
#define ADD(c,line,row)	(v[(c)-1].a.push_back(MPOINT(line,row)))	

//c2移到c1中(包括复制c2到c1,清空c2,回收c2)
#define MOVE(c1,c2)																			\
{																							\
	v[(c1) - 1].a.insert(v[(c1) - 1].a.end(), v[(c2) - 1].a.begin(), v[(c2) - 1].a.end());	\
	for (auto iter = v[(c2)-1].a.begin(); iter != v[(c2)-1].a.end(); iter++)				\
		CON(iter->line, iter->row) = (c1);													\
	v[(c2)-1].a.clear();																	\
	c_unused.push_back(c2);																	\
}		
	//***************************源地址目地址处理******************************
	if (src == nullptr || src->flag == 0)
	{
		cout << "图像求连通域失败,src图像无效" << endl;
		return -1;
	}
	if (drt == nullptr)
	{
		cout << "图像求连通域失败,drt保存地址为空" << endl;
		return -1;
	}
	drt->copy(*src);						//直接操作原图像,目的图像=原图像
	//***************************源地址目地址处理******************************

	uint16 num = 0;				//连通域数量
	int line = 0, row = 0;
	uint16 c_num, c1, c2, c3;	//连通域
	vector<uint16> c_unused;	//合并过后没有使用的连通域下标集合

	//遍历之前确定图像有效区间
	if (linemin < 0)						linemin = 0;
	if (linemax > drt->height)				linemax = drt->height;
	if (rowmin < 0)							rowmin = 0;
	if (rowmax > drt->width)				rowmax = drt->width;

	//连通域下标清空
	for (line = linemin; line < linemax; line++)
		for (row = rowmin; row < rowmax; row++)
			CON(line, row) = 0;
	
	//找第一个点,计入第一个连通域
	for (line = linemin; num==0&&line < linemax; line++)
		for (row = rowmin; num == 0 && row < rowmax; row++)
		{
			if (BIT(line, row) == 255)		//如果该点有值
			{
				CON(line, row) = ++num;
				NEW(line, row);
			}
			else if (BIT(line, row) == 255 || BIT(line, row) == 0)
				;
			else
			{
				cout << "图像求连通域失败,src不是位图" << endl;
				return -1;
			}
		}

	//遍历该行,计入连通域数量
	for (row = row + 1; row < rowmax; row++)
	{
		if (drt->get_RGB(line, row, R) == 255)		//该点有值
		{
			c1 = CON(line, row - 1);
			if ( c1 == 0)//该点前没有连通域,就存入新连通域
			{
				CON(line, row) = ++num;
				NEW(line, row);
			}
			else					//该点前有连通域,则该连通域存入新坐标
			{
				CON(line, row) = c1;
				ADD(c1, line, row);
			}
		}
		else if (BIT(line, row) == 255 || BIT(line, row) == 0)
			;
		else
		{
			cout << "图像求连通域失败,src不是位图" << endl;
			return -1;
		}
	}

	//遍历其他行,计入连通域
	for (line++; line < linemax; line++)
	{
		for (row = rowmin; row < rowmax; row++)
		{
			if (BIT(line, row) == 255)		//如果该点有值
			{
				c_num = 0;
				c1 = CON(line - 1, LIMIT(row - 1, rowmin, rowmax - 1));
				c2 = CON(line - 1, row - 0);
				c3 = CON(line - 1, LIMIT(row + 1, rowmin, rowmax - 1));
				if (c3 != 0)
					if (c3 == c2 || c3 == c1)			c3 = 0;
					else								c_num++;
				if (c2 != 0)
					if (c2 == c3 || c2 == c1)			c2 = 0;
					else								c_num++;
				if (c1 != 0)
					if (c1 == c3 || c1 == c2)			c1 = 0;
					else								c_num++;

				if (c_num == 0)	//没有连通域
				{
					if (c_unused.size() == 0)		//没有回收连通域就新建
					{
						CON(line, row) = ++num;
						NEW(line, row);
					}
					else							//有回收连通域就使用回收的
					{
						CON(line, row) = c_unused.back();	//使用回收连通域
						ADD(c_unused.back(), line, row);	//添加点
						c_unused.pop_back();				//从回收站放出该连通域
					}
				}
				else if (c_num == 1)
				{
					if (c1 != 0)	//只有左上连通
					{
						CON(line, row) = c1;
						ADD(c1, line, row);
					}
					else if (c2 != 0)	//只有中间连通
					{
						CON(line, row) = c2;
						ADD(c2, line, row);
					}
					else	//只有右上连通
					{
						CON(line, row) = c3;
						ADD(c3, line, row);
					}
				}
				else if (c_num == 2)
				{
					if (c1 == 0)	//只有左上不连通
					{
						CON(line, row) = c2;
						ADD(c2, line, row);								//添加该点
						MOVE(c2, c3);									//将c3移到c2
					}
					else if (c2 == 0)	//只有中间不连通
					{
						CON(line, row) = c1;
						ADD(c1, line, row);								//添加该点
						MOVE(c1, c3);									//将c3移到c1
					}
					else if (c3 == 0)	//只有右上不连通
					{
						CON(line, row) = c1;
						ADD(c1, line, row);								//添加该点
						MOVE(c1, c2);									//将c2移到c1
					}
				}
				else		//三个全通
				{
					CON(line, row) = c1;
					ADD(c1, line, row);								//添加该点
					MOVE(c1, c2);									//将c3移到c2
					MOVE(c1, c3);									//将c3移到c1
				}
			}
			else if (BIT(line, row) == 255 || BIT(line, row) == 0)
				;
			else
			{
				cout << "图像求连通域失败,src不是位图" << endl;
				return -1;
			}
		}
	}

	int i = 0;
	while (i != v.size())
	{
		v[i].get_area();		//求连通域的linemin、linemax、rowmin、rowmax范围
		i++;
	}

	return (int)v.size();
}

//输入:连通域a,像素值rgb
//输出:连通域内像素点数量
//作用:将连通域转为固定颜色图像,颜色默认为白色(不清空原图片,直接在原图片上显示本连通域)
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::connected_to_img(AREA& a, IMG_TYPE* drt, uint8 r, uint8 g, uint8 b)
{	//***************************源地址目地址处理******************************
	if (drt == nullptr)
	{
		cout << "图像求连通域失败,drt保存地址为空" << endl;
		return -1;
	}
	else if (drt->flag)	//如果原来有图像
	{
		if (drt->height <= a.linemax && drt->width <= a.rowmax)
		{//并且目的地址图片宽度高度小于连通域
			IMG_TYPE* drt_temp = new IMG_TYPE(*drt);				//复制该图片
			drt->set_size(a.linemax + 1, a.rowmax + 1);				//创建大图片
			for (int line = 0; line < drt_temp->height; line++)		//拷贝复印件
				for (int row = 0; row < drt_temp->width; row++)
				{
					drt->get_RGB(line, row, R) = drt_temp->get_RGB(line, row, R);
					drt->get_RGB(line, row, G) = drt_temp->get_RGB(line, row, G);
					drt->get_RGB(line, row, B) = drt_temp->get_RGB(line, row, B);
				}
			delete drt_temp;										//删除复印件
		}
		else if (drt->height <= a.linemax)
		{//目的地址图片高度小于连通域
			IMG_TYPE* drt_temp = new IMG_TYPE(*drt);				//复制该图片
			drt->set_size(a.linemax + 1, drt->width);				//创建大图片
			for (int line = 0; line < drt_temp->height; line++)		//拷贝复印件
				for (int row = 0; row < drt_temp->width; row++)
				{
					drt->get_RGB(line, row, R) = drt_temp->get_RGB(line, row, R);
					drt->get_RGB(line, row, G) = drt_temp->get_RGB(line, row, G);
					drt->get_RGB(line, row, B) = drt_temp->get_RGB(line, row, B);
				}
			delete drt_temp;										//删除复印件
		}
		else if (drt->width <= a.rowmax)
		{//目的地址图片宽度小于连通域
			IMG_TYPE* drt_temp = new IMG_TYPE(*drt);				//复制该图片
			drt->set_size(drt->height, a.rowmax + 1);				//创建大图片
			for (int line = 0; line < drt_temp->height; line++)		//拷贝复印件
				for (int row = 0; row < drt_temp->width; row++)
				{
					drt->get_RGB(line, row, R) = drt_temp->get_RGB(line, row, R);
					drt->get_RGB(line, row, G) = drt_temp->get_RGB(line, row, G);
					drt->get_RGB(line, row, B) = drt_temp->get_RGB(line, row, B);
				}
			delete drt_temp;										//删除复印件
		}
	}
	else
	{//原本没有图像,就直接新建
		drt->set_size(a.linemax + 10, a.rowmax + 10);
	}
	//***************************源地址目地址处理******************************

	for (auto iter = a.a.begin(); iter != a.a.end(); iter++)
	{
		if (iter->line < drt->height && iter->row < drt->width)
		{
			drt->get_RGB(iter->line, iter->row, R) = r;
			drt->get_RGB(iter->line, iter->row, G) = g;
			drt->get_RGB(iter->line, iter->row, B) = b;
		}
		else
		{
			cout << "转换失败,连通域超出图像,连通域范围linemin,linemax,rowmin,rowmax与实际连通域大小不匹配" << endl;
			return -1;
		}
	}
	return (int)a.a.size();
}

//输入:连通域容器v,像素值rgb(默认是伪彩色,如果设置颜色,则所有连通域会显示该颜色)
//输出:总连通域内像素点数量
//作用:将连通域转为图片(先将目的图片清空),默认转为伪彩色,可以修改为固定颜色
//作者:陈亮
//日期:2022.9.18
int IMG_TYPE::connected_to_img(vector<AREA>& v, IMG_TYPE* drt, uint8 r, uint8 g, uint8 b)
{
	int linemax = 0; int rowmax = 0;
	for (auto iter = v.begin(); iter != v.end(); iter++)
	{
		if (iter->linemax > linemax)		linemax = iter->linemax;
		if (iter->rowmax > rowmax)			rowmax = iter->rowmax;
	}

	drt->set_size(linemax+10, rowmax+10);

	int num = 0;
	if (r == g && g == b && b == 1)
		for (int i = 0; i != v.size(); i++)
			num += connected_to_img(v[i], drt, (i % 10) * 25, ((i / 10) % 10) * 25, ((i / 100) % 10) * 25);
	else
		for (int i = 0; i != v.size(); i++)
			num += connected_to_img(v[i], drt, r, g, b);
	return num;
}

//输入:连通域a,连通域所在图片在屏幕上的位置xy,连通域a显示的颜色rgb(默认黑色)
//输出:无
//作用:显示连通域(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)(默认黑色,可设置)
//作者:陈亮
//日期:2022.9.18
void show(AREA& a, int x, int y, uint8 r, uint8 g, uint8 b)
{
	HDC hdc = GetDC(GetForegroundWindow());
	for (auto iter = a.a.begin(); iter != a.a.end(); iter++)
		SetPixel(hdc, iter->row + x, iter->line + y, RGB(r, g, b));
}

//输入:连通域容器v,连通域所在图片在屏幕上的位置xy,显示的颜色rgb(默认伪彩色,可设置为固定颜色)
//输出:无
//作用:显示连通域(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)(默认伪彩色,可设置为固定颜色)
//作者:陈亮
//日期:2022.9.18
void show(vector<AREA>& v, int x, int y, uint8 r, uint8 g, uint8 b)
{
	if (r == 1 && g == 1 && b == 1)
	{//默认彩色
		for (int i = 0; i != v.size(); i++)
			show(v[i], x, y, (i % 10) * 25, ((i / 10) % 10) * 25, ((i / 100) % 10) * 25);
	}
	else	//指定颜色
	{
		for (auto iter = v.begin(); iter != v.end(); iter++)
			show(*iter, x, y, r, g, b);
	}
}

//输入:一幅彩色RGB细胞图片,目的图片,一个存储细胞连通域的容器
//输出:-1失败,0成功
//作用:计算图片src中细胞位置,对原图像标注后保存到目的图片,将细胞连通域存入连通域容器中
//作者:陈亮
//日期:2022.9.18
int cells_sift(vector<AREA>& v, vector<AREA>& cells)
{	
#define K	1.5		//定义单个细胞的最大长宽比例
	while (v.size() != 0)
	{//直到所有连通域都计算完毕
		auto c = v.back();	//取一个连通域
		v.pop_back();

		if (c.linemax - c.linemin < 15 && c.rowmax - c.rowmin < 15)
		{//正好是细胞大小	10*10<  连通域c  <20*20
			cells.push_back(c);
		}
		else//连通域太大,需要收缩
		{
			//连通域转图像
			IMG_TYPE* img = new IMG_TYPE;
			IMG_TYPE::connected_to_img(c, img);

			vector<uint8> temp;
			if (((double)c.linemax - c.linemin) >=K * ((double)c.rowmax - c.rowmin))
				//高度远大于宽度,横向收缩
				temp = { 1,1,1,255,255,255,1,1,1 };
			else if (K * ((double)c.linemax - c.linemin) <= ((double)c.rowmax - c.rowmin))
				//宽度远大于高度,纵向收缩
				temp = { 1,255,1,1,255,1,1,255,1 };
			else
			{
				c.get_intercept();			//求斜线范围
				if (((double)c.leftup - c.rightdown) >= ((double)c.rightup - c.leftdown))
					//图片是一条从左上到右下的线,斜向收缩
					temp = { 255,1,1,1,255,1,1,1,255 };
				else if (((double)c.leftup - c.rightdown) <= ((double)c.rightup - c.leftdown))
					//图片是一条从右上到左下的线,斜向收缩
					temp = { 1,1,255,1,255,1,255,1,1 };
				//else
				//	//宽度高度一样,全向收缩
				//	temp = { 1,255,1,255,255,255,1,255,1 };
			}

			//收缩
			IMG_TYPE::bit_shrink(img, img, temp, c.linemin - 10, c.linemax + 10, c.rowmin - 10, c.rowmax + 10);
			//图像转连通域	存入总连通域容器
			vector<AREA> v_temp;
			IMG_TYPE::img_to_connected(v_temp, img, img);
			v.insert(v.end(), v_temp.begin(), v_temp.end());
			//删除缓存
			delete img;
			v_temp.clear();
		}
	}
	return 0;
}

这就是自己纯手写的数字图像处理库函数,全都是基于像素点级别的操作,个人感觉还是比较好理解的,程序中关键位置基本都有注释,函数注释也比较标准,可供大家学习与使用,转载记得注明出处,下面提供使用例程,也就是main.c例程

mian.c

/********************************************************************************
* 文件名称:main.cpp
* 功    能:函数使用实例,细胞识别方案
* 作    者:陈亮
* 版    本:version1
* 日    期:2022.9.24
* 版权所属:陈亮,侵权必究
********************************************************************************/

/*****************************************图像基本操作例程******************************************
#include <iostream>
#include "image.h"
#ifndef STD
#define STD
using namespace std;
#endif

int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	//打开彩色图到图片0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//打开彩色图片到图片1,之后关闭图片1
	image[1].open("Blood.bmp");
	image[1].close();
	cout << "	//打开彩色图片到图片1,之后关闭图片1" << endl;

	//打开彩色图片到图片2,之后清空图片2像素
	image[2].open("Blood.bmp");
	image[2].clear();
	cout << "	//打开彩色图片到图片2,之后清空图片2像素" << endl;

	///拷贝图片0到图片3
	image[3].copy(image[0]);
	cout << "	///拷贝图片0到图片3" << endl;

	//设置图片4宽度高度,同时对图像像素初始化rgb=100
	image[4].set_size(80, 80, 100, 100, 100);
	cout << "	//设置图片4宽度高度,同时对图像像素初始化rgb=100" << endl;

	//保存图片4到程序目录下
	image[4].save("test.bmp");
	cout << "	//保存图片4到程序目录下" << endl;

	//拷贝图片0到图片5,修改图片1-50行,1-50列像素点rgb=0
	image[5].copy(image[0]);
	for (int line = 0; line < 50; line++)
		for (int row = 0; row < 50; row++)
		{
			image[5].get_RGB(line, row, R) = 0;
			image[5].get_RGB(line, row, G) = 0;
			image[5].get_RGB(line, row, B) = 0;
		}
	cout << "	//拷贝图片0到图片5,修改图片1-50行,1-50列像素点rgb=0" << endl;

	//拷贝图片0的上半部分到图片6
	image[6].copy(image[0], 0, image[0].height / 2);
	cout << "	//拷贝图片0的上半部分到图片6" << endl;

	//以屏幕x=800,y=80,为图片起点,显示图片0第0-99行,第350-399列像素点
	for (int line = 0; line < 100; line++)
		for (int row = 350; row < 400; row++)
			image[0].show_point(line, row, 800, 80);
	cout << "	//以屏幕x=800,y=80,为图片起点,显示图片0第0-99行,第350-399列像素点" << endl;

	//以屏幕x=800,y=80,为图片起点,显示图片0的0-49行,550-649列像素点
	image[0].show(800, 80, 0, 50, 550, 650);
	cout << "	//以屏幕x=800,y=80,为图片起点,显示图片0的0-49行,550-649列像素点" << endl;

	//统计图片0中,0-99行,0-99列,非0像素点个数
	int num = image[0].count(0, 99, 0, 99);
	cout << "	//统计图片0中,0-99行,0-99列,非0像素点个数: " << num << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();

	return 0;
}

/*****************************************图像基本操作例程******************************************/

/*****************************************bit图相关操作例程*****************************************
#include <iostream>
#include "image.h"
#ifndef STD
#define STD
using namespace std;
#endif

//提取紫色计算公式
bool is_purple(uint8& src_r, uint8& src_g, uint8& src_b)
{
	if ((int)src_r + src_b - 2 * src_g > 100)		//红色加蓝色=紫色
		return 1;
	else
		return 0;
}

//满足条件处理成白色
void is_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 255;
}

//不满足条件处理成黑色
void else_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 0;
}

int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	//打开彩色图到图0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//rgb图0转为bit图到图1
	IMG_TYPE::rgb_sift(&image[0], &image[1], is_purple, is_do_bit, else_do_bit);
	cout << "	//rgb图0转为bit图到图1" << endl;

	//对图1上半部分击中保留到图2
	IMG_TYPE::bit_hit(&image[1], &image[2], vector<uint8>(9, 255), 0, image[1].height / 2);
	cout << "	//对图1上半部分击中保留到图2" << endl;

	//对图1下半部分击中保留到图3
	IMG_TYPE::bit_hit(&image[1], &image[3], vector<uint8>(9, 255), image[1].height / 2);
	cout << "	//对图1下半部分击中保留到图3" << endl;

	//对图1击中删除到图4
	IMG_TYPE::bit_nhit(&image[1], &image[4]);
	cout << "	//对图1击中删除到图4" << endl;

	//图2&&图3=图5
	IMG_TYPE::bit_and(&image[2], &image[3], &image[5]);
	cout << "	//图2&&图3=图5" << endl;

	//图2||图3=图6
	IMG_TYPE::bit_or(&image[2], &image[3], &image[6]);
	cout << "	//图2||图3=图6" << endl;

	//图2+图3=图7
	IMG_TYPE::bit_add(&image[2], &image[3], &image[7]);
	cout << "	//图2+图3=图7" << endl;

	//图1扩展操作保存到图8
	IMG_TYPE::bit_extend(&image[1], &image[8]);
	cout << "	//图1扩展操作保存到图8" << endl;

	//图1收缩操作保存到图9
	IMG_TYPE::bit_shrink(&image[1], &image[9]);
	cout << "	//图1收缩操作保存到图9" << endl;

	//图1取边缘操作保存到图10
	IMG_TYPE::bit_edge(&image[1], &image[10]);
	cout << "	//图1取边缘操作保存到图10" << endl;

	//图11=!图1
	IMG_TYPE::bit_not(&image[1], &image[11]);
	cout << "	//图11=!图1" << endl;

	//图1-图5=图12
	IMG_TYPE::bit_sub(&image[1], &image[5],&image[12]);
	cout << "	//图1-图5=图12" << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();

	return 0;
}
/*****************************************bit图相关操作例程******************************************/

/*****************************************灰度图相关操作例程*****************************************
#include <iostream>
#include "image.h"
#ifndef STD
#define STD
using namespace std;
#endif

//线性亮度增强
bool is_light(uint8& src_r, uint8& src_g, uint8& src_b)
{
	if (src_r > 128)		//高亮区域
		return 1;
	else
		return 0;
}

//满足条件处理成白色
void is_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = (uint8)(src_r * 1.5);		//高亮区域变亮
}

//不满足条件处理成黑色
void else_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = (uint8)(src_r / 1.5);		//低亮区域变暗
}

int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	//打开彩色图到图0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//彩色图片0转灰度图保存到图片1
	IMG_TYPE::rgb_to_gray(&image[0], &image[1]);
	cout << "	//彩色图片0转灰度图保存到图片1" << endl;

	//图片1灰度值线性化,高亮区增强,低亮区减弱,保存到图片2
	IMG_TYPE::gray_sift(&image[1], &image[2], is_light, is_do_bit, else_do_bit);
	cout << "	//图片1灰度值线性化,高亮区增强,低亮区减弱,保存到图片2" << endl;

	//图片1sobel计算边缘,5倍增强后保存到图片3
	IMG_TYPE::gray_sobel(&image[1], &image[3], 5);
	cout << "	//图片1sobel计算边缘,5倍增强后保存到图片3" << endl;

	//图片1模板均值滤波(默认7*7),保存到图片4
	IMG_TYPE::gray_mean(&image[1], &image[4]);
	cout << "	//图片1模板均值滤波(默认7*7),保存到图片4" << endl;

	//图片1全局像素亮度减0.5,保存到图片5
	IMG_TYPE::gray_bright(&image[1], &image[5], 0.5);
	cout << "	//图片1全局像素亮度减0.5,保存到图片5" << endl;

	//图片1直方图均衡化,保存到图片6
	IMG_TYPE::gray_equalization(&image[1], &image[6]);
	cout << "	//图片1直方图均衡化,保存到图片6" << endl;

	//图片1求最佳阈值
	int threshold = IMG_TYPE::gray_ostu(&image[1]);
	cout << "	//图片1求最佳阈值" << threshold << endl;

	//图片1二值化,保存到图片7
	IMG_TYPE::gray_to_bit(&image[1], &image[7], threshold);
	cout << "	//图片1二值化,保存到图片7" << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();

	return 0;
}
/*****************************************灰度图相关操作例程*****************************************/

/*****************************************RGB图相关操作例程******************************************
#include <iostream>
#include "image.h"
#ifndef STD
#define STD
using namespace std;
#endif

//提取紫色计算公式
bool is_purple(uint8& src_r, uint8& src_g, uint8& src_b)
{
	if ((int)src_r + src_b - 2 * src_g > 100)		//红色加蓝色=紫色
		return 1;
	else
		return 0;
}

//边缘提取计算公式
bool is_edge(uint8& r, uint8& g, uint8& b)
{
	if (r + g + b > 160)		//像素值较大
		return 1;
	else
		return 0;
}

//满足条件处理成白色
void is_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 255;
}

//不满足条件处理成黑色
void else_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 0;
}

//满足条件保留彩色
void is_do_rgb(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = src_r; drt_g = src_g; drt_b = src_b;
}

int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	//打开彩色图到图0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//图片0提取紫色生成二值化图片保存到图片1
	IMG_TYPE::rgb_sift(&image[0], &image[1], is_purple, is_do_bit, else_do_bit);
	cout << "	//图片0提取紫色生成二值化图片保存到图片1" << endl;

	//图片0提取紫色像素保存到图片2
	IMG_TYPE::rgb_sift(&image[0], &image[2], is_purple, is_do_rgb, else_do_bit);
	cout << "	//图片0提取紫色像素保存到图片2" << endl;

	//图片0灰度化保存到图片3
	IMG_TYPE::rgb_to_gray(&image[0], &image[3]);
	cout << "	//图片0灰度化保存到图片3" << endl;

	//图片0用sobel算子计算边缘,提亮5倍后保存到图片4
	IMG_TYPE::rgb_sobel(&image[0], &image[4], 5);
	cout << "	//图片0用sobel算子计算边缘,提亮5倍后保存到图片4" << endl;

	//图片0均值滤波后保存到图片5
	IMG_TYPE::rgb_mean(&image[0], &image[5]);
	cout << "	//图片0均值滤波后保存到图片5" << endl;

	//图片0降低亮度到原来0.5倍保存到图片6
	IMG_TYPE::rgb_bright(&image[0], &image[6], 0.5);
	cout << "	//图片0降低亮度到原来0.5倍保存到图片6" << endl;

	//图片4二值化为边缘图,保存到图片7
	IMG_TYPE::rgb_sift(&image[4], &image[7], is_edge, is_do_bit, else_do_bit);
	cout << "	//图片4二值化为边缘图,保存到图片7" << endl;

	//图片0均衡化保存到图片8
	IMG_TYPE::rgb_equalization(&image[0], &image[8]);
	cout << "	//图片0均衡化保存到图片8" << endl;

	//图片0自动阈值计算(RGB可选择,默认3者同时求阈值)
	int threshold = IMG_TYPE::rgb_ostu(&image[0]);
	cout << "	//图片0自动阈值计算" << threshold << endl;

	//用threshold对图像0二值化,保存到图像9(RGB可选择,默认3者同时二值化,8种颜色)
	IMG_TYPE::rgb_to_bit(&image[0], &image[9], threshold);
	cout << "	//用threshold对图像0二值化,保存到图像9" << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();

	return 0;
}

/*****************************************RGB图相关操作例程******************************************/

/*****************************************其他图片相关操作例程***************************************
#include <iostream>
#include "image.h"

#ifndef STD
#define STD
using namespace std;
#endif

//提取紫色计算公式
bool is_purple(uint8& src_r, uint8& src_g, uint8& src_b)
{
	if ((int)src_r + src_b - 2 * src_g > 100)		//红色加蓝色=紫色
		return 1;
	else
		return 0;
}

//满足条件处理成白色
void is_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 255;
}

//不满足条件处理成黑色
void else_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 0;
}

int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	int num = 0;//像素数量
	//打开彩色图到图0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//图片0提取紫色生成二值化图片保存到图片1
	num = IMG_TYPE::rgb_sift(&image[0], &image[1], is_purple, is_do_bit, else_do_bit);
	cout << "	//图片0提取紫色生成二值化图片保存到图片1" << endl;

	//图片0叠加图片1,实现标注,保存到图片2
	IMG_TYPE::img_add(&image[0], &image[1], &image[2]);
	cout << "	//图片0叠加图片1,实现标注,保存到图片2" << endl;

	//图片1作为种子,在rgb图片0上像素生长,结果保存到图片3
	num = IMG_TYPE::img_grow(&image[0], &image[1], &image[3]);
	cout << "	//图片1作为种子,在rgb图片0上像素生长,结果保存到图片3" << endl;

	//对图片1求连通域,保存到v中,同时对将连通域的伪彩色图片保存到图片4
	vector<AREA> v;
	num = IMG_TYPE::img_to_connected(v, &image[1], &image[4]);
	cout << "	//对图片1求连通域,保存到v中,同时对将连通域的伪彩色图片保存到图片4" << endl;

	//将其中一个较大连通域转为靛色图片,保存到图片5
	num = 0;
	for (auto iter = v.begin(); iter != v.end(); iter++)
		if (iter->a.size() > 1500) {
			num = (int)(iter - v.begin());
			break;
		}
		IMG_TYPE::connected_to_img(v[num], &image[5], 0, 255, 255);
	cout << "	//将其中一个较大连通域转为靛色图片,保存到图片5" << endl;

	//将整个连通域容器转换为一幅伪彩色图片保存到图片6
	IMG_TYPE::connected_to_img(v, &image[6]);
	cout << "	//将整个连通域容器转换为一幅伪彩色图片保存到图片6" << endl;

	//伪彩色显示连通域容器(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)
	show(v);
	cout << "	//伪彩色显示连通域容器(只显示相关连通域" << endl;
	cout << "	//对连通域周围不做处理,可能屏幕保留上一次图片)" << endl;

	//用靛色显示容器中最大的连通域(只显示相关连通域,对连通域周围不做处理,可能屏幕保留上一次图片)
	num = 0;
	int i = 0;
	for (auto iter = v.begin(); iter != v.end(); iter++)
		if ((int)iter->a.size() > num) {
			num = (int)(iter->a.size());
			i = (int)(iter - v.begin());
		}
	show(v[i], SCREEN_X, SCREEN_Y, 0, 255, 255);
	cout << "	//用靛色显示容器中最大的连通域(只显示相关连通域" << endl;
	cout << "	//对连通域周围不做处理,可能屏幕保留上一次图片)" << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();

	return 0;
}

/*****************************************其他图片相关操作例程******************************************/

/*****************************************基于本图像处理库的细胞识别************************************/

#include <iostream>
#include "image.h"

#ifndef STD
#define STD
using namespace std;
#endif

//提取紫色
bool is_purple(uint8& src_r, uint8& src_g, uint8& src_b)
{
	if (src_r > 70 && src_r < 150)		//70/150
		return 1;
	return 0;
}

//满足条件处理成白色
void is_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 255;
}

//不满足条件处理成黑色
void else_do_bit(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = 0;
}

bool all_is(uint8& src_r, uint8& src_g, uint8& src_b)
{
	return 1;
}

void all_do(uint8& src_r, uint8& src_g, uint8& src_b, uint8& drt_r, uint8& drt_g, uint8& drt_b)
{
	drt_r = drt_g = drt_b = (uint8)((int)src_r + src_b - 2 * src_g);
}
int main()
{
	IMG_TYPE image[20];											//创建图片数组
	run_flag = 1;												//开启等待线程
	thread wait_thread(wait_thread_handle);						//创建等待线程

	cout << "begin" << endl;

	int num = 0;//像素数量
	//打开彩色图到图0
	image[0].open("Blood.bmp");
	cout << "	//打开彩色图到图0" << endl;

	//对图片0均衡化得到图片1
	IMG_TYPE::rgb_equalization(&image[0], &image[1]);
	cout << "	//对图片0均衡化得到图片1" << endl;

	//对rgb图像0提取紫色变成灰度图2
	IMG_TYPE::rgb_sift(&image[0], &image[2], all_is, all_do, else_do_bit);
	cout << "	//对rgb图像0提取紫色变成灰度图2" << endl;

	//对灰度图2筛选成细胞二值化图3
	IMG_TYPE::gray_sift(&image[2], &image[3], is_purple, is_do_bit, else_do_bit);
	cout << "	//对灰度图2筛选成二值化图3" << endl;

	//对rgb图片1用索贝尔算法计算边缘图4
	IMG_TYPE::rgb_sobel(&image[1], &image[4], 10);
	cout << "	//对rgb图片1用索贝尔算法计算边缘图4" << endl;

	//对边缘图4灰度化成图片5
	IMG_TYPE::rgb_to_gray(&image[4], &image[5]);
	cout << "	//对边缘图4灰度化成图片5" << endl;

	//对灰度边缘图5大津法求阈值
	num = IMG_TYPE::gray_ostu(&image[5]);
	cout <<num<< "	//对灰度边缘图5大津法求阈值" << endl;

	//边缘灰度图片5二值化到图片6
	IMG_TYPE::gray_to_bit(&image[5], &image[6], num);
	cout << "	//边缘灰度图片5二值化到图片6" << endl;

	//图片6先收缩后扩张,实现滤波,保存到图7
	IMG_TYPE::bit_shrink(&image[6], &image[7], vector<uint8>{1, 255, 1, 255, 255, 255, 1, 255, 1});
	IMG_TYPE::bit_extend(&image[7], &image[7]);
	cout << "	//图片6先收缩后扩张,实现滤波,保存到图7" << endl;

	//二值化细胞图片3减去边缘图片7保存到图片8
	IMG_TYPE::bit_sub(&image[3], &image[7], &image[8]);
	cout << "	//二值化细胞图片3减去边缘图片7保存到图片8" << endl;

	//图片8三次滤波保存到图片9
	IMG_TYPE::bit_shrink(&image[8], &image[9]);
	IMG_TYPE::bit_shrink(&image[9], &image[9]);
	IMG_TYPE::bit_shrink(&image[9], &image[9]);
	cout << "	//图片8三次滤波保存到图片9" << endl;

	//图片9转为连通域,伪彩色图片保存到图10
	vector<AREA> v;
	IMG_TYPE::img_to_connected(v, &image[9], &image[10]);
	IMG_TYPE::connected_to_img(v, &image[10]);
	cout << "	//图片9转为连通域,伪彩色图片保存到图10" << endl;

	//对连通域循环收缩筛选
	vector<AREA> cells;
	cells_sift(v, cells);
	cout << "	//对连通域循环收缩筛选" << endl;
	
	//筛选完成的细胞连通域转伪彩色图片11
	IMG_TYPE::connected_to_img(cells, &image[11]);
	cout << "	//筛选完成的细胞连通域转伪彩色图片11" << endl;

	//滤除特别小的连通域之后,细胞转伪彩色图片12
	int i = 0;
	while (i != cells.size())
	{
		if (cells[i].a.size() <= 3)
			cells.erase(cells.begin() + i);
		else
			i++;
	}
	IMG_TYPE::connected_to_img(cells, &image[12]);
	cout << "	//滤除特别小的连通域之后,细胞转伪彩色图片12" << endl;

	vector<AREA> core;
	//注意此时细胞图片只是大概,需要求中心点然后再筛选
	//对每个连通域求中心
	for (auto iter = cells.begin(); iter != cells.end(); iter++)
	{
		int line = 0, row = 0;
		for (auto i = iter->a.begin(); i != iter->a.end(); i++)
		{
			line += i->line;
			row += i->row;
		}
		if (iter->a.size() != 0)	
			core.push_back(AREA(MPOINT(line / (int)iter->a.size(), row / (int)iter->a.size())));
	}
	cout << "	//对每个连通域求中心" << endl;

	//连通域转为位图13
	IMG_TYPE::connected_to_img(core, &image[13], 255, 255, 255);
	cout << "	//连通域转为位图13" << endl;

	//位图13扩张成圆形,到图14
	vector<uint8> temp(
		{ 1,1,1,1,255,255,255,1,1,1,1,
		1,1,255,255,255,255,255,255,255,1,1,
		1,255,255,255,255,255,255,255,255,255,1,
		1,255,255,255,255,255,255,255,255,255,1,
		255,255,255,255,255,255,255,255,255,255,255,
		255,255,255,255,255,255,255,255,255,255,255,
		255,255,255,255,255,255,255,255,255,255,255,
		1,255,255,255,255,255,255,255,255,255,1,
		1,255,255,255,255,255,255,255,255,255,1,
		1,1,255,255,255,255,255,255,255,1,1,
		1,1,1,1,255,255,255,1,1,1,1,
		});
	IMG_TYPE::bit_extend(&image[13], &image[14], temp);
	cout << "	//位图13扩张成圆形,到图14" << endl;

	//将相邻过近的原合并,重新计算连通域,伪彩色图片保存到图15
	cells.clear();
	core.clear();
	IMG_TYPE::img_to_connected(cells, &image[14], &image[15]);
	cout << "	//将相邻过近的原合并,重新计算连通域,伪彩色图片保存到图15" << endl;

	//对每个连通域求中心
	for (auto iter = cells.begin(); iter != cells.end(); iter++)
	{
		int line = 0, row = 0;
		for (auto i = iter->a.begin(); i != iter->a.end(); i++)
		{
			line += i->line;
			row += i->row;
		}
		if (iter->a.size() != 0)	
			core.push_back(AREA(MPOINT(line / (int)iter->a.size(), row / (int)iter->a.size())));
	}
	cout << "	//对每个连通域求中心" << endl;
	
	//细胞中心连通域转为图片16
	IMG_TYPE::connected_to_img(core, &image[16], 255, 255, 255);
	cout << "	//细胞中心连通域转为图片16" << endl;

	//细胞中心图16扩张成圆形到图片17
	IMG_TYPE::bit_extend(&image[16], &image[17], temp);
	IMG_TYPE::bit_extend(&image[17], &image[17], temp);
	cout<<core.size() << "	//细胞中心图16扩张成圆形到图片17" << endl;

	//将细胞图17与原图片0叠加显示到18
	IMG_TYPE::img_add(&image[17], &image[0], &image[18]);
	cout << "	//将细胞图17与原图片0叠加显示到18" << endl;

	//19是用于清屏的图片
	image[19].copy(image[0]);
	image[19].clear();
	cout << "	//19是用于清屏的图片" << endl;

	cout << "共计" << core.size() << "个细胞" << endl;
	cout << "细胞面积" << cells[1].a.size() << "个像素" << endl;
	cout << "细胞半径" << sqrt(cells[1].a.size() / 3.1415926) << "个像素" << endl;

	v.clear();
	cells.clear();
	core.clear();


	//图片0-19保存成功
	for (int i = 0; i < 20; i++)
		image[i].save(nullptr);
	cout << "	//图片0-19保存成功" << endl;

	cout << "end" << endl;
	cout << "按下0-19查看对应图片,20以上退出" << endl;
	int key;
	while (cin >> key)
	{
		if (key < 20)
			image[key].show();
		else
			break;
	}

	run_flag = 0;				//等待线程
	if (wait_thread.joinable())
		wait_thread.detach();
}

/*****************************************基于本图像处理库的细胞识别************************************/

效果

dos窗口可以查看各个阶段图片,同时所有图片会保存至save文件夹中
运行结果
在这里插入图片描述
程序文件夹下在这里插入图片描述
运行窗口输入对应编号,例如18
在这里插入图片描述

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值