cv1.1

分水岭算法实现分割

分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。

分水岭算法一般和区域生长法或聚类分析法相结合。

分水岭算法一般用于分割感兴趣的图像区域,应用如细胞边界的分割,分割出相片中的头像等等。

// watershedSegmenter.h
#if !defined WATERSHS
#define WATERSHS

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

class WatershedSegmenter {

  private:

	  cv::Mat markers;

  public:

	  void setMarkers(const cv::Mat& markerImage) {

		// Convert to image of ints
		markerImage.convertTo(markers,CV_32S);
	  }

	  cv::Mat process(const cv::Mat &image) {

		// Apply watershed
		cv::watershed(image,markers);

		return markers;
	  }

	  // Return result in the form of an image
	  cv::Mat getSegmentation() {

		cv::Mat tmp;
		// all segment with label higher than 255
		// will be assigned value 255
		markers.convertTo(tmp,CV_8U);

		return tmp;
	  }

	  // Return watershed in the form of an image
	  cv::Mat getWatersheds() {

		cv::Mat tmp;
		markers.convertTo(tmp,CV_8U,255,255);

		return tmp;
	  }
};

#endif
// cv2.cpp : Defines the entry point for the console application.
//
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

#include "watershedSegmenter.h"

int main()
{
	// Read input image
	cv::Mat image= cv::imread("f:\\img\\group.jpg");
	if (!image.data)
		return 0; 

    // Display the image
	cv::namedWindow("Original Image");
	cv::imshow("Original Image",image);

	// Get the binary map
	cv::Mat binary;
	binary= cv::imread("f:\\img\\binary.bmp",0);

    // Display the binary image
	cv::namedWindow("Binary Image");
	cv::imshow("Binary Image",binary);

	// Eliminate noise and smaller objects
	cv::Mat fg;
	cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),6);

    // Display the foreground image
	cv::namedWindow("Foreground Image");
	cv::imshow("Foreground Image",fg);
	cv::imwrite("ForegroundImage.jpg",fg);
	// Identify image pixels without objects
	cv::Mat bg;
	cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),6);
	cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);

    // Display the background image
	cv::namedWindow("Background Image");
	cv::imshow("Background Image",bg);
	cv::imwrite("BackgroundImage.jpg",bg);
	// Show markers image
	cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
	markers= fg+bg;
	cv::namedWindow("Markers");
	cv::imshow("Markers",markers);
	cv::imwrite("Markers.jpg",markers);
	// Create watershed segmentation object
	WatershedSegmenter segmenter;

	// Set markers and process
	segmenter.setMarkers(markers);
	segmenter.process(image);

	// Display segmentation result
	cv::namedWindow("Segmentation");
	cv::imshow("Segmentation",segmenter.getSegmentation());
	cv::imwrite("Segmentation.jpg",segmenter.getSegmentation());
	// Display watersheds
	cv::namedWindow("Watersheds");
	cv::imshow("Watersheds",segmenter.getWatersheds());
	cv::imwrite("Watersheds.jpg",segmenter.getWatersheds());
	// Open another image
	image= cv::imread("f:\\img\\tower.jpg");

	// Identify background pixels
	cv::Mat imageMask(image.size(),CV_8U,cv::Scalar(0));
	cv::rectangle(imageMask,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255),3);
	// Identify foreground pixels (in the middle of the image)
	cv::rectangle(imageMask,cv::Point(image.cols/2-10,image.rows/2-10),
						 cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1),10);

	// Set markers and process
	segmenter.setMarkers(imageMask);
	segmenter.process(image);

    // Display the image with markers
	cv::rectangle(image,cv::Point(5,5),cv::Point(image.cols-5,image.rows-5),cv::Scalar(255,255,255),3);
	cv::rectangle(image,cv::Point(image.cols/2-10,image.rows/2-10),
						 cv::Point(image.cols/2+10,image.rows/2+10),cv::Scalar(1,1,1),10);
	cv::namedWindow("Image with marker");
	cv::imshow("Image with marker",image);
	cv::imwrite("Image with marker.jpg",image);
	// Display watersheds
	cv::namedWindow("Watersheds of foreground object");
	cv::imshow("Watersheds of foreground object",segmenter.getWatersheds());
	cv::imwrite("Watersheds of foreground object.jpg",segmenter.getWatersheds());

	cv::waitKey();
	return 0;
}

grabcut算法进行图像分割

原理在这几篇博客里已经讲得很仔细了,涉及到的内容也比较多,大家可以查阅一下,它是一个系列来的

①、图像分割之(一)概述

②、 图像分割之(二)Graph Cut(图割)

③、 图像分割之(三)从Graph Cut到Grab Cut

④、 图像分割之(四)OpenCV的GrabCut函数使用和源码解读

总的来说,GrabCut算法时Graph Cut算法的改进,主要有以下几点的改进

①、Graph Cut的目标和背景的模型是灰度直方图,Grab Cut取代为RGB三通道的混合高斯模型GMM;

②、Graph Cut的能量最小化(分割)是一次达到的,而Grab Cut取代为一个不断进行分割估计和模型参数学习的交互迭代过程;

③、Grab Cut允许不完全的标注,Graph Cut需要用户指定目标和背景的一些种子点,但是Grab Cut只需要提供背景区域的像素集,最后如果需要得到更精确的分割,可以在初次分割的结果上加上一些确定的种子点,再运行算法。

void cv::grabCut( const Mat& img, Mat& mask, Rect rect,

Mat& bgdModel, Mat& fgdModel,

int iterCount, int mode )

其中:

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

mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:

GCD_BGD(=0),背景;

GCD_FGD(=1),前景;

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

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

如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只会有GCD_PR_BGD或GCD_PR_FGD;

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

bgdModel——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

fgdModel——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;

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

mode——用于指示grabCut函数进行什么操作,可选的值有:

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

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

GC_EVAL(=2),执行分割。

GrabCut的用法

您可以按以下方式来使用GrabCut函数:

(1)用矩形窗或掩码图像初始化grabCut;

(2)执行分割;

(3)如果对结果不满意,在掩码图像中设定前景和(或)背景,再次执行分割;

(4)使用掩码图像中的前景或背景信息。

例子1

// cv2.cpp : Defines the entry point for the console application.
//
#include <opencv2/opencv.hpp>
//#include <cv.h>
//#include <highgui.h>

using namespace cv;
using namespace std;
#include <iostream>

void getBinMask( const Mat& comMask, Mat& binMask )
{
    binMask.create( comMask.size(), CV_8UC1 );
    binMask = comMask & 1;
}
int main( int argc, char** argv )
{
    Mat image = imread( "f:\\img\\lena.jpg", 1 );
	const string winName = "image";
	imshow("src",image);

	/***********************************/
	Mat bg;Mat fg;
	Rect rect = Rect(47,48,408,464);
	Mat mask,res;
	mask.create( image.size(), CV_8UC1);
    grabCut( image, mask, rect, bg, fg, 1, 0 );

	Mat binMask;
	getBinMask( mask, binMask );
    image.copyTo( res, binMask );
	imshow("cut",res);
	/***********************************/
	waitKey(0);
    return 0;
}

例子2

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
const Scalar RED = Scalar(0,0,255);
const Scalar PINK = Scalar(230,130,255);
const Scalar BLUE = Scalar(255,0,0);
const Scalar LIGHTBLUE = Scalar(255,255,160);
const Scalar GREEN = Scalar(0,255,0);

const int BGD_KEY = CV_EVENT_FLAG_CTRLKEY;//当CTRL被按下时,flags返回的值
const int FGD_KEY = CV_EVENT_FLAG_SHIFTKEY;//当SHIFT被按下时,flags返回的值

static void getBinMask( const Mat& comMask, Mat& binMask )
{
  if( comMask.empty() || comMask.type()!=CV_8UC1 )
    CV_Error( CV_StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );
  if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )
    binMask.create( comMask.size(), CV_8UC1 );
  binMask = comMask & 1;
}
class GCApplication
{
public:
  enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
  static const int radius = 2;
  static const int thickness = -1;

  void reset();
  void setImageAndWinName( const Mat& _image, const string& _winName );
  void showImage() const;
  void mouseClick( int event, int x, int y, int flags, void* param );
  int nextIter();
  int getIterCount() const { return iterCount; }
private:
  void setRectInMask();
  void setLblsInMask( int flags, Point p, bool isPr );

  const string* winName;
  const Mat* image;
  Mat mask;
  Mat bgdModel, fgdModel;
  //rectState, lblsState, prLblsState三个变量分别表示矩形标记的状态,
  //鼠标左键标记的状态,鼠标右键标记的状态,分别有三个状态:NOT_SET(未处理)
  //IN_PROCESS(处理)、SET(已处理)
  uchar rectState, lblsState, prLblsState;
  bool isInitialized;

  Rect rect;
  //在第一次矩形分割后,第二次标记mask值时,四种值出现的点都分别保存在
  //fgdPxls, bgdPxls, prFgdPxls, prBgdPxls四个变量中
  vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;
  //迭代的次数
  int iterCount;
};
#include "GCApplication.h"
//初始化掩码图和各变量
//mask图GrabCut函数中对应第二个参数,标记图片中哪些属于前景,哪些属于背景
//mask图只可存入四种数值,分别为:GC_BGD、GC_FGD、GC_PR_BGD、GC_PR_FGD
//mask初始化为背景,即赋值为GC_BGD
void GCApplication::reset()
{
  if( !mask.empty() )
    mask.setTo(Scalar::all(GC_BGD));
  bgdPxls.clear(); fgdPxls.clear();
  prBgdPxls.clear();  prFgdPxls.clear();
  isInitialized = false;
  rectState = NOT_SET;
  lblsState = NOT_SET;
  prLblsState = NOT_SET;
  iterCount = 0;
}
//初始化窗口和图片
//将读取的图片和窗口名存入类中的私有变量image和winName中,有利于存储
//初始化掩码图以及各变量
void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName  )
{
  if( _image.empty() || _winName.empty() )
    return;
  image = &_image;
  winName = &_winName;
  mask.create( image->size(), CV_8UC1);
  reset();
}
//显示图片
//如果 fgdPxls, bgdPxls, prFgdPxls, prBgdPxls变量非空,则在图片中显示标记的点
//如果 rectState 已经表示被标记,则也在图片中显示标记的矩形
void GCApplication::showImage() const
{
  if( image->empty() || winName->empty() )
    return;

  Mat res;
  Mat binMask;
  //如果图像已经被重置,则拷贝整幅图像
  //否则显示已经被处理过的图像
  if( !isInitialized )
    image->copyTo( res );
  else
  {
    getBinMask( mask, binMask );
    image->copyTo( res, binMask );
  }

  vector<Point>::const_iterator it;
  for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )
    circle( res, *it, radius, BLUE, thickness );
  for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )
    circle( res, *it, radius, RED, thickness );
  for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )
    circle( res, *it, radius, LIGHTBLUE, thickness );
  for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )
    circle( res, *it, radius, PINK, thickness );

  if( rectState == IN_PROCESS || rectState == SET )
    rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);

  imshow( *winName, res );
}
//通过矩形标记Mask
void GCApplication::setRectInMask()
{
  assert( !mask.empty() );
  mask.setTo( GC_BGD );
  rect.x = max(0, rect.x);
  rect.y = max(0, rect.y);
  rect.width = min(rect.width, image->cols-rect.x);
  rect.height = min(rect.height, image->rows-rect.y);
  (mask(rect)).setTo( Scalar(GC_PR_FGD) );
}

void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
{
  vector<Point> *bpxls, *fpxls;
  uchar bvalue, fvalue;
  //如果左键按下,则运行以下代码
  if( !isPr )
  {
    bpxls = &bgdPxls;
    fpxls = &fgdPxls;
    bvalue = GC_BGD;
    fvalue = GC_FGD;
  }
  //否则,运行以下代码
  else
  {
    bpxls = &prBgdPxls;
    fpxls = &prFgdPxls;
    bvalue = GC_PR_BGD;
    fvalue = GC_PR_FGD;
  }
  //判断是shift键被按下或者ctrl键被按下,分别执行操作
  if( flags & BGD_KEY )
  {
    bpxls->push_back(p);
    circle( mask, p, radius, bvalue, thickness );
  }
  if( flags & FGD_KEY )
  {
    fpxls->push_back(p);
    circle( mask, p, radius, fvalue, thickness );
  }
}
//鼠标响应
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
{
  // TODO add bad args check
  switch( event )
  {
  case CV_EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
    {
      bool isb = (flags & BGD_KEY) != 0,
        isf = (flags & FGD_KEY) != 0;
      //如果rectState为NOT_SET并且ctrl或者shift没被按下,则运行以下代码,设置矩形框
      if( rectState == NOT_SET && !isb && !isf )
      {
        rectState = IN_PROCESS;
        rect = Rect( x, y, 1, 1 );
      }
      //如果rectState为SET,并且ctrl或者shift被按下,则运行以下代码,标记GC_BGD(GC_FGD)
      if ( (isb || isf) && rectState == SET )
        lblsState = IN_PROCESS;
    }
    break;
  case CV_EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
    {
      //如果rectState为SET,并且ctrl或者shift被按下时,标记GC_PR_BGD(GC_PR_FGD)
      bool isb = (flags & BGD_KEY) != 0,
        isf = (flags & FGD_KEY) != 0;
      if ( (isb || isf) && rectState == SET )
        prLblsState = IN_PROCESS;
    }
    break;
  case CV_EVENT_LBUTTONUP:
    //如果rectState为IN_PROCESS,则确定鼠标走过的整个矩形,并且通过矩形设置Mask
    if( rectState == IN_PROCESS )
    {
      rect = Rect( Point(rect.x, rect.y), Point(x,y) );
      rectState = SET;
      setRectInMask();
      assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
      showImage();
    }
    //如果lblsState为IN_PROCESS,则通过圆圈标记Mask
    if( lblsState == IN_PROCESS )
    {
      setLblsInMask(flags, Point(x,y), false);
      lblsState = SET;
      showImage();
    }
    break;
  case CV_EVENT_RBUTTONUP:
    //如果prLblsState为IN_PROCESS,则通过圆圈标记Mask
    if( prLblsState == IN_PROCESS )
    {
      setLblsInMask(flags, Point(x,y), true);
      prLblsState = SET;
      showImage();
    }
    break;
  case CV_EVENT_MOUSEMOVE:
    //如果rectState为IN_PROCESS,则鼠标移动时生成矩形
    if( rectState == IN_PROCESS )
    {
      rect = Rect( Point(rect.x, rect.y), Point(x,y) );
      assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
      showImage();
    }//如果lblsState为IN_PROCESS,则鼠标移动时用圆圈标记Mask
    else if( lblsState == IN_PROCESS )
    {
      setLblsInMask(flags, Point(x,y), false);
      showImage();
    }//如果prLblsState为IN_PROCESS,则鼠标移动时用圆圈标记Mask
    else if( prLblsState == IN_PROCESS )
    {
      setLblsInMask(flags, Point(x,y), true);
      showImage();
    }
    break;
  }
}
//如果lblsState或者prLblsState被设置为SET,则说明图片已经被鼠标标记处前景和背景,
//而且已经经过矩形处理过一次了,则执行grabCut的GC_INIT_WITH_MASK形式,否则,执行
//GC_INIT_WITH_RECT形式,清除bgdPxls等变量标记,方便下次标记
int GCApplication::nextIter()
{
  if( isInitialized )
    grabCut( *image, mask, rect, bgdModel, fgdModel, 3 );
  else
  {
    if( rectState != SET )
      return iterCount;

    if( lblsState == SET || prLblsState == SET )
      grabCut( *image, mask, rect, bgdModel, fgdModel, 3, GC_INIT_WITH_MASK );
    else
      grabCut( *image, mask, rect, bgdModel, fgdModel, 3, GC_INIT_WITH_RECT );

    isInitialized = true;
  }
  iterCount++;

  bgdPxls.clear(); fgdPxls.clear();
  prBgdPxls.clear(); prFgdPxls.clear();

  return iterCount;
}
// cv2.cpp : Defines the entry point for the console application.
//
#include <opencv2/opencv.hpp>
//#include <cv.h>
//#include <highgui.h>
using namespace cv;
using namespace std;
#include <iostream>
#include "GCApplication.h"

static void help()
{
  cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"
    "and then grabcut will attempt to segment it out.\n"
    "Call:\n"
    "./grabcut <image_name>\n"
    "\nSelect a rectangular area around the object you want to segment\n" <<
    "\nHot keys: \n"
    "\tESC - quit the program\n"
    "\tr - restore the original image\n"
    "\tn - next iteration\n"
    "\n"
    "\tleft mouse button - set rectangle\n"
    "\n"
    "\tCTRL+left mouse button - set GC_BGD pixels\n"
    "\tSHIFT+left mouse button - set CG_FGD pixels\n"
    "\n"
    "\tCTRL+right mouse button - set GC_PR_BGD pixels\n"
    "\tSHIFT+right mouse button - set CG_PR_FGD pixels\n" << endl;
}

GCApplication gcapp;

static void on_mouse( int event, int x, int y, int flags, void* param )
{
  gcapp.mouseClick( event, x, y, flags, param );
}

int main( int argc, char** argv )
{
  //读取图片文件
  string filename ="f:\\img\\ball2.jpg";
  if( filename.empty() )
  {
    cout << "\nDurn, couldn't read any file."<< endl;
    return 1;
  }
  Mat image = imread( filename, 1 );
  if( image.empty() )
  {
    cout << "\n Durn, couldn't read image filename " << filename << endl;
    return 1;
  }
  //帮助说明
  help();
  imshow("src",image);

  const string winName = "image";
  namedWindow( winName, WINDOW_AUTOSIZE );
  //设置鼠标响应函数
  setMouseCallback( winName, on_mouse, 0 );
  //初始化窗口和图片
  gcapp.setImageAndWinName( image, winName );
  gcapp.showImage();

  for(;;)
  {
    int c = waitKey(0);
    switch( (char) c )
    {
      //ESC按键退出
    case '\x1b':
      cout << "Exiting ..." << endl;
      goto exit_main;
      //r按键重置图像
    case 'r':
      cout << endl;
      gcapp.reset();
      gcapp.showImage();
      break;
      //n按键进行一次处理
    case 'n':
      int iterCount = gcapp.getIterCount();
      cout << "<" << iterCount << "... ";
      int newIterCount = gcapp.nextIter();
      if( newIterCount > iterCount )
      {
        gcapp.showImage();
        cout << iterCount << ">" << endl;
      }
      else
        cout << "rect must be determined>" << endl;
      break;
    }
  }

exit_main:
  destroyWindow( winName );
  return 0;
}

直线拟合

求直线距离

// cv2.cpp : Defines the entry point for the console application.
//
#include "ProcessImage.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#define _TEST
using namespace cv;
int main(int argc, char * argv[])
{
    //判断输入是否满足要求  

    IplImage *pSrc = cvLoadImage("f:\\img\\line.png", CV_LOAD_IMAGE_GRAYSCALE);
    if (!pSrc)
    {
        std::cout << "read file failed!";
        return -1;
    }  

    //显示原图
    namedWindow("原图", CV_WINDOW_AUTOSIZE);
    cvShowImage("原图", pSrc);  

    IplImage *pTemp = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
    IplImage *pDst = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);  

    //将原图像转换为二值图像
    cvThreshold(pSrc, pTemp, 128, 1, CV_THRESH_BINARY_INV);
    //细化
    thinImage(pTemp, pDst);  

#ifdef _TEST
    //显示细化后的图像
    IplImage *pThinImage = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
    cvCopy(pDst, pThinImage);
    cvThreshold(pThinImage, pThinImage, 0.5, 255,CV_THRESH_BINARY);
    namedWindow("1 图像细化的结果", CV_WINDOW_AUTOSIZE);
    cvShowImage("1 图像细化的结果", pThinImage);
    cvReleaseImage(&pThinImage);
#endif  

    //求轮廓
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contours = 0;
    cvFindContours(pDst , storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));  

#ifdef _TEST
    //将轮廓画出来
    IplImage *pDrawing1 = cvCreateImage(cvGetSize(pSrc),8,3);
    cvZero(pDrawing1);
    cvDrawContours(pDrawing1, contours, Scalar(255, 0, 0), Scalar(0, 0, 255), 1, 2, 8, cvPoint(0, 0));
    namedWindow("2 求轮廓", CV_WINDOW_AUTOSIZE);
    cvShowImage("2 求轮廓", pDrawing1);
    cvReleaseImage(&pDrawing1);
#endif  

    //轮廓已经寻找到,均在contours中存放,我们需要对轮廓进行拟合
    //FitLine函数的用法:
    // 二维空间点拟合时 是 float[4]
    // 三位空间点拟合时 是 float[6]
    float *line1 = new float[4];
    float *line2 = new float[4];
    // 第一个参数: 存储点序列
    // 第二个参数: 拟合算法,其中 CV_DIST_L2 就是平常的最小二乘法
    // 第三,第四,第五参数推荐值是 0,   0.01,  0.01,
    // 第六参数: line中存储返回值
    // 二维空间时: line[0--3] 分别为 (vx, vy, x0, y0)
    //      其中 vx, vy 是正规化之后的斜率向量。 x0,y0 是直线经过的点。
    // 三维空间时: line[0--5]  分别是 (vx, vy, vz, x0, y0, z0) 。意义同上
    cvFitLine(contours, CV_DIST_L2, 0, 0.01, 0.01, line1);
    cvFitLine(contours->h_next, CV_DIST_L2, 0, 0.01, 0.01, line2);  

    //输出四个点
    std::cout << "第一条线: " << line1[0] << " " << line1[1] << " " << line1[2] << " " << line1[3] << std::endl;
    std::cout << "第二条线: " << line2[0] << " " << line2[1] << " " << line2[2] << " " << line2[3] << std::endl;  

#ifdef _TEST
    //根据直线方程公式,我们从直线上取点,并画出来
    IplImage *pDrawing2 = cvCreateImage(cvGetSize(pSrc), 8, 3);
    cvZero(pDrawing2);
    cvLine(pDrawing2, cvPoint(0, (int)(line1[3] - line1[1] / line1[0] * line1[2])),
        cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line1[2])*line1[1] / line1[0] + line1[3])),
        cvScalar(255, 0, 0));
    cvLine(pDrawing2, cvPoint(0, (int)(line2[3] - line2[1] / line2[0] * line2[2])),
        cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line2[2])*line2[1] / line2[0] + line2[3])),
        cvScalar(0, 0, 255));
    namedWindow("3 直线拟合", CV_WINDOW_AUTOSIZE);
    cvShowImage("3 直线拟合", pDrawing2);
    cvReleaseImage(&pDrawing2);
#endif  

    //我们根据距离方程,求出两条直线的距离
    double distance = abs(line1[0] * (line2[3]-line1[3]) - line1[1] * (line2[2]-line1[2])); //注意,vx,vy已经正规化了
    std::cout << "两条直线之间的距离为: " << distance << std::endl;
    delete[] line1;
    delete[] line2;  

    cvReleaseMemStorage(&storage);
    cvReleaseImage(&pSrc);
    cvReleaseImage(&pTemp);
    cvReleaseImage(&pDst);  

    waitKey(0);  

    return 0;
}  
//ProcessImage.h
#pragma once
#include <opencv2/highgui/highgui.hpp>  

/* 对输入图像进行细化
 * src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
 * dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白
 * maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
 */
void thinImage(IplImage* src, IplImage* dst, int maxIterations = -1); 
    //ProcessImage.cpp
    #include "ProcessImage.h"
    #include <utility>
    #include <vector>
    void thinImage(IplImage* src, IplImage* dst, int maxIterations)
    {
        using namespace cv;
        CvSize size = cvGetSize(src);
        cvCopy(src, dst);//将src中的内容拷贝到dst中
        int count = 0;  //记录迭代次数
        while (true)
        {
            count++;
            if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达
                break;
            //std::cout << count << ' ';输出迭代次数
            std::vector<std::pair<int, int> > mFlag; //用于标记需要删除的点
            //对点标记
            for (int i = 0; i<size.height; ++i)
            {
                for (int j = 0; j<size.width; ++j)
                {
                    //如果满足四个条件,进行标记
                    //  p9 p2 p3
                    //  p8 p1 p4
                    //  p7 p6 p5
                    int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
                    int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
                    int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
                    int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
                    int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
                    int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
                    int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
                    int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
                    int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);  

                    if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
                    {
                        int ap = 0;
                        if (p2 == 0 && p3 == 1) ++ap;
                        if (p3 == 0 && p4 == 1) ++ap;
                        if (p4 == 0 && p5 == 1) ++ap;
                        if (p5 == 0 && p6 == 1) ++ap;
                        if (p6 == 0 && p7 == 1) ++ap;
                        if (p7 == 0 && p8 == 1) ++ap;
                        if (p8 == 0 && p9 == 1) ++ap;
                        if (p9 == 0 && p2 == 1) ++ap;  

                        if (ap == 1)
                        {
                            if (p2*p4*p6 == 0)
                            {
                                if (p4*p6*p8 == 0)
                                {
                                    //标记
                                    mFlag.push_back(std::make_pair(i, j));
                                }
                            }
                        }
                    }
                }
            }  

            //将标记的点删除
            for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
            {
                CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
            }  

            //直到没有点满足,算法结束
            if (mFlag.size() == 0)
            {
                break;
            }
            else
            {
                mFlag.clear();//将mFlag清空
            }  

            //对点标记
            for (int i = 0; i<size.height; ++i)
            {
                for (int j = 0; j<size.width; ++j)
                {
                    //如果满足四个条件,进行标记
                    //  p9 p2 p3
                    //  p8 p1 p4
                    //  p7 p6 p5
                    int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
                    if (p1 != 1) continue;
                    int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
                    int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
                    int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
                    int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
                    int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
                    int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
                    int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
                    int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);  

                    if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
                    {
                        int ap = 0;
                        if (p2 == 0 && p3 == 1) ++ap;
                        if (p3 == 0 && p4 == 1) ++ap;
                        if (p4 == 0 && p5 == 1) ++ap;
                        if (p5 == 0 && p6 == 1) ++ap;
                        if (p6 == 0 && p7 == 1) ++ap;
                        if (p7 == 0 && p8 == 1) ++ap;
                        if (p8 == 0 && p9 == 1) ++ap;
                        if (p9 == 0 && p2 == 1) ++ap;  

                        if (ap == 1)
                        {
                            if (p2*p4*p8 == 0)
                            {
                                if (p2*p6*p8 == 0)
                                {
                                    //标记
                                    mFlag.push_back(std::make_pair(i, j));
                                }
                            }
                        }
                    }
                }
            }
            //删除
            for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
            {
                CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
            }  

            //直到没有点满足,算法结束
            if (mFlag.size() == 0)
            {
                break;
            }
            else
            {
                mFlag.clear();//将mFlag清空
            }
        }
    }  

圆拟合

椭圆拟合

#include "opencv\\cv.h"
#include "opencv\\highgui.h"
int slider_pos = 70;
IplImage *image02 = 0, *image03 = 0, *image04 = 0;
void process_image(int h);
int main( int argc, char** argv )
{
    const char* filename = "f:\\img\\tc.png";

    // 读入图像,强制为灰度图像
    if( (image03 = cvLoadImage(filename, 0)) == 0 )
        return -1;
    // Create the destination images
    image02 = cvCloneImage( image03 );
    image04 = cvCloneImage( image03 );
    // Create windows.
    cvNamedWindow("Source", 1);
    cvNamedWindow("Result", 1);
    // Show the image.
    cvShowImage("Source", image03);
    // Create toolbars. HighGUI use.
    cvCreateTrackbar( "Threshold", "Result", &slider_pos, 255, process_image );
    process_image(0);
    // Wait for a key stroke; the same function arranges events processing
    cvWaitKey(0);
    cvReleaseImage(&image02);
    cvReleaseImage(&image03);
    cvDestroyWindow("Source");
    cvDestroyWindow("Result");
    return 0;
}
// Define trackbar callback functon. This function find contours,
// draw it and approximate it by ellipses.
void process_image(int h)
{
    CvMemStorage* stor;
    CvSeq* cont;
    CvBox2D32f* box;
    CvPoint* PointArray;
    CvPoint2D32f* PointArray2D32f;

    // 创建动态结构序列
    stor = cvCreateMemStorage(0);
    cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);

    // 二值话图像.
    cvThreshold( image03, image02, slider_pos, 255, CV_THRESH_BINARY );

    // 寻找所有轮廓.
    cvFindContours( image02, stor, &cont, sizeof(CvContour),
                    CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0,0));

    // Clear images. IPL use.
    cvZero(image02);
    cvZero(image04);

    // 本循环绘制所有轮廓并用椭圆拟合.
    for(;cont;cont = cont->h_next)
    {
        int i; // Indicator of cycle.
        int count = cont->total; // This is number point in contour
        CvPoint center;
        CvSize size;

        // Number point must be more than or equal to 6 (for cvFitEllipse_32f).
        if( count < 6 )
            continue;

        // Alloc memory for contour point set.
        PointArray = (CvPoint*)malloc( count*sizeof(CvPoint) );
        PointArray2D32f= (CvPoint2D32f*)malloc( count*sizeof(CvPoint2D32f) );

        // Alloc memory for ellipse data.
        box = (CvBox2D32f*)malloc(sizeof(CvBox2D32f));

        // Get contour point set.
        cvCvtSeqToArray(cont, PointArray, CV_WHOLE_SEQ);

        // Convert CvPoint set to CvBox2D32f set.
        for(i=0; i<count; i++)
        {
            PointArray2D32f[i].x = (float)PointArray[i].x;
            PointArray2D32f[i].y = (float)PointArray[i].y;
        }

        //拟合当前轮廓.
        cvFitEllipse(PointArray2D32f, count, box);

        // 绘制当前轮廓.
        cvDrawContours(image04,cont,CV_RGB(255,255,255),
CV_RGB(255,255,255),0,1,8,cvPoint(0,0));

        // Convert ellipse data from float to integer representation.
        center.x = cvRound(box->center.x);
        center.y = cvRound(box->center.y);
        size.width = cvRound(box->size.width*0.5);
        size.height = cvRound(box->size.height*0.5);
        box->angle = -box->angle;

        // Draw ellipse.
        cvEllipse(image04, center, size,
                  box->angle, 0, 360,
                  CV_RGB(0,0,255), 1, CV_AA, 0);

        // Free memory.
        free(PointArray);
        free(PointArray2D32f);
        free(box);
    }

    // Show image. HighGUI use.
    cvShowImage( "Result", image04 );
}

图像修补

例子1

// cv2.cpp : Defines the entry point for the console application.
//
#include <opencv2/opencv.hpp> 

#include <iostream>

using namespace cv;
using namespace std;

static void help()
{
    cout << "\nCool inpainging demo. Inpainting repairs damage to images by floodfilling the damage \n"
            << "with surrounding image areas.\n"
            "Using OpenCV version %s\n" << CV_VERSION << "\n"
    "Usage:\n"
        "./inpaint [image_name -- Default fruits.jpg]\n" << endl;

    cout << "Hot keys: \n"
        "\tESC - quit the program\n"
        "\tr - restore the original image\n"
        "\ti or SPACE - run inpainting algorithm\n"
        "\t\t(before running it, paint something on the image)\n" << endl;
}

Mat img, inpaintMask;
Point prevPt(-1,-1);

static void onMouse( int event, int x, int y, int flags, void* )
{
    if( event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON) )
        prevPt = Point(-1,-1);
    else if( event == CV_EVENT_LBUTTONDOWN )
        prevPt = Point(x,y);
    else if( event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON) )
    {
        Point pt(x,y);
        if( prevPt.x < 0 )
            prevPt = pt;
        line( inpaintMask, prevPt, pt, Scalar::all(255), 5, 8, 0 );
        line( img, prevPt, pt, Scalar::all(255), 5, 8, 0 );
        prevPt = pt;
        imshow("image", img);
    }
}

int main( int argc, char** argv )
{
        //读取图像和mask图像
    char* filename = "f:\\img\\inpaint.jpg";
    Mat img0 = imread(filename, -1);
    if(img0.empty())
    {
        cout << "Couldn't open the image " << filename << ". Usage: inpaint <image_name>\n" << endl;
        return 0;
    }

    namedWindow( "image", 1 );
    img = img0.clone();

    imshow("image", img);

        Mat inpaintMask = imread("f:\\img\\mask2.jpg", 0);
        imshow("mask",inpaintMask);
              Mat inpainted;
                          //注意这个inpaintmask的
              inpaint(img, inpaintMask, inpainted, 3, CV_INPAINT_TELEA);
              imshow("inpainted image", inpainted);
    cv::waitKey();
    return 0;
}

例子2

// cv2.cpp : Defines the entry point for the console application.
//
#include <opencv2/opencv.hpp>
    #include<iostream>
    #include<opencv2/opencv.hpp>  

    using namespace std;
    using namespace cv;  

    bool g_bDrawing = false;
    Point g_CurrPoint, g_OrgPoint;
    int g_nThick = 5, g_nBlue = 255, g_nGreen = 255, g_nRed = 0;
    int g_nImageOneValue = 49;
    Mat srcImage;
    Mat grayImage;
    Mat maskImage;  

    /*注意:不能在毁掉函数中写入未初始化的矩阵类,所以需要用时,需要写一个标志位,然后再在while(1)循环内使用*/
    void onMouse(int event, int x, int y, int flag, void *param)
    {
        Mat &img = *(cv::Mat*)param;  

        switch (event)
        {
            //移动鼠标的时候
        case CV_EVENT_MOUSEMOVE:
        {
                                   g_OrgPoint = g_CurrPoint;
                                   g_CurrPoint = Point(x, y);  

                                   if (g_bDrawing == 1)
                                   {
                                       line(srcImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick);
                                       imshow("【鼠标事件窗口】", srcImage);  

                                       //在掩膜图上进行显示
                                       line(maskImage, g_CurrPoint, g_OrgPoint, Scalar(g_nBlue, g_nGreen, g_nRed), g_nThick);
                                       imshow("【掩膜图像】", maskImage);
                                   }
        }
            break;
            //点击鼠标左键时
        case CV_EVENT_LBUTTONDOWN:
        {
                                     g_bDrawing = true;
                                     g_OrgPoint = Point(x, y);
                                     g_CurrPoint = g_OrgPoint;
        }
            break;
            //松开鼠标左键时
        case CV_EVENT_LBUTTONUP:
        {
                                   g_bDrawing = false;
        }
            break;
        }
    }  

    int main()
    {
        Mat tempImage;
        RNG &rng = theRNG();  

        srcImage = imread("f:\\img\\inp3.png");  

        //用一个变量来存储原图像
        Mat g_srcImage;
        srcImage.copyTo(g_srcImage);  

        //为掩膜图 分配空间
        maskImage.create(srcImage.size(), CV_8UC1);
        maskImage = Scalar::all(0);  

        namedWindow("【鼠标事件窗口】");
        setMouseCallback("【鼠标事件窗口】", onMouse, 0);  

        namedWindow("【滚动条窗口】", 0);
        createTrackbar("thick", "【滚动条窗口】", &g_nThick, 100, 0);
        createTrackbar("Blue", "【滚动条窗口】", &g_nBlue, 255, 0);
        createTrackbar("Green", "【滚动条窗口】", &g_nGreen, 255, 0);
        createTrackbar("Red", "【滚动条窗口】", &g_nRed, 255, 0);  

        char key;
        while (1)
        {
            imshow("【鼠标事件窗口】", srcImage);
            key = waitKey();
            if (key == 27)
                break;  

            //如果检测到 键值是1 则恢复原图
            if (key == '1')
            {
                g_srcImage.copyTo(srcImage);
                maskImage = Scalar::all(0);
                imshow("【鼠标事件窗口】", srcImage);
            }  

            //如果检测到空格 则开始执行图像修复
            Mat dstImage;
            dstImage.create(srcImage.size(), srcImage.type());
            if (key == ' ')
            {
                inpaint(srcImage, maskImage, dstImage, 3, INPAINT_TELEA);
                imshow("【修补后的图像】", dstImage);
            }
        }  

        return 0;
    }  
时间: 2024-11-07 18:14:35

cv1.1的相关文章

Oculus Home安装的一些坑和如何使用Oculus Rict CV1在unity中做开发。

Oculus Home的安装和如何使用Oculus Rift CV1在unity中开发使用 By:zscjob 最近因工作需要适配了一下Oculus CV1,真是踩坑无数.了解一下步骤, 1. 到oculus官方网站下载Oculus Home的Setup文件,目前官方下载地址:https://www.oculus.com/en-us/setup/.安装oculus Home 必须卸载之前的Oculus RunTime;如果你之前有安装过就需要卸载掉它, 2. 如果你下载链接打不开的话,你需要一个

cv1.5

使用均值漂移算法查找物体 #include <opencv2/opencv.hpp> using namespace cv; using namespace std; #include "Histogram1D.h" #include <iostream> #include <vector> #include "ContentFinder.h" #include "colorhistogram.h" int m

cv1.2

图像去噪声 添加高斯噪声 // cv2.cpp : Defines the entry point for the console application. // #include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; #define TWO_PI 6.2831853071795864769252866 double generateGaussianNois

cv1.0

环境 VS2010UltimTrial1.iso                    http://pan.baidu.com/s/1dEL85kl VS2010UltimTrialCHS版注册码    YCFHQ-9DWCY-DKV88-T2TMH-G7BHP opencv-2.4.9.exe                              http://pan.baidu.com/s/1kVaVwoR 图片地址:                                  

python作业:购物车(第二周)

一.作业需求: 1.启动程序后,输入用户名密码后,如果是第一次登录,让用户输入工资,然后打印商品列表 2.允许用户根据商品编号购买商品 3.用户选择商品后,检测余额是否够,够就直接扣款,不够就提醒 4.可随时退出,退出时,打印已购买商品和余额 5.在用户使用过程中, 关键输出,如余额,商品已加入购物车等消息,需高亮显示 6.用户下一次登录后,输入用户名密码,直接回到上次的状态,即上次消费的余额什么的还是那些,再次登录可继续购买 7.允许查询之前的消费记录 二.购物车用户信息: {'name':

036-多控制器管理(2)-iOS笔记

学习目标 1.[掌握]控制器的创建方式 2.[了解]导航控制器的介绍 3.[掌握]代码创建导航控制器 4.[掌握]storyboard中创建导航控制器 5.[理解]控制器的生命周期 一.控制器的创建方式 视图控制器就是用来控制或者说管理界面(视图)的,换句话说界面长得丑与美由视图控制器说的算.每个视图控制器 (UIViewController)都有一个View属性来描述界面长什么样,上一篇文章中提到的设置窗口的根控制器其实就是会把视图控制器的里 View会添加在窗口上进行显示. UIStoryb

《zw版&#183;Halcon-delphi系列原创教程》 酸奶自动分类脚本(机器学习、人工智能)

<zw版·Halcon-delphi系列原创教程> 酸奶自动分类脚本(机器学习.人工智能) Halcon强大的图像处理能力,令人往往会忽视其更加彪悍的机器学习.人工智能.      至少,目前国内.海外机器学习.人工智能方面的学者,没有几位重视这块.      国外,可能是版权问题,毕竟,Halcon是售价高达数万欧元(不是人民币)的商业软件,而且主要用于自控.机器视觉等工业领域,而不是大学.      国内,可能是对于Halcon的了解不够.      其实,图像处理的核心,图像识别.分类,

Android的SQLite基本操作

涉及SQLite的增删改查,结果用log显示 package com.example.sqlconnecttest; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; im

[强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!)

原文:[强烈推荐]ORACLE PL/SQL编程详解之七:程序包的创建与应用(聪明在于学习,天才在于积累!) [强烈推荐]ORACLE PL/SQL编程详解之七: 程序包的创建与应用(聪明在于学习,天才在于积累!) ——通过知识共享树立个人品牌.   继上七篇:            [推荐]ORACLE PL/SQL编程详解之一:PL/SQL 程序设计简介(千里之行,始于足下)            [顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之