这是一幅基因芯片的荧光图像,检测图像的ROI区域,对这个区域内的阴性点(弱)和阳性点(强)的数量进行统计,并标出点的位置。
思路:
(1)观察到图像对比度很低,首先对图像进行对比度增强
(2)图像分割需要获得边缘信息,用canny算子检测边缘
(3)对图像做闭运算,图像中很小的点江北腐蚀掉,从而显现出大的边缘
(4)用findContours方法找出边缘
(5) boundingRect方法检测外轮廓
(6)获得ROI并返回裁剪后的图片
代码:
Mat get_ROI_image(Mat &src_image)
{
Mat imageConvert;
src_image.convertTo(imageConvert, src_image.type(), 3, 0);//对比度增强y = alpha*x + beta; alpha = 3, beta = 0;
Mat src_gray;
cvtColor(imageConvert, src_gray, COLOR_BGR2GRAY);
//使用Canny检测边缘
Mat canny_image;
Canny(src_gray, canny_image, 80, 126, (3, 3));
//imshow("canny_image", canny_image);
//高级形态学闭运算函数
Mat closed_image;
//自定义形态学元素结构
Mat element5(9, 9, CV_8U, cv::Scalar(1));//5*5正方形,8位uchar型,全1结构元素
morphologyEx(canny_image, closed_image, cv::MORPH_CLOSE, element5);
//imshow("closed_image", closed_image);
//waitKey(0);
//外部加框
//检测连通域,每一个连通域以一系列的点表示,FindContours方法只能得到第一个域
vector> contours;
vector hierarchy;
findContours(closed_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//CV_RETR_EXTERNAL只检测外部轮廓,可根据自身需求进行调整
Rect maxRect, secondRect;
for (int index = 0; index >= 0; index = hierarchy[index][0])
{
Rect rect = boundingRect(contours[index]);//检测外轮廓
if (index == 0)
{
maxRect = rect;
secondRect = rect;
}
if (rect.area() > maxRect.area())
{
secondRect = maxRect;
maxRect = rect;
}
}
Mat ROI_image;
ROI_image = src_image(secondRect);
rectangle(src_image, secondRect, Scalar(0, 0, 255), 3);//对外轮廓加矩形框
imshow("src_image_plus_rect", src_image);
imwrite("src_image_plus_rect.bmp", src_image);
cout << "完成检测";
return ROI_image;
}
找阳性点思路:
(1)对ROI图像做ACE处理,得到高对比度的图像
(2)然后对图像进行高阈值二值化处理,以获得阳性点
(3)用canny找出边缘
(4)用findContours方法找出轮廓
(5)drawContours方法画出轮廓
(6)返回轮廓数量近似为阳性点数量
找阴性点思路:
(1)对ROI图像做ACE处理,得到高对比度的图像
(2)然后对图像进行低阈值二值化处理,以获得全部点
(3)用canny找出边缘
(4)用findContours方法找出轮廓
(5)drawContours方法画出轮廓
(6)阴性点数量=全部点的轮廓数量-阳性点的轮廓数量
ACE代码:
Mat ACE(Mat &src, int C = 3, int n = 3, float MaxCG = 7.5)
{
int rows = src.rows;
int cols = src.cols;
Mat meanLocal; //图像局部均值
Mat varLocal; //图像局部方差
Mat meanGlobal;//全局均值
Mat varGlobal; //全局标准差
blur(src.clone(), meanLocal, Size(n, n));
Mat highFreq = src - meanLocal;//高频成分
varLocal = matrixWiseMulti(highFreq, highFreq);
blur(varLocal, varLocal, Size(n, n));
//换算成局部标准差
varLocal.convertTo(varLocal, CV_32F);
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
varLocal.at(i, j) = (float)sqrt(varLocal.at(i, j));
}
}
meanStdDev(src, meanGlobal, varGlobal);
Mat gainArr = 0.5 * meanGlobal / varLocal;//增益系数矩阵
//对增益矩阵进行截止
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
if (gainArr.at(i, j) > MaxCG){
gainArr.at(i, j) = MaxCG;
}
}
}
gainArr.convertTo(gainArr, CV_8U);
gainArr = matrixWiseMulti(gainArr, highFreq);
Mat dst2 = meanLocal + C*highFreq;
return dst2;
}
计算点的数量代码:
size_t cal_point_num(Mat &img, String str)
{
Mat canny_output;
vector > contours;
vector hierarchy;
size_t point_num;
CvFont font;
// canny 边缘检测
Canny(img, canny_output, thresh, thresh * 2, 3);
//imshow("canny", canny_output);
// 寻找轮廓
findContours(canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
// 画出轮廓
for (size_t i = 0; i< contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point());
}
point_num = contours.size();
imshow(str, drawing);
imwrite(str, drawing);
return point_num;
}
高阈值二值化处理结果:
画出的阳性点轮廓结果:
低阈值二值化处理结果:
画出的全部点轮廓结果:
(1)读入待处理图片,获得ROI区域
(2)一些特定的自定义去噪处理
(3)ACE处理
(4)阈值二值化处理
(5)计算点的数量
完整代码:
#include
#include
#include
using namespace std;
using namespace cv;
Mat get_ROI_image(Mat &src_image);//获得矩形有效区域
void draw_hist(Mat &img_gray);//画出一幅图像的直方图
Mat ACE(Mat &src, int C, int n, float MaxCG);//对一幅图像做ACE处理
void drop_noise(Mat &img);//去掉图像的一些噪声
size_t cal_point_num(Mat &img, String str);//canny边缘计算及画出轮廓
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
int main() {
Mat src_image = imread("作业4图像2.bmp", CV_LOAD_IMAGE_UNCHANGED);
if (src_image.empty()){
cout << "图像加载失败" << endl;
}
imshow("src_image", src_image);
Mat ROI_image = get_ROI_image(src_image);//获得区域
imshow("ROI_image", ROI_image);
cvtColor(ROI_image, ROI_image, COLOR_BGR2GRAY);//转换成灰度图
drop_noise(ROI_image);//一些特定的自定义的去噪处理
imshow("ROI_drop_noise_image", ROI_image);
Mat ROI_ACE_image = ACE(ROI_image, 8, 11, 15);//进行ACE处理
imshow("ROI_ACE_image", ROI_ACE_image);
imwrite("ROI_ACE_image.bmp", ROI_ACE_image);
Mat threshold_image;
threshold(ROI_ACE_image, threshold_image, 200, 255, THRESH_BINARY);//高阈值二值化,以获得阳性点
imshow("threshold_image", threshold_image);
imwrite("threshold_image1.bmp", threshold_image);
size_t light_point_num, dark_point_num, whole_point_num;
light_point_num = cal_point_num(threshold_image, "light_point.bmp");//计算阳性点数量
threshold(ROI_ACE_image, threshold_image, 60, 255, THRESH_BINARY);//低阈值二值化,以获得全部点
imshow("threshold_image2", threshold_image);
imwrite("threshold_image2.bmp", threshold_image);
whole_point_num = cal_point_num(threshold_image, "whole_point.bmp");//计算所有点数量
cout << "light_point_num:" << light_point_num << endl;
cout << "dark_point_num:" << whole_point_num - light_point_num << endl;//阴性点数量=总的点数量-阳性点数量
waitKey(0);
return 0;
}
void drop_noise(Mat &img)//自定义的去噪处理
{
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
if (img.at(i, j) >= 69)//图像中有些盐噪声,通过设置阈值滤除掉
{
img.at(i, j) = 0;
}
}
}
}
Mat matrixWiseMulti(Mat &m1, Mat &m2){
Mat dst = m1.mul(m2);
return dst;
}
//float MaxCG:对高频成分的最大增益值,int n:局部半径,int C:对高频的直接增益系数
Mat ACE(Mat &src, int C = 3, int n = 3, float MaxCG = 7.5)
{
int rows = src.rows;
int cols = src.cols;
Mat meanLocal; //图像局部均值
Mat varLocal; //图像局部方差
Mat meanGlobal;//全局均值
Mat varGlobal; //全局标准差
blur(src.clone(), meanLocal, Size(n, n));
//imshow("低通滤波", meanLocal);
Mat highFreq = src - meanLocal;//高频成分
//imshow("高频成分", highFreq);
varLocal = matrixWiseMulti(highFreq, highFreq);
blur(varLocal, varLocal, Size(n, n));
//换算成局部标准差
varLocal.convertTo(varLocal, CV_32F);
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
varLocal.at(i, j) = (float)sqrt(varLocal.at(i, j));
}
}
meanStdDev(src, meanGlobal, varGlobal);
Mat gainArr = 0.5 * meanGlobal / varLocal;//增益系数矩阵
//对增益矩阵进行截止
for (int i = 0; i < rows; i++){
for (int j = 0; j < cols; j++){
if (gainArr.at(i, j) > MaxCG){
gainArr.at(i, j) = MaxCG;
}
}
}
gainArr.convertTo(gainArr, CV_8U);
gainArr = matrixWiseMulti(gainArr, highFreq);
Mat dst2 = meanLocal + C*highFreq;
return dst2;
}
void draw_hist(Mat &img_gray)
{
//需要计算图像的哪个通道(bgr空间需要确定计算 b或g或r空间)
const int channels[1] = { 0 };
//直方图的每一个维度的 柱条的数目(就是将灰度级分组)
int histSize[] = { 256 }; //如果这里写成int histSize = 256; 那么下面调用计算直方图的函数的时候,该变量需要写 &histSize
//定义一个变量用来存储 单个维度 的数值的取值范围
float midRanges[] = { 0, 256 };
//确定每个维度的取值范围,就是横坐标的总数
const float *ranges[] = { midRanges };
//输出的结果存储的 空间 ,用MatND类型来存储结果
MatND dstHist;
calcHist(&img_gray, 1, channels, Mat(), dstHist, 1, histSize, ranges, true, false);
//calcHist 函数调用结束后,dstHist变量中将储存了 直方图的信息 用dstHist的模版函数 at(i)得到第i个柱条的值 at(i, j)得到第i个并且第j个柱条的值
//首先先创建一个黑底的图像,为了可以显示彩色,所以该绘制图像是一个8位的3通道图像
Mat drawImage = Mat::zeros(Size(256, 256), CV_8UC3);
//一个图像的某个灰度级的像素个数(最多为图像像素总数),可能会超过显示直方图的所定义的图像的尺寸,因此绘制直方图的时候,让直方图最高的地方只有图像高度的90%来显示
//先用minMaxLoc函数来得到计算直方图后的像素的最大个数
double g_dHistMaxValue;
minMaxLoc(dstHist, 0, &g_dHistMaxValue, 0, 0);
//遍历直方图得到的数据
//int myvalue[256];
for (int i = 0; i < 256; i++)
{
cout << i << ":" << dstHist.at(i) << endl;
int value = cvRound(256 * 0.9 *(dstHist.at(i) / g_dHistMaxValue));
line(drawImage, Point(i, drawImage.rows - 1), Point(i, drawImage.rows - 1 - value), Scalar(255, 0, 0));
}
imshow("【直方图】", drawImage);
//waitKey(0);
}
Mat get_ROI_image(Mat &src_image)
{
Mat imageConvert;
src_image.convertTo(imageConvert, src_image.type(), 3, 0);//对比度增强y = alpha*x + beta; alpha = 3, beta = 0;
//imshow("imageConvert", imageConvert);
Mat src_gray;
cvtColor(imageConvert, src_gray, COLOR_BGR2GRAY);
//imshow("src_gray", src_gray);
//使用Canny检测边缘
Mat canny_image;
Canny(src_gray, canny_image, 80, 126, (3, 3));
//imshow("canny_image", canny_image);
//高级形态学闭运算函数
Mat closed_image;
//自定义形态学元素结构
Mat element5(9, 9, CV_8U, cv::Scalar(1));//5*5正方形,8位uchar型,全1结构元素
morphologyEx(canny_image, closed_image, cv::MORPH_CLOSE, element5);
//imshow("closed_image", closed_image);
//waitKey(0);
//外部加框
//检测连通域,每一个连通域以一系列的点表示,FindContours方法只能得到第一个域
vector> contours;
vector hierarchy;
findContours(closed_image, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//CV_RETR_EXTERNAL只检测外部轮廓,可根据自身需求进行调整
//Mat contoursImage(closed_image.rows, closed_image.cols, CV_8U, Scalar(255));
Rect maxRect, secondRect;
for (int index = 0; index >= 0; index = hierarchy[index][0])
{
Scalar color(rand() & 255, rand() & 255, rand() & 255);
Rect rect = boundingRect(contours[index]);//检测外轮廓
if (index == 0)
{
maxRect = rect;
secondRect = rect;
}
if (rect.area() > maxRect.area())
{
secondRect = maxRect;
maxRect = rect;
}
}
Mat ROI_image;
ROI_image = src_image(secondRect);
rectangle(src_image, secondRect, Scalar(0, 0, 255), 3);//对外轮廓加矩形框
imshow("src_image_plus_rect", src_image);
imwrite("src_image_plus_rect.bmp", src_image);
cout << "完成检测";
return ROI_image;
}
size_t cal_point_num(Mat &img, String str)
{
Mat canny_output;
vector > contours;
vector hierarchy;
size_t point_num;
CvFont font;
// canny 边缘检测
Canny(img, canny_output, thresh, thresh * 2, 3);
// 寻找轮廓
findContours(canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
// 画出轮廓
for (size_t i = 0; i< contours.size(); i++) {
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0, Point());
}
point_num = contours.size();
imshow(str, drawing);
imwrite(str, drawing);
return point_num;
}