// 注:本内容为做者原创,禁止在其余网站复述内容以及用于商业盈利,如需引用,请标明出处:https://www.cnblogs.com/lv-anchoret/
html
今天咱们来介绍用C++算法如何来实现图像分割算法中的区域生长算法ios
区域生长的简介算法
咱们解决的是对一整张图像全部内容进行区域生长分类,固然,若是是对图像中的某一类型进行区域生长可能更容易一些数据结构
我的理解函数
区域生长算法须要肯定一个阈值,这个值表明同一类图像信息灰度值的差值,好比,我要一我的脸图(假设眼睛是蓝色的),头发是黑色的可是不一样光线反射缘由或者位置不一样,图像中显示的灰度颜色值有五、十、3等,虽然灰度值不同,可是他们表明的都是黑色,都是头发,区域生长,就是把这些类似灰度值的一类事物统一标注显示,这也就达到了分类识别的目的,关于阈值,好比上述的头发,咱们须要将全部是头发的像素点都标注出来,那么咱们的阈值就应该设置为10,若是设置为3,可能灰度值为3和5的点会统一识别,可是10就被排除在外了。优化
算法核心就是一个bfs,设立一个种子点,向四周扩张,若是相邻的点的灰度值相对于种子点在阈值范围以内,那么,咱们把它识别并包含统一进来,扩张完毕以后,全部类似的一类图像将被统一标注。网站
关于标注咱们还须要说一下,一开始,想起了四色定理,即用四种颜色就能够吧整个地图分类标注且相邻类别颜色不一样,后来想了想还不如把同一类型区域中的全部点都设置为种子点灰度像素值。spa
以后想起来咱们光线追踪一直用的ppm彩色文件格式,索性将灰度值转成rgb彩色图看着更爽设计
区域生长算法流程3d
1. 找种子点
2. 扩张原则
3. 终止条件
数据介绍
咱们的数据是一张灰度图 : 见 纹理相册夹中的第二张图
咱们处理输出的是一张彩色图像,图像格式是咱们光线追踪的文件格式 .ppm,用咱们光线追踪的图片解析器(ppmviewer)就能打开(没有的也不要紧,搜索一下,下载不超过十几秒,超轻量级ppm解读器)
咱们都知道,C/C++ 中读取图像麻烦,特别是这种.jpg复杂格式数据,因此,咱们用matlab先把图像读出来,输出到一个TXT中,存储为二维矩阵形式,而后用C++代码读取TXT文件内容,存储到一个二维数据序列中。(都有代码在后面)
咱们侧重实现算法自己,因此关于图像的读取和写入咱们不作过多描述
算法介绍
算法自命名:首次左上区域生长算法
时间复杂度:O(图像大小*种子数量*log(种子数量))
1、区域生长的三大要素确立:
(1)生长合并规则:
用户自定义阈值,根据种子点和当前点的差值,若是在阈值以内,那么合并,将当前点的灰度值设为种子灰度值
(2)种子选取:
将图像左上角第一个点做为第一个种子,在扩张的过程当中第一个不符合生长合并规则的位置,做为下一次生长的种子,即首次选定,后期自适应肯定。
(3)算法结束:
种子为空
2、优缺点
该算法的优势: 针对大型全局生长而衍生
该算法种子不会将同一个位置做为种子屡次重复生长(时间空间复杂度优化)
某个种子在开始生长时,若是已经被包含于另外一个种子的生长区域中,那么该种子将不会进行生长(时间复杂度优化)
该算法的缺点: 首次选定法不能用合适的灰度表明整个区域,只能是坐标小的点的灰度值
生长出来的区域可能不是很完美,由于该区域是由该区域坐标最小的点生长而成的。
3、灰度值转rgb算法设计
由于要将单一的灰度值映射到r、g、b,使其表明的颜色具备独特性
这个能够本身设计,个人设计以下:
4、构架设计
含有一个类 —— regional
数据成员
_img:用于存储图像灰度矩阵
reset:用于记录某个位置的灰度是否被重置
_delt:阈值
成员函数
readfile:读图像灰度矩阵文件
bfs:进行区域生长
out:输出处理后的图像灰度矩阵
readout:读取处理后的图像灰度矩阵文件
gograph:将灰度图像转为rgb图像,因为ppmview显示空间有限,因此将此图划分为6块(将原图像分为3行2列的6块),分别输出6个图像
流程图以下:
5、数据结构设计:(C++描述)
用bfs算法进行的话,必然须要队列,可是种子们不能用队列去存,某个种子进行生长的时候能够用队列记录每个生长状态
此法采用8领域进行生长
用队列存储当前种子生长过程当中的状态点,进行bfs扩展,肯定该种子生长所造成的区域
用set容器存储各个种子,保证了种子点惟一性,即优势2,同时,set容器还会根据位置自动排序,因此致使了缺点2,其次,set容器的存取操做的时间复杂度均为O(n log n)
Bfs状态采用只包含x、y坐标的一个结构体
采用C++描述的首次左上区域生长算法针对2012*1881的灰度卫星图像矩阵数据处理时间为:78.9s
阈值为20
图1
首先蓝色圈所表明的部分,若是是水域的深浅,那么这一块仍是被划分的很清楚的,大体分了5部分
再看下阈值为25的图
图2
如咱们所预期的那样,图1中蓝色圈的水深划分等级更少了
其次,咱们看图1的红色圈表明的水体,内部中间划分出来的区域更小了,或者说水体的边缘区域扩张了。
再如,黑色圈表明的水域,中间的黑色圈有一块东西,它的区域更小了,不利于捕捉细微的水内情况
若是图1的黑色内的小黑圈部分看不太清楚,那么能够看一下下面这个,都是同样的
XnView打开效果
图3
若是把黄色部分看作是竖着的地质锤,那么图2显然少了锤头~
还有水边一片房子汇集地,也被基本划分为一种色调
图4
而针对下图以及图4以及原图上方一片森林山脉,将各类处理方法进行叠加,效果可能会更好,方案以下:
有不少星星点点的噪声,能够选择先去噪声,可是,效果也不是很好
若是要将其归入到统一的大片区域中,仍是选择先作一个平滑处理,将其尖锐的边缘过渡更加平滑些,再进行区域生长,加以阈值调整,星点可能会减小,可能还存在一些,可是不会那么显眼,和周围环境的色差不会那么大了
图5
7、代码
matlab 代码
matlab:
function writetxt I = imread('poyanghu.jpg'); fid = fopen('image.txt','w'); [x,y] = size(I); fprintf(fid,'%d %d\n',x,y); for i = 1:x for j = 1:y fprintf(fid,'%d ',I(i,j)); end fprintf(fid,'\n'); end fclose(fid);
C++:
regional.h
//regional.h
#pragma once namespace region { constexpr int dir[8][2] { {-1,-1}, {-1, 0}, {-1, 1}, { 0,-1}, { 0, 1}, { 1,-1}, { 1, 0}, { 1, 1} }; constexpr size_t H = 2012; constexpr size_t L = 1881; class regional { public: struct pos { int _x, _y; pos(const int a, const int b) :_x(a), _y(b) { } bool operator<(const pos& p)const { if (_x == p._x) return _y < p._y; return _x < p._x; } }; public: regional(const size_t delt); void readfile(); void bfs(); void out(); void readout(); void gograph()const; private: short _img[2012 + 1][1881 + 1]; bool reset[H + 1][L + 1]; size_t _delt; }; }
regional.cpp
#include "regional.h" #include <iostream> #include <fstream> #include <queue> #include <set> using namespace std; using namespace region; regional::regional(const size_t delt) :_delt(delt) { memset(reset, false, sizeof reset); } void regional::readfile() { ifstream infile; infile.open("image.txt"); if (!infile.is_open()) cerr << "open failed" << endl; int x, y; infile >> x >> y; for (int i = 1; i <= x; ++i) for (int j = 1; j <= y; ++j) infile >> _img[i][j]; infile.close(); } void regional::bfs() { queue<pos> Qcurrent; set<pos> Qnew; Qnew.insert(pos(1, 1)); while (Qnew.size()) { Qcurrent.push(*Qnew.begin()); Qnew.erase(Qnew.begin()); if (reset[Qcurrent.front()._x][Qcurrent.front()._y])//该种子点已经访问过 { Qcurrent.pop(); continue; } while (Qcurrent.size()) { pos seed = Qcurrent.front(); reset[seed._x][seed._y] = true; Qcurrent.pop(); for (int trans = 0; trans < 8; ++trans) { pos p(seed._x + dir[trans][0], seed._y + dir[trans][1]); if (p._x > 0 && p._x <= H && p._y > 0 && p._y <= L && !reset[p._x][p._y]) if (abs(_img[p._x][p._y] - _img[seed._x][seed._y]) < _delt) { _img[p._x][p._y] = _img[seed._x][seed._y]; reset[p._x][p._y] = true; Qcurrent.push(p); } else Qnew.insert(p); } } } } void regional::out() { ofstream outfile; outfile.open("outall.txt"); if (!outfile.is_open()) cerr << "open failed" << endl; for (int i = 1; i <= H; ++i) { for (int j = 1; j <= L; ++j) outfile << _img[i][j] << " "; outfile << endl; } outfile.close(); } void regional::readout() { ifstream infile("outall.txt"); if (!infile.is_open()) { cerr << "error open" << endl; } for (int i = 1; i <= H; ++i) for (int j = 1; j <= L; ++j) infile >> _img[i][j]; infile.close(); } void regional::gograph()const { ofstream file; auto left = [&](int cnt) { for (int i = (cnt - 1) * 700 + 1; i <= 700 * cnt; ++i) for (int j = 1; j <= 1000; ++j) file << (int)((0.2 + float(_img[i][j] % 10) / 10)*_img[i][j]) << " " << (int)((0.5 + float(_img[i][j] % 10) / 10)*_img[i][j]) << " " << (int)((0.7 + float(_img[i][j] % 10) / 10)*_img[i][j]) << endl; }; auto right = [&](int cnt) { for (int i = (cnt - 1) * 700 + 1; i <= 700 * cnt; ++i) for (int j = L - 1000 + 1; j <= L; ++j) file << (int)((0.2 + float(_img[i][j] % 10) / 10)*_img[i][j]) << " " << (int)((0.5 + float(_img[i][j] % 10) / 10)*_img[i][j]) << " " << (int)((0.7 + float(_img[i][j] % 10) / 10)*_img[i][j]) << endl; }; file.open("slip1'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; left(1); file.close(); file.open("slip2'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; right(1); file.close(); file.open("slip3'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; left(2); file.close(); file.open("slip4'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; right(2); file.close(); file.open("slip5'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; left(3); file.close(); file.open("slip6'.ppm"); file << "P3" << endl; file << 1000 << " " << 700 << endl; file << "255" << endl; right(3); file.close(); }
main.cpp
#include "regional.h" using namespace region; #include <iostream> #include <fstream> #define stds std:: int main() { regional reg(25); reg.readfile(); reg.bfs(); reg.out(); //reg.readout(); reg.gograph(); stds cout << "complished" << stds endl; return 0; }
感谢您的阅读,生活愉快~