opencv的图像分割与融合技术

嵌入式设计应用

127人已加入

描述

  图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。

  图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。

  基本原理

  基本原理是:通过设定不同的特征阈值,把图像象素点分为若干类。

  常用的特征包括:直接来自原始图像的灰度或彩色特征;由原始灰度或彩色值变换得到的特征。

  设原始图像为f(x,y),按照一定的准则f(x,y)中找到特征值T,将图像分割为两个部分,分割后的图像为:

  若取:b0=0(黑),b1=1(白),即为我们通常所说的图像二值化。

  图像分割的意义:

  在一幅图像中,人们常常只对其中的部分目标感兴趣,这些目标通常占据一定的区域,并且在某些特性(如灰度、轮廓、颜色和纹理等)上和临近的图像有差别。这些特性差别可能非常明显,也可能很细微,以至肉眼察觉不出来。随着计算机图像处理技术的发展,使得人们可以通过计算机来获取和处理图像信息。图像识别的基础是图像分割,其作用是把反映物体真实情况的、占据不同区域的、具有不同特性的目标区分开来,并形成数字特征。图像分割是图像识别和图像理解的基本前提步骤,图像分割质量的好坏直接影响后续图像处理的效果,甚至决定其成败,因此,图像分割的作用是至关重要的。

  函数原型

  1、opencv官方介绍:opencv官方grabcut介绍

  2、网上童鞋翻译解释:学习OpenCV——学习grabcut算法

  3、大致内容如下:

  函数原型:

  void grabCut(InputArray img, InputOutputArray mask, Rect rect,

  InputOutputArray bgdModel, InputOutputArray fgdModel,

  int iterCount, int mode=GC_EVAL )

  img:待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;

  mask:掩码图像,大小和原图像一致。可以有如下几种取值:

  GC_BGD(=0),背景;

  GC_FGD(=1),前景;

  GC_PR_BGD(=2),可能的背景;

  GC_PR_FGD(=3),可能的前景。

  rect:用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;

  bgdModel:背景模型,如果为null,函数内部会自动创建一个bgdModel;

  fgdModel:前景模型,如果为null,函数内部会自动创建一个fgdModel;

  iterCount:迭代次数,必须大于0;

  mode:用于指示grabCut函数进行什么操作。可以有如下几种选择:

  GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;

  GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;

  GC_EVAL(=2),执行分割。

  基本原理:

  首先用户在图片上画一个方框,grabCut默认方框内部为前景,设置掩码为2,方框外部都是背景,设置掩码为0。然后根据算法,

  将方框内部检查出来是背景的位置,掩码由2改为0。最后,经过算法处理,方框中掩码依然为2的,就是检查出来的前景,其他为背景。

  实例讲解1

  这些例子都主要是根据opencv自带的例子:opencvsamplescppgrabcut.cpp 简化修改而来。

  源代码

  代码如下:

  #include “opencv2/highgui/highgui.hpp”

  #include “opencv2/imgproc/imgproc.hpp”

  #include 《iostream》

  using namespace std;

  using namespace cv;

  string filename;

  Mat image;

  string winName = “show”;

  Rect rect;

  Mat mask;

  const Scalar GREEN = Scalar(0,255,0);

  Mat bgdModel, fgdModel;

  void setRectInMask(){

  rect.x = 110;

  rect.y = 220;

  rect.width = 100;

  rect.height = 100;

  }

  static void getBinMask( const Mat& comMask, Mat& binMask ){

  binMask.create( comMask.size(), CV_8UC1 );

  binMask = comMask & 1;

  }

  int main(int argc, char* argv[]){

  Mat binMask, res;

  filename = argv[1];

  image = imread( filename, 1 );

  mask.create(image.size(), CV_8UC1);

  mask.setTo(GC_BGD);

  setRectInMask();

  (mask(rect)).setTo(Scalar(GC_PR_FGD));

  rectangle(image, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);

  imshow(winName, image);

  image = imread( filename, 1 );

  grabCut(image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT);

  getBinMask(mask, binMask);

  image.copyTo(res, binMask);

  imshow(“result”, res);

  waitKey(0);

  return 0;

  }.

  代码讲解

  1、首先是装载需要处理的源图片。

  filename = argv[1];

  image = imread( filename, 1 );

  2、设置掩码,首先创建了一个和源图片一样大小的掩码空间。接着将整个掩码空间设置为背景:GC_BGD。接着创建了一个rect,对应左上角坐标为:

  (110,220),长宽都为100。接着在掩码空间mask对应左边位置的掩码设置为GC_PR_FGD(疑似为前景)。这个rect就是需要分离前景背景的空间。同时

  在源图像上,rect对应的需要被处理位置画出绿色方框框选。接着将画了绿色方框之后的源图片显示出来。

  mask.create(image.size(), CV_8UC1);

  mask.setTo(GC_BGD);

  setRectInMask();

  (mask(rect)).setTo(Scalar(GC_PR_FGD));

  rectangle(image, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);

  imshow(winName, image);

  3、之前的源图像被画了绿色方框,所以需要重新装载一遍源图像。接着使用函数grabCut,根据传入的相关参数,进行前景背景分离操作。最后在生成的

  结果保存在mask中,背景被置为0,前景被置为1。接着将mask结果筛选到binMask中。最后使用image.copyTo(res, binMask);将原图像根据binMask作为掩码,

  将筛选出来的前景复制到目标图像res中。并将目标图像显示出来。

  效果演示

  

  

  图像融合(去裂缝处理)

  从下图可以看出,两图的拼接并不自然,原因就在于拼接图的交界处,两图因为光照色泽的原因使得两图交界处的过渡很糟糕,所以需要特定的处理解决这种不自然。这里的处理思路是加权融合,在重叠部分由前一幅图像慢慢过渡到第二幅图像,即将图像的重叠区域的像素值按一定的权值相加合成新的图像。

 

  //优化两图的连接处,使得拼接自然

  void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)

  {

  int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界

  double processWidth = img1.cols - start;//重叠区域的宽度

  int rows = dst.rows;

  int cols = img1.cols; //注意,是列数*通道数

  double alpha = 1;//img1中像素的权重

  for (int i = 0; i 《 rows; i++)

  {

  uchar* p = img1.ptr《uchar》(i); //获取第i行的首地址

  uchar* t = trans.ptr《uchar》(i);

  uchar* d = dst.ptr《uchar》(i);

  for (int j = start; j 《 cols; j++)

  {

  //如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据

  if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)

  {

  alpha = 1;

  }

  else

  {

  //img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好

  alpha = (processWidth - (j - start)) / processWidth;

  }

  d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);

  d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);

  d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);

  }

  }

  验证拼接效果

  

  最后给出完整的SURF算法实现的拼接代码。

  #include “highgui/highgui.hpp”

  #include “opencv2/nonfree/nonfree.hpp”

  #include “opencv2/legacy/legacy.hpp”

  #include 《iostream》

  using namespace cv;

  using namespace std;

  void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst);

  typedef struct

  {

  Point2f left_top;

  Point2f left_bottom;

  Point2f right_top;

  Point2f right_bottom;

  }four_corners_t;

  four_corners_t corners;

  void CalcCorners(const Mat& H, const Mat& src)

  {

  double v2[] = { 0, 0, 1 };//左上角

  double v1[3];//变换后的坐标值

  Mat V2 = Mat(3, 1, CV_64FC1, v2); //列向量

  Mat V1 = Mat(3, 1, CV_64FC1, v1); //列向量

  V1 = H * V2;

  //左上角(0,0,1)

  cout 《《 “V2: ” 《《 V2 《《 endl;

  cout 《《 “V1: ” 《《 V1 《《 endl;

  corners.left_top.x = v1[0] / v1[2];

  corners.left_top.y = v1[1] / v1[2];

  //左下角(0,src.rows,1)

  v2[0] = 0;

  v2[1] = src.rows;

  v2[2] = 1;

  V2 = Mat(3, 1, CV_64FC1, v2); //列向量

  V1 = Mat(3, 1, CV_64FC1, v1); //列向量

  V1 = H * V2;

  corners.left_bottom.x = v1[0] / v1[2];

  corners.left_bottom.y = v1[1] / v1[2];

  //右上角(src.cols,0,1)

  v2[0] = src.cols;

  v2[1] = 0;

  v2[2] = 1;

  V2 = Mat(3, 1, CV_64FC1, v2); //列向量

  V1 = Mat(3, 1, CV_64FC1, v1); //列向量

  V1 = H * V2;

  corners.right_top.x = v1[0] / v1[2];

  corners.right_top.y = v1[1] / v1[2];

  //右下角(src.cols,src.rows,1)

  v2[0] = src.cols;

  v2[1] = src.rows;

  v2[2] = 1;

  V2 = Mat(3, 1, CV_64FC1, v2); //列向量

  V1 = Mat(3, 1, CV_64FC1, v1); //列向量

  V1 = H * V2;

  corners.right_bottom.x = v1[0] / v1[2];

  corners.right_bottom.y = v1[1] / v1[2];

  }

  int main(int argc, char *argv[])

  {

  Mat image01 = imread(“g5.jpg”, 1); //右图

  Mat image02 = imread(“g4.jpg”, 1); //左图

  imshow(“p2”, image01);

  imshow(“p1”, image02);

  //灰度图转换

  Mat image1, image2;

  cvtColor(image01, image1, CV_RGB2GRAY);

  cvtColor(image02, image2, CV_RGB2GRAY);

  //提取特征点

  SurfFeatureDetector Detector(2000);

  vector《KeyPoint》 keyPoint1, keyPoint2;

  Detector.detect(image1, keyPoint1);

  Detector.detect(image2, keyPoint2);

  //特征点描述,为下边的特征点匹配做准备

  SurfDescriptorExtractor Descriptor;

  Mat imageDesc1, imageDesc2;

  Descriptor.compute(image1, keyPoint1, imageDesc1);

  Descriptor.compute(image2, keyPoint2, imageDesc2);

  FlannBasedMatcher matcher;

  vector《vector《DMatch》 》 matchePoints;

  vector《DMatch》 GoodMatchePoints;

  vector《Mat》 train_desc(1, imageDesc1);

  matcher.add(train_desc);

  matcher.train();

  matcher.knnMatch(imageDesc2, matchePoints, 2);

  cout 《《 “total match points: ” 《《 matchePoints.size() 《《 endl;

  // Lowe‘s algorithm,获取优秀匹配点

  for (int i = 0; i 《 matchePoints.size(); i++)

  {

  if (matchePoints[i][0].distance 《 0.4 * matchePoints[i][1].distance)

  {

  GoodMatchePoints.push_back(matchePoints[i][0]);

  }

  }

  Mat first_match;

  drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);

  imshow(“first_match ”, first_match);

  vector《Point2f》 imagePoints1, imagePoints2;

  for (int i = 0; i《GoodMatchePoints.size(); i++)

  {

  imagePoints2.push_back(keyPoint2[GoodMatchePoints[i].queryIdx].pt);

  imagePoints1.push_back(keyPoint1[GoodMatchePoints[i].trainIdx].pt);

  }

  //获取图像1到图像2的投影映射矩阵 尺寸为3*3

  Mat homo = findHomography(imagePoints1, imagePoints2, CV_RANSAC);

  ////也可以使用getPerspectiveTransform方法获得透视变换矩阵,不过要求只能有4个点,效果稍差

  //Mat homo=getPerspectiveTransform(imagePoints1,imagePoints2);

  cout 《《 “变换矩阵为: ” 《《 homo 《《 endl 《《 endl; //输出映射矩阵

  //计算配准图的四个顶点坐标

  CalcCorners(homo, image01);

  cout 《《 “left_top:” 《《 corners.left_top 《《 endl;

  cout 《《 “left_bottom:” 《《 corners.left_bottom 《《 endl;

  cout 《《 “right_top:” 《《 corners.right_top 《《 endl;

  cout 《《 “right_bottom:” 《《 corners.right_bottom 《《 endl;

  //图像配准

  Mat imageTransform1, imageTransform2;

  warpPerspective(image01, imageTransform1, homo, Size(MAX(corners.right_top.x, corners.right_bottom.x), image02.rows));

  //warpPerspective(image01, imageTransform2, adjustMat*homo, Size(image02.cols*1.3, image02.rows*1.8));

  imshow(“直接经过透视矩阵变换”, imageTransform1);

  imwrite(“trans1.jpg”, imageTransform1);

  //创建拼接后的图,需提前计算图的大小

  int dst_width = imageTransform1.cols; //取最右点的长度为拼接图的长度

  int dst_height = image02.rows;

  Mat dst(dst_height, dst_width, CV_8UC3);

  dst.setTo(0);

  imageTransform1.copyTo(dst(Rect(0, 0, imageTransform1.cols, imageTransform1.rows))); image02.copyTo(dst(Rect(0, 0, image02.cols, image02.rows)));

  imshow(“b_dst”, dst);

  OptimizeSeam(image02, imageTransform1, dst);

  imshow(“dst”, dst);

  imwrite(“dst.jpg”, dst);

  waitKey();

  return 0;

  }

  //优化两图的连接处,使得拼接自然

  void OptimizeSeam(Mat& img1, Mat& trans, Mat& dst)

  {

  int start = MIN(corners.left_top.x, corners.left_bottom.x);//开始位置,即重叠区域的左边界

  double processWidth = img1.cols - start;//重叠区域的宽度

  int rows = dst.rows;

  int cols = img1.cols; //注意,是列数*通道数

  double alpha = 1;//img1中像素的权重

  for (int i = 0; i 《 rows; i++)

  {

  uchar* p = img1.ptr《uchar》(i); //获取第i行的首地址

  uchar* t = trans.ptr《uchar》(i);

  uchar* d = dst.ptr《uchar》(i);

  for (int j = start; j 《 cols; j++)

  {

  //如果遇到图像trans中无像素的黑点,则完全拷贝img1中的数据

  if (t[j * 3] == 0 && t[j * 3 + 1] == 0 && t[j * 3 + 2] == 0)

  {

  alpha = 1;

  }

  else

  {

  //img1中像素的权重,与当前处理点距重叠区域左边界的距离成正比,实验证明,这种方法确实好

  alpha = (processWidth - (j - start)) / processWidth;

  }

  d[j * 3] = p[j * 3] * alpha + t[j * 3] * (1 - alpha);

  d[j * 3 + 1] = p[j * 3 + 1] * alpha + t[j * 3 + 1] * (1 - alpha);

  d[j * 3 + 2] = p[j * 3 + 2] * alpha + t[j * 3 + 2] * (1 - alpha);

  }

  }

  }

  }

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分