作者 群号 C语言交流中心 240137450 微信 15013593099
分水岭算法实现分割
分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭。分水岭的概念和形成可以通过模拟浸入过程来说明。在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭。
分水岭算法一般和区域生长法或聚类分析法相结合。
分水岭算法一般用于分割感兴趣的图像区域,应用如细胞边界的分割,分割出相片中的头像等等。
// 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到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;
}
直线拟合
随机点直线拟合
#include "stdafx.h"
#include "opencv\cv.h"
#include "opencv\highgui.h"
#include <math.h>
int _tmain(int argc, _TCHAR* argv[])
{
IplImage* img = cvCreateImage( cvSize( 500, 500 ), 8, 3 );
CvRNG rng = cvRNG(-1); //cvRNG()跟一般的C语言srand()使用方法一样,要先给它一个种子,
//但srand()用到的是unsigned int的32位种子范围,而cvRNG()用的是64位长整数种子。
//初始化CvRNG资料结构,假如seed给0,它将会自动转成-1 cvRNG(64位种子)
cvNamedWindow( "fitline", 1 );
for(;;)
{
char key;
int i;
int count = cvRandInt(&rng)%100 + 1; //产生1-100 之间的数
int outliers = count/5; // 奇异点的个数。0--20 之间的数
printf("count = %d", count);
float a = cvRandReal(&rng)*200; // 0~ 199 之间的浮点数 [cvRandReal 浮点型随机数并更新 RNG ,范围在 0..1 之间,不包括 。
float b = cvRandReal(&rng)*40; //返回0 ~ 39之间的数
float angle = cvRandReal(&rng)*CV_PI;
printf("count = %f", angle);
float cos_a = cos(angle), sin_a = sin(angle);
printf("cos_a = %f", cos_a);
CvPoint pt1, pt2; //直线的两个端点
CvPoint* points = (CvPoint*)malloc( count * sizeof(points[0])); //存放随机产生的点点,数目为count
CvMat pointMat = cvMat( 1, count, CV_32SC2, points ); //点集, 存储count个随机点points
float line[4]; //输出的直线参数。2D 拟合情况下,它是包含 4 个浮点数的数组 (vx, vy, x0, y0)
//其中 (vx, vy) 是线的单位向量而 (x0, y0) 是线上的某个点
float d, t;
b = MIN(a*0.3, b);
// generate some points that are close to the line
for( i = 0; i < count - outliers; i++ )
{
float x = (cvRandReal(&rng)*2-1)*a;
float y = (cvRandReal(&rng)*2-1)*b;
points[i].x = cvRound(x*cos_a - y*sin_a + img->width/2);
points[i].y = cvRound(x*sin_a + y*cos_a + img->height/2);
}
// generate "completely off" points
for( ; i < count; i++ )
{
points[i].x = cvRandInt(&rng) % img->width;
points[i].y = cvRandInt(&rng) % img->height;
}
// find the optimal line 曲线拟合
cvFitLine( &pointMat, CV_DIST_L1, 1, 0.001, 0.001, line );
cvZero( img );
//画出产生的随机分布的点点
for( i = 0; i < count; i++ )
cvCircle( img, points[i], 2, i < count - outliers ? CV_RGB(255, 0, 0) :CV_RGB(255,255,0), CV_FILLED, CV_AA, 0 );
// ... and the long enough line to cross the whole image
d = sqrt((double)line[0]*line[0] + (double)line[1]*line[1]); //line[0 & 1]存储的是单位向量,所以d=1
//printf("\n %f\n", d);
line[0] /= d;
line[1] /= d;
//画出线段的两个端点(避免线太短,以线上一个随机点向两侧延伸line[0]*t )
t = (float)(img->width + img->height) ;
pt1.x = cvRound(line[2] - line[0]*t);
pt1.y = cvRound(line[3] - line[1]*t);
pt2.x = cvRound(line[2] + line[0]*t);
pt2.y = cvRound(line[3] + line[1]*t);
cvLine( img, pt1, pt2, CV_RGB(0,255,0), 3, CV_AA, 0 );
cvShowImage( "fitline", img );
key = (char) cvWaitKey(0);
if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'
break;
free( points );
}
cvDestroyWindow( "fitline" );
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清空
}
}
}
圆拟合
根据圆找mark
#include "stdafx.h"
#include <opencv\\cv.h>
#include <opencv\\highgui.h>
#include <opencv\\cxcore.h>
#include <iostream>
using namespace cv;
using namespace std;
#define DEBUG
double matchShapes(IplImage* src, IplImage* tmplt);
CvPoint matchTemplate(IplImage* src, IplImage* tmplt);
void L_TemplateMatch(IplImage* src,IplImage* tmp);
int _tmain(int argc, _TCHAR* argv[])
{
cvNamedWindow("tmplt", 1);
cvNamedWindow("src", 1);
IplImage* tmplt = cvLoadImage("f:\\img\\mark.png", 1);
IplImage* pcbmark = cvLoadImage("f:\\img\\model.jpg", 1);
// IplImage* Rg=cvCreateImage(cvGetSize(pcbmark),8,3);
//cvCvtColor(pcbmark,Rg,CV_GRAY2BGR);
double t=(double)getTickCount();
// cvWaitKey(1000);
//cvShowImage("BGR", Rg);
cvShowImage("src", pcbmark);
cvShowImage("tmplt", tmplt);
CvPoint center = matchTemplate(pcbmark, tmplt);
t=((double)getTickCount()-t)/getTickFrequency();
cout<<t<<endl;
cvWaitKey();
cvDestroyWindow("tmplt");
cvDestroyWindow("src");
return 0;
}
CvPoint matchTemplate(IplImage* src, IplImage* tmplt)
{
CvPoint center;
CvSize sizeSrc = cvGetSize(src);
CvSize sizeTemp = cvGetSize(tmplt);
CvSize sizeResult = cvSize(sizeSrc.width-sizeTemp.width+1,sizeSrc.height-sizeTemp.height+1);
IplImage* imgResult = cvCreateImage(sizeResult,IPL_DEPTH_32F,1);
cvMatchTemplate(src,tmplt,imgResult,CV_TM_CCORR_NORMED);
// a=cvMatchShapes(src,tmplt,CV_CONTOURS_MATCH_I3);
float dMax = 0.;
CvPoint point = cvPoint(0,0);
double min_val;
double max_val;
CvPoint pt1;
CvPoint pt2;
CvPoint min_loc;
CvPoint max_loc;
cvMinMaxLoc(imgResult,&min_val,&max_val,&min_loc,&max_loc,NULL);
CvRect rect=cvRect(max_loc.x,max_loc.y,tmplt->width,tmplt->height);
pt1=cvPoint(rect.x,rect.y);
pt2=cvPoint(rect.x+rect.width,rect.y+rect.height);
cvRectangle(src,pt1,pt2,cvScalar(255));
center.x = fabs((double)(pt1.x + pt2.x)/2);
center.y = fabs((double)(pt1.y + pt2.y)/2);
cvNamedWindow( "Test", CV_WINDOW_AUTOSIZE );
cvShowImage("Test",src);
cvWaitKey();
cvDestroyWindow("Test");
return center;
}
椭圆找mark点
// FitCircle.cpp : 定义控制台应用程序的入口
#include "opencv\cv.h"
#include "opencv\highgui.h"
#include "opencv\cxcore.h"
#include "opencv\cvaux.h"
#include <iostream>
using namespace cv;
using namespace std;
void main()
{
int BasicGlobalThreshold(int*pg,int start,int end);
CvBox2D findRectContours(IplImage *src);
IplImage* imgGrey=cvLoadImage("f:\\img\\model.jpg",0);
//IplImage* imgGrey=cvLoadImage("28027.jpg",0);
cvNamedWindow("原始图像");
//cvShowImage("fa",imgGrey);
cvShowImage("原始图像",imgGrey);
double t=(double)getTickCount();
IplImage* imgBasicGlobalThreshold = cvCreateImage(cvGetSize(imgGrey),IPL_DEPTH_8U,1);
cvCopyImage(imgGrey,imgBasicGlobalThreshold);
int pg[256],i,thre;
for (i=0;i<256;i++) pg[i]=0;
for (i=0;i<imgBasicGlobalThreshold->imageSize;i++) // 直方图统计
pg[(unsigned char)imgBasicGlobalThreshold->imageData[i]]++;
thre = BasicGlobalThreshold(pg,0,256); // 确定阈值
cout<<"The Threshold of this Image in BasicGlobalThreshold is:"<<thre<<endl;//输出显示阀值
cvThreshold(imgBasicGlobalThreshold,imgBasicGlobalThreshold,thre,255,CV_THRESH_BINARY); // 二值化
cvNamedWindow("二值图像");
cvShowImage("二值图像",imgBasicGlobalThreshold);
CvBox2D box=findRectContours(imgBasicGlobalThreshold);
t=((double)getTickCount()-t)/getTickFrequency();
cout<<t<<" mark点坐标"<<box.center.x<<"\t"<<box.center.y<<endl<<"半径"<<box.size.width<<endl;
cvWaitKey(0);
}
CvBox2D findRectContours(IplImage *src)
{
CvBox2D box1;
IplImage* des=cvCreateImage(cvGetSize(src),src->depth,src->nChannels);
cvZero(des);
CvMemStorage* memory=cvCreateMemStorage(0);
CvSeq* Icontour=NULL;
CvSeq* maxContour =NULL;
cvShowImage("原始图像1",src);
cvFindContours(src,memory,&Icontour, sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));
double area=0;
double maxArea=0;
while(Icontour)
{
area=fabs(cvContourArea(Icontour,CV_WHOLE_SEQ));
//cout<<area<<endl;
//cvDrawContours(src, Icontour,
// CV_RGB(255,255,255), CV_RGB(255, 255,255),
// 0, 1, 8, cvPoint(0,0));
//
if(area>500 && area<20000)
{
goto l1;
}
else
goto l2;
l1: CvBox2D box0=cvFitEllipse2(Icontour);
float a=(float)box0.size.height/(float)box0.size.width;
//cout<<area<<endl;
if(fabs(a)<=1.5)
{
cvDrawContours(des, Icontour,
CV_RGB(255,255,255), CV_RGB(255, 255,255), 0, 1, 8, cvPoint(0,0));
box1=box0;
cvDrawCircle(des,cvPoint(box0.center.x,box0.center.y),1,cvScalar(255,255,255),2,8,0);
//cvDrawContours(des,maxContour,cvScalar(0,0,255),cvScalar(0,0,255),1,1,0,cvPoint(0,0));
}
// maxContour = Icontour;
l2: Icontour =Icontour->h_next;
}
cvShowImage("fds",des);
return box1;
}
/*============================================================================
= 代码内容:基本全局阈值法 迭代法
==============================================================================*/
int BasicGlobalThreshold(int*pg,int start,int end)
{
// 基本全局阈值法
int i,t,t1,t2,k1,k2;
double u,u1,u2;
t=0;
u=0;
for (i=start;i<end;i++)
{
t+=pg[i];
u+=i*pg[i];
}
k2=(int) (u/t); // 计算此范围灰度的平均值
do
{
k1=k2;
t1=0;
u1=0;
for (i=start;i<=k1;i++)
{ // 计算低灰度组的累加和
t1+=pg[i];
u1+=i*pg[i];
}
t2=t-t1;
u2=u-u1;
if (t1)
u1=u1/t1; // 计算低灰度组的平均值
else
u1=0;
if (t2)
u2=u2/t2; // 计算高灰度组的平均值
else
u2=0;
k2=(int) ((u1+u2)/2); // 得到新的阈值估计值
}
while(k1!=k2); // 数据未稳定,继续
//cout<<"The Threshold of this Image in BasicGlobalThreshold is:"<<k1<<endl;
return(k1); // 返回阈值
}
模板匹配找mark
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <iostream>
using namespace cv;
using namespace std;
#define DEBUG
double matchShapes(IplImage* src, IplImage* tmplt);
CvPoint matchTemplate(IplImage* src, IplImage* tmplt);
void L_TemplateMatch(IplImage* src,IplImage* tmp);
int main(int argc, char* argv[])
{
cvNamedWindow("tmplt", 1);
cvNamedWindow("src", 1);
IplImage* pcbmark = cvLoadImage("f:\\img\\mark.png", 1);
IplImage* tmplt = cvLoadImage("f:\\img\\model.jpg", 1);
// IplImage* Rg=cvCreateImage(cvGetSize(pcbmark),8,3);
//cvCvtColor(pcbmark,Rg,CV_GRAY2BGR);
double t=(double)getTickCount();
// cvWaitKey(1000);
#ifdef DEBUG
//cvShowImage("BGR", Rg);
cvShowImage("src", pcbmark);
cvShowImage("tmplt", tmplt);
cvWaitKey();
#endif
CvPoint center = matchTemplate(pcbmark, tmplt);
t=((double)getTickCount()-t)/getTickFrequency();
cout<<t<<endl;
cvDestroyWindow("tmplt");
cvDestroyWindow("src");
return 0;
}
CvPoint matchTemplate(IplImage* src, IplImage* tmplt)
{
CvPoint center;
CvSize sizeSrc = cvGetSize(src);
CvSize sizeTemp = cvGetSize(tmplt);
CvSize sizeResult = cvSize(sizeSrc.width-sizeTemp.width+1,sizeSrc.height-sizeTemp.height+1);
IplImage* imgResult = cvCreateImage(sizeResult,IPL_DEPTH_32F,1);
cvMatchTemplate(src,tmplt,imgResult,CV_TM_CCORR_NORMED);
// a=cvMatchShapes(src,tmplt,CV_CONTOURS_MATCH_I3);
float dMax = 0.;
CvPoint point = cvPoint(0,0);
//for (int cx=0 ; cx<sizeResult.width; cx++)
//{
// for (int cy=0 ; cy<sizeResult.height; cy++)
// {
// float fTemp = CV_IMAGE_ELEM(imgResult,float,cy,cx);
// if (dMax < fTemp) //找到最接近的位置
// {
// dMax = fTemp;
// point = cvPoint(cx,cy); //记录位置
// }
// }
//}
//CvPoint point2 = cvPoint(point.x+sizeTemp.width,point.y+sizeTemp.height); //对角位置
//cvRectangle(src,point,point2,cvScalar(255));
//center.x = fabs((double)(point.x + point2.x)/2);
//center.y = fabs((double)(point.y + point2.y)/2);
double min_val;
double max_val;
CvPoint pt1;
CvPoint pt2;
CvPoint min_loc;
CvPoint max_loc;
cvMinMaxLoc(imgResult,&min_val,&max_val,&min_loc,&max_loc,NULL);
CvRect rect=cvRect(max_loc.x,max_loc.y,tmplt->width,tmplt->height);
pt1=cvPoint(rect.x,rect.y);
pt2=cvPoint(rect.x+rect.width,rect.y+rect.height);
cvRectangle(src,pt1,pt2,cvScalar(255));
center.x = fabs((double)(pt1.x + pt2.x)/2);
center.y = fabs((double)(pt1.y + pt2.y)/2);
cvNamedWindow( "Test", CV_WINDOW_AUTOSIZE );
cvShowImage("Test",src);
cvWaitKey();
cvDestroyWindow("Test");
return center;
}
椭圆拟合
例子1
#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\\el.png";
if ((image03 = cvLoadImage(filename,0))==0)//读入图像为灰度图像
{
return -1;
}
image02 = cvCloneImage(image03);
image04 = cvCloneImage(image03);
cvNamedWindow("Source",1);
cvNamedWindow("Result",1);
cvShowImage("Source",image03);
cvCreateTrackbar("Threshold","Result",&slider_pos,255,process_image);
process_image(0);
cvWaitKey(0);
cvSaveImage("1.jpg",image04);
cvReleaseImage(&image02);
cvReleaseImage(&image03);
cvDestroyWindow("Source");
cvDestroyWindow("Result");
return 0;
}
//这个函数寻找出轮廓、用椭圆拟合画出
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));
cvZero(image02);
cvZero(image04);
//绘制所有轮廓并用椭圆拟合
for (;cont;cont = cont ->h_next)
{
int i;
int count= cont->total;//轮廓个数
CvPoint center;
CvSize size;
/*个数必须大于6,这是cvFitEllipse_32f的要求*/
if (count<6)
{
continue;
}
//分配内存给点集
PointArray = (CvPoint *)malloc(count*sizeof(CvPoint));
PointArray2D32f = (CvPoint2D32f*)malloc(count*sizeof(CvPoint2D32f));
//分配内存给椭圆数据
box = (CvBox2D32f *)malloc(sizeof(CvBox2D32f));
//得到点集(这个方法值得借鉴)
cvCvtSeqToArray(cont,PointArray,CV_WHOLE_SEQ);
//将CvPoint点集转化为CvBox2D32f集合
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));
//将椭圆数据从浮点转化为整数表示
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;
//画椭圆
cvEllipse(image04,center,size,box->angle,0,360,CV_RGB(0,0,255),1,CV_AA,0);
free(PointArray);
free(PointArray2D32f);
free(box);
}
cvShowImage("Result",image04);
}
例子2 多边形找交点坐标
//opencv版本2.0.
//先用cvFindContours提取轮廓,再用cvApproxPoly拟合轮廓,就可以得到多边形的顶点
#include "opencv//cv.h"
#include "opencv//highgui.h"
//#include "cv"
void GetCouner()
{
char *pszImgPath = "f:\\img\\pp.jpg";
IplImage* pImg = NULL; //声明IplImage指针
//载入图像
if( (pImg = cvLoadImage( pszImgPath, 0)) != 0 )//[[此处的argc==2是否需要改成argc==1?我改了之后才能运行成功。求大牛解惑]] // wmzzzz : 在"属性"|"debug"|里的command arguments 里加入参数(一个路径:要打开的文件路径) 这时 argc==2 就合理了...可以试试多加几个
{
IplImage* pResImg = cvCreateImage(cvGetSize(pImg), 8, 1);
cvZero(pResImg);
CvMemStorage * storage = cvCreateMemStorage(0);
CvSeq * contour = 0;
int mode = CV_RETR_EXTERNAL;
cvFindContours(pImg/*pCannyImg*/, storage, &contour, sizeof(CvContour),
mode, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
cvDrawContours( pResImg, contour, CV_RGB(255,255,255), CV_RGB(255,255,255), 1, 1, CV_AA, cvPoint(0,0) );
//cvShowImage( "pResImg", pResImg );//显示图像
//cvWaitKey(0); //等待按键
contour = cvApproxPoly( contour, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 );
//cvZero(pResImg);
for(int i = 0; i < contour ->total; i++) // 提取一个轮廓的所有坐标点
{
CvPoint *pt = (CvPoint*) cvGetSeqElem(contour, i); // 得到一个轮廓中一个点的函数cvGetSeqElem
cvCircle(pResImg, *pt, 3, cvScalar(255,255,255), 1);
}
cvShowImage( "Image", pImg );//显示图像
cvShowImage( "pResImg", pResImg );//显示图像
cvWaitKey(0); //等待按键
cvClearSeq(contour);
cvReleaseMemStorage(&storage);
cvReleaseImage( &pImg ); //释放图像
cvReleaseImage(&pResImg);
return;
}
}
int main( int argc, char** argv )
{
GetCouner();
return 0;
}
例子2
#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;
}
填充
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
int main(int argc, char **argv)
{
IplImage * pSrcImg= cvLoadImage("f:\\img\\1.jpg");
IplImage * pGrayImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pThresholdImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pDstImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,3);
cvSetZero(pDstImg);
srand((int)time(0));
CvSeq * contours = 0;
CvMemStorage * storage=cvCreateMemStorage(0);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvCvtColor(pSrcImg,pGrayImg,CV_BGR2GRAY);
cvNot(pGrayImg,pGrayImg);
cvThreshold(pGrayImg,pThresholdImg,100,255,CV_THRESH_BINARY);
cvSet(pDstImg,color);
CvContourScanner scanner = cvStartFindContours(pThresholdImg, storage,
sizeof(CvContour),CV_RETR_TREE ,CV_CHAIN_APPROX_NONE);
while (contours=cvFindNextContour(scanner))
{
color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvDrawContours(pDstImg, contours, color,color, 0,CV_FILLED);
};
contours= cvEndFindContours(&scanner);
cvShowImage("dst",pDstImg);
cvWaitKey(0);
cvSaveImage("dst.jpg",pDstImg);
cvReleaseImage(&pDstImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pThresholdImg);
cvReleaseMemStorage(&storage);
return 0;
}
比较图像像素
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
int main( int argc, char** argv )
{
IplImage* pImg,*pImg2; //声明IplImage指针
int row,col,row1,col1;
int sign;
int i=0;
if( argc == 3 && (pImg = cvLoadImage( "f:\\img\\1.bmp", 1)) != 0 && (pImg2 = cvLoadImage( "f:\\img\\2.bmp", 1)) != 0)
{
cvNamedWindow( "Image", CV_WINDOW_AUTOSIZE ); //创建窗口
cvShowImage( "Image", pImg ); //显示图像
cvNamedWindow( "Image2", CV_WINDOW_AUTOSIZE ); //创建窗口
cvShowImage( "Image2", pImg2 ); //显示图像
for(i=0;i<9;i++)
{
for(row1=row=(i/3)*(pImg->height/3);row<row1+(pImg->height/3);row++)
{
uchar* ptr=(uchar*)(pImg->imageData+row*pImg->widthStep);
uchar* ptr2=(uchar*)(pImg2->imageData+row*pImg2->widthStep);
sign=0;
for(col1=col=(i%3)*(pImg->width/3);col<col1+(pImg->width/3);col++)
{
if(ptr[3*col]!=ptr2[3*col]||ptr[3*col+1]!=ptr2[3*col+1]||ptr[3*col+2]!=ptr2[3*col+2])
{
sign=i;
continue;
}
}
if(sign!=0)
{
continue;
}
}
if(sign!=0)
{
printf("the %d picture is the same!",sign);
}
}
// if(row==pImg->height&&row==pImg2->height)printf("two pictures are the same!");
// else printf("two pictures are different!");
cvWaitKey(0); //等待按键
cvDestroyWindow( "Image" );//销毁窗口
cvReleaseImage( &pImg ); //释放图像
cvDestroyWindow( "Image2" );//销毁窗口
cvReleaseImage( &pImg2 ); //释放图像
return 0;
}
return -1;
}