前言
顺手写的一个基于数字图像处理的细胞识别,其主要内容包括:
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