opencv实现图像分割,分离前景和背景(2)

简介

  如题,本篇是在前一篇的基础上进一步讲解的第三个图像背景分离例子。

实例介绍

  这个例子是在上一个加入鼠标操作实例的进一步操作。
  本例:可以在鼠标选框完成之后,1、通过shift+鼠标右键来选择设置图像对应位置为前景。
                                  2、通过ctrl +鼠标右键来选择设置图像对应位置为背景景。
                                  3、按下键值‘n’,进行图像背景分离计算,并显示结果。
                                  4、按下键值‘esc’,退出程序。

实例讲解

具体代码

#include "opencv2/highgui/highgui.hpp"                                                                                               
#include "opencv2/imgproc/imgproc.hpp"
#include "stdio.h"
 
#include <iostream>
 
using namespace std;
using namespace cv; 
 
string filename;
char filename_tmp[10] = "tmp.jpg";
Mat image;
string winName = "show";
enum{NOT_SET = 0, IN_PROCESS = 1, SET = 2};
enum{LS_LEFT = 0, LS_RIGHT = 1, LS_NONE = 2};
uchar rectState, mouse_flag;
Rect rect;
Mat mask;
const Scalar GREEN = Scalar(0,255,0);
const Scalar RED = Scalar(0,0,255);
const Scalar BLUE = Scalar(255,0,0);
Mat bgdModel, fgdModel;
int line_count[4];
const int BGD_KEY = CV_EVENT_FLAG_CTRLKEY;
const int FGD_KEY = CV_EVENT_FLAG_SHIFTKEY;
int i, j;
 
void setRectInMask(){
	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);
 
}
 
static void getBinMask( const Mat& comMask, Mat& binMask ){
	binMask.create( comMask.size(), CV_8UC1 );
	binMask = comMask & 1;
}
 
void on_mouse( int event, int x, int y, int flags, void* )
{
	switch( event ){
		case CV_EVENT_LBUTTONDOWN:
			mouse_flag = LS_LEFT;
			if( rectState == NOT_SET){
				rectState = IN_PROCESS;
				rect = Rect( x, y, 1, 1 );
			}
			break;
		case CV_EVENT_LBUTTONUP:
			if( rectState == IN_PROCESS ){
				rect = Rect( Point(rect.x, rect.y), Point(x,y) );
				rectState = SET;
				(mask(rect)).setTo( Scalar(GC_PR_FGD));
			}
			break;
		case CV_EVENT_RBUTTONDOWN:
			mouse_flag = LS_RIGHT;
			line_count[0] = x;
			line_count[1] = y;
			break;
		case CV_EVENT_RBUTTONUP:
			mouse_flag = LS_NONE;
			line_count[0] = 0;
			line_count[1] = 0;
			line_count[2] = 0;
			line_count[3] = 0;
			imwrite(filename_tmp,image);
			break;
		case CV_EVENT_MOUSEMOVE:
			if(mouse_flag == LS_LEFT){
				if( rectState == IN_PROCESS ){
					rect = Rect( Point(rect.x, rect.y), Point(x,y) );
					image = imread(filename_tmp, 1 );
					rectangle(image, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);
					imshow(winName, image);
				}
			}else if(mouse_flag == LS_RIGHT){
				IplImage pI = image;
				IplImage pI_2 = mask;
				line_count[2] = x;
				line_count[3] = y;
				if((flags & BGD_KEY) != 0){
					cvLine(&pI, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), RED, 5);
					cvLine(&pI_2, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), cvScalar(0,0,0), 5);
				}else if((flags & FGD_KEY) != 0){
					cvLine(&pI, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), BLUE, 5);
					cvLine(&pI_2, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), cvScalar(1,0,0), 5);
				}
				line_count[0] = x;
				line_count[1] = y;
				imshow(winName, image);
			}
			break;
	}
}
 
int main(int argc, char* argv[]){
	Mat res;
	Mat binMask;
 
	filename = argv[1];
	image = imread( filename, 1 );
	imshow(winName, image);
	imwrite(filename_tmp,image);
	mask.create(image.size(), CV_8UC1);
	rectState = NOT_SET;
	mask.setTo(GC_BGD);
 
	setMouseCallback(winName, on_mouse, 0);
	while(1){
		int c = waitKey(0);
		if(c == '\x1b'){
			break;
		}else if(c == 'n'){
			image = imread(filename, 1 );
			grabCut(image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);
			getBinMask( mask, binMask );
			image.copyTo(res, binMask );
			imshow("11", res);
		}
	}
 
	return 0;
}

代码讲解

  和前面实例1和实例2相同的代码部分,不在做讲解。只讲在之基础上新加入的部分。
鼠标右键响应
  加入了鼠标右键+键值ctrl、shift的组合操作。
  1、鼠标左键按下时候,记录下当前坐标,并且设置当前模式为LS_RIGHT(前景背景设置模式)
   case CV_EVENT_RBUTTONDOWN:
	mouse_flag = LS_RIGHT;
	line_count[0] = x;
	line_count[1] = y;
	break;
  2、当鼠标右键+shift按下,并拖动鼠标的时候,在图像上绘制出鼠标移动的蓝色线条轨迹,同时在掩码mask上,对应轨迹位置标注为前景(GC_FGD)。
       当鼠标右键+Ctrl 按下,并拖动鼠标的时候,在图像上绘制出鼠标移动的红色线条轨迹,同时在掩码mask上,对应轨迹位置标注为前景(GC_BGD)。
          case CV_EVENT_MOUSEMOVE:
		........
		}else if(mouse_flag == LS_RIGHT){
			IplImage pI = image;
			IplImage pI_2 = mask;
			line_count[2] = x;
			line_count[3] = y;
			if((flags & BGD_KEY) != 0){
				cvLine(&pI, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), RED, 5);
				cvLine(&pI_2, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), cvScalar(0,0,0), 5);
			}else if((flags & FGD_KEY) != 0){
				cvLine(&pI, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), BLUE, 5);
				cvLine(&pI_2, Point(line_count[0], line_count[1]), Point(line_count[2], line_count[3]), cvScalar(1,0,0), 5);
			}
			line_count[0] = x;
			line_count[1] = y;
			imshow(winName, image);
		}
		break;
   3、当鼠标右键抬起的时候,清除掉一些前景背景操作中的临时变量。
      case CV_EVENT_RBUTTONUP:
	   mouse_flag = LS_NONE;
	   line_count[0] = 0;
	   line_count[1] = 0;
	   line_count[2] = 0;
	   line_count[3] = 0;
	   imwrite(filename_tmp,image);
	   break;
键盘响应
  加入了两个键值的响应操作:1、esc:直接退出程序。
                             2、‘n’:进行图像背景分离计算,并显示结果。
        while(1){
		int c = waitKey(0);
		if(c == '\x1b'){
			break;
		}else if(c == 'n'){
			image = imread(filename, 1 );
			grabCut(image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK);
			getBinMask( mask, binMask );
			image.copyTo(res, binMask );
			imshow("11", res);
		}
          }

效果演示

  特别注意:本实例使用时候,需要首先鼠标左键画出背景分离矩形框,然后鼠标右键选择的自定义前景背景才能正常时候。之后按下n键,
计算背景分离并显示结果。
  运行效果:
      原图像:
      
      
      结果图像:
      
k-means算法是一种常用的无监督学习算法,用于将数据集划分为K个不同的簇。在彩色图像前景背景分割中,可以使用k-means算法将图像中的像素点分为前景背景两个簇。 以下是一个基于Matlab的k-means彩色图像前景背景分割的简单代码示例: ```matlab % 读取彩色图像 image = imread('image.jpg'); % 转换图像为Lab颜色空间 lab_image = rgb2lab(image); % 从Lab颜色空间中提取a和b通道 ab_channels = lab_image(:, :, 2:3); % 扁平化ab通道,仅保留两个通道的像素信息 ab_channels = reshape(ab_channels, [], 2); % 颜色通道标准化 ab_channels = double(ab_channels) / 255; % 使用k-means算法将像素点分为两个簇,即前景背景 % 这里我们取K=2 k = 2; [cluster_idx, ~] = kmeans(ab_channels, k); % 将像素点重新组织为原始图像的大小 segmented_image = reshape(cluster_idx, size(lab_image, 1), size(lab_image, 2)); % 显示分割结果 figure; subplot(1, 2, 1); imshow(image); title('原始图像'); subplot(1, 2, 2); imagesc(segmented_image); title('分割结果'); colormap jet; % 显示分割结果的前景背景 foreground = segmented_image == 2; background = segmented_image == 1; figure; subplot(1, 3, 1); imshow(image); title('原始图像'); subplot(1, 3, 2); imshow(foreground); title('前景'); subplot(1, 3, 3); imshow(background); title('背景'); ``` 这段代码首先读取彩色图像,并将其转换为Lab颜色空间。然后,将a和b通道从Lab图像中提取出来,并将其扁平化为一个Nx2的矩阵。然后,利用k-means算法将这些像素点分为两个簇,即前景背景。最后,将分割结果重新组织为原始图像的大小,并将其显示出来。同时,将分割结果中的前景背景提取出来,并显示在单独的图像中。 这只是一个简单的示例,实际应用中可能需要对图像进行预处理、调整算法参数以及进行后处理等步骤,以提高分割的准确性和鲁棒性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值