数字图像处理(DIP)大作业 基于OpenCV和ros的人腿跟踪
数字图像处理课程相关文章 传送门
https://blog.csdn.net/qq_46164507/article/details/122503851
博文说明
本文所使用代码或多或少参考了以往博文的同类or相似文章的代码,并非纯原创
本文仅用于记录并提供一种代码思路,供大家参考
文章目录
- 数字图像处理(DIP)大作业 基于OpenCV和ros的人腿跟踪
-
文章正文
实验要求
我们组选择的是第二个项目:人腿识别跟踪
解决思路以及算法介绍
- 1、 采用k-means算法,实现提取(切换)图像主色(即人腿特征颜色)功能
原理介绍:在RGB域中设置固定数目的聚类中心,经过k-means聚类,实现色域分割,并提取当前图像的颜色最多的区域,认为是人的腿部特征(裤子颜色)。 - 2、采用形态学处理的算法,实现找到固定HSV/RGB范围颜色的色域中最大连通域的中心及宽度功能
原理介绍:gamma变换(增亮图像)-高斯滤波(滤除图像坏点、平滑图像)-HSV/RGB提取(提取固定颜色范围的图像)-二值化图像-开运算(去除小块噪声、打断连通域)-提取最大连通域(找到目标人腿位置)-绘制最小外接矩形(提取人腿相对于相机的位置特征(距离、左右位置))。 - 3、设计非线性控制器,实现让小车跟踪人腿的运动控制功能
原理介绍:以最小外接矩形的宽度(代表距离信息)和中心点水平位置(代表角度信息)为控制量,分别控制机器人运动的线速度和角速度分量,实现精确的运动控制。 - 4、设计键盘检测程序,实现半自动切换跟踪目标功能
原理介绍:用户在进程内敲击键盘任意键,程序会自动切换运行模式(目标检测模式-目标跟踪模式)。
实验代码
运行环境:Ubuntu16.04 LTS + OpenCV 3.0.4 + ROS-kinetic-full
代码语言:c++
#include <stdlib.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <cv.h>
#include <highgui.h>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include "ros/ros.h"
#include <termios.h>
#include <boost/thread.hpp>
#include <geometry_msgs/Twist.h>
#include <math.h>
using namespace cv;
using namespace std;
static struct termios initial_settings, new_settings;
static int peek_character = -1;
void init_keyboard()
{
tcgetattr(0,&initial_settings);
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON;
new_settings.c_lflag &= ~ECHO;
new_settings.c_lflag &= ~ISIG;
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
tcsetattr(0, TCSANOW, &new_settings);
}
void close_keyboard()
{
tcsetattr(0, TCSANOW, &initial_settings);
}
int kbhit()
{
char ch;
int nread;
if(peek_character != -1)
return 1;
new_settings.c_cc[VMIN]=0;
tcsetattr(0, TCSANOW, &new_settings);
nread = read(0,&ch,1);
new_settings.c_cc[VMIN]=1;
tcsetattr(0, TCSANOW, &new_settings);
if(nread == 1) {
peek_character = ch;
return 1;
}
return 0;
}
void GammaCorrection(Mat src, Mat &dst, float fGamma)
{
unsigned char lut[256];
for( int i = 0; i < 256; i++ )
{
lut[i] = saturate_cast<uchar>(pow((float)(i/255.0), fGamma) * 255.0f);
}
dst = src.clone();
const int channels = dst.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++ )
*it = lut[(*it)];
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++ )
{
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}
Mat DoKmeans(Mat src)
{
Mat gammad = src.clone();
GammaCorrection(src,gammad,1/1.6);
int width = src.cols;
int height = src.rows;
int dims = src.channels();
int sampleCount = width * height;
int clusterCount = 8;
cv::Mat Points(sampleCount,dims,CV_32F,cv::Scalar(10));
cv::Mat labels;
cv::Mat centers(clusterCount,1,Points.type());
for (int row= 0;row<height;row++) {
for (int col = 0;col <width; col++) {
int index = row * width + col;
cv::Vec3b bgr =gammad.at<cv::Vec3b>(row,col);
Points.at<float>(index,0)=static_cast<int>(bgr[0]);
Points.at<float>(index,1)=static_cast<int>(bgr[1]);
Points.at<float>(index,2)=static_cast<int>(bgr[2]);
}
}
cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::COUNT,10,0.1);
cv::kmeans(Points, clusterCount,labels, criteria,3,cv::KMEANS_PP_CENTERS,centers);
int labelamount[clusterCount]={0};
cv::Mat result =cv::Mat::zeros(src.size(),src.type());
for (int row = 0;row < height;row++) {
for (int col = 0; col < width; col++) {
int index =row * width + col;
int label = labels.at<int>(index,0);
labelamount[label]=labelamount[label]+1;
result. at<cv::Vec3b>(row,col)[0] = centers.at<float>(label,0);
result. at<cv::Vec3b>(row,col)[1] = centers.at<float>(label,1);
result. at<cv::Vec3b>(row,col)[2] = centers.at<float>(label,2);
}
}
int maxlabeli = 0;
int maxlabelamount = 0;
for (int i = 0; i < clusterCount; i++)
{
if (labelamount[i] > maxlabelamount)
{
maxlabeli = i;
maxlabelamount = labelamount[i];
}
}
for (int row = 0;row < height;row++) {
for (int col = 0; col < width; col++) {
int index =row * width + col;
if (labels.at<int>(index,0) == maxlabeli)
{
result.at<cv::Vec3b>(row,col)[0] = 0;
result.at<cv::Vec3b>(row,col)[1] = 255;
result.at<cv::Vec3b>(row,col)[2] = 0;
}
}
}
cv::Mat RGBSum =cv::Mat::zeros(1,dims,CV_32F);
for (int row = 0;row < height;row++) {
for (int col = 0; col < width; col++) {
int index =row * width + col;
if (labels.at<int>(index,0) == maxlabeli)
{
RGBSum.at<float>(0,0) += Points.at<float>(index,0);
RGBSum.at<float>(0,1) += Points.at<float>(index,1);
RGBSum.at<float>(0,2) += Points.at<float>(index,2);
}
}
}
Mat RGBAverage = RGBSum / maxlabelamount;
cv::Mat RGBSigma2 =cv::Mat::zeros(1,dims,CV_32F);
for (int row = 0;row < height;row++) {
for (int col = 0; col < width; col++) {
int index =row * width + col;
if (labels.at<int>(index,0) == maxlabeli)
{
RGBSigma2.at<float>(0,0) += pow((Points.at<float>(index,0)-RGBAverage.at<float>(0,0)),2);
RGBSigma2.at<float>(0,1) += pow((Points.at<float>(index,1)-RGBAverage.at<float>(0,1)),2);
RGBSigma2.at<float>(0,2) += pow((Points.at<float>(index,2)-RGBAverage.at<float>(0,2)),2);
}
}
}
cv::Mat RGBSigma(1,dims,CV_32F,cv::Scalar(10));
RGBSigma.at<float>(0,0) = sqrt((RGBSigma2.at<float>(0,0) / maxlabelamount));
RGBSigma.at<float>(0,1) = sqrt((RGBSigma2.at<float>(0,1) / maxlabelamount));
RGBSigma.at<float>(0,2) = sqrt((RGBSigma2.at<float>(0,2) / maxlabelamount));
cv::Mat maxlabelRGB(1,dims*3,CV_32F,cv::Scalar(10));
maxlabelRGB.at<float>(0,0) = RGBAverage.at<float>(0,0);
maxlabelRGB.at<float>(0,1) = RGBAverage.at<float>(0,1);
maxlabelRGB.at<float>(0,2) = RGBAverage.at<float>(0,2);
maxlabelRGB.at<float>(0,3) = RGBAverage.at<float>(0,0) - 4 * RGBSigma.at<float>(0,0);
maxlabelRGB.at<float>(0,4) = RGBAverage.at<float>(0,1) - 4 * RGBSigma.at<float>(0,1);
maxlabelRGB.at<float>(0,5) = RGBAverage.at<float>(0,2) - 4 * RGBSigma.at<float>(0,2);
maxlabelRGB.at<float>(0,6) = RGBAverage.at<float>(0,0) + 4 * RGBSigma.at<float>(0,0);
maxlabelRGB.at<float>(0,7) = RGBAverage.at<float>(0,1) + 4 * RGBSigma.at<float>(0,1);
maxlabelRGB.at<float>(0,8) = RGBAverage.at<float>(0,2) + 4 * RGBSigma.at<float>(0,2);
cv::imshow("Kmeans_Result",result);
cv::waitKey(1);
return maxlabelRGB;
}
void RGBSeparate(Mat src,Mat &dst,Mat maxlabelRGB)
{
GammaCorrection(src,src,1/1.6);
inRange(src, Scalar(maxlabelRGB.at<float>(0,3),maxlabelRGB.at<float>(0,4),maxlabelRGB.at<float>(0,5)),Scalar(maxlabelRGB.at<float>(0,6),maxlabelRGB.at<float>(0,7),maxlabelRGB.at<float>(0,8)), dst);
Mat element=getStructuringElement(MORPH_RECT, Size(20,20));
morphologyEx(dst, dst, MORPH_OPEN, element);
}
void LargestConnectedComponent(Mat srcImage, Mat &dstImage)
{
Mat temp;
Mat labels;
srcImage.copyTo(temp);
int n_comps = connectedComponents(temp, labels, 4, CV_16U);
vector<int> histogram_of_labels;
for (int i = 0; i < n_comps; i++)
{
histogram_of_labels.push_back(0);
}
int rows = labels.rows;
int cols = labels.cols;
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
histogram_of_labels.at(labels.at<unsigned short>(row, col)) += 1;
}
}
histogram_of_labels.at(0) = 0;
int maximum = 0;
int max_idx = 0;
for (int i = 0; i < n_comps; i++)
{
if (histogram_of_labels.at(i) > maximum)
{
maximum = histogram_of_labels.at(i);
max_idx = i;
}
}
for (int row = 0; row < rows; row++)
{
for (int col = 0; col < cols; col++)
{
if (labels.at<unsigned short>(row, col) == max_idx)
{
labels.at<unsigned short>(row, col) = 255;
}
else
{
labels.at<unsigned short>(row, col) = 0;
}
}
}
labels.convertTo(dstImage, CV_8U);
}
double * Getcenter(Mat src,double SPEED[2],Mat maxlabelRGB)
{
Mat aimcolor=src.clone();
RGBSeparate(src,aimcolor,maxlabelRGB);
imshow("Color_Search",aimcolor);
Mat maxearea=src.clone();
LargestConnectedComponent(aimcolor,maxearea);
double static speed[2];
for(int i=0;i<2;i++){speed[i]=0;}
Mat resultImage=Mat::zeros(src.rows, src.cols, CV_8UC1);;
vector<vector<Point> > contours;
vector<Vec4i> hierarcy;
findContours(maxearea, contours, hierarcy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
vector<RotatedRect> box(contours.size());
Point2f rect[4];
float width = 0;
float height = 0;
float ratio = 0;
Point2f center;
for (int i = 0; i < contours.size(); i++)
{
box[i] = minAreaRect(Mat(contours[i]));
box[i].points(rect);
width = box[i].size.width;
height = box[i].size.height;
if (height >= width)
{
float x = 0;
x = height;
height = width;
width = x;
}
ratio = width / height;
center=(rect[1]+rect[3])/2;
cout << "宽" << width << " " << "高" << height << "长宽比"<<ratio<<"中心"<<center<<endl;
for (int j = 0; j < 4; j++)
{
line(maxearea, rect[j], rect[(j + 1) % 4], Scalar(255,255,255), 3, 25);
line(src, rect[j], rect[(j + 1) % 4], Scalar(50,155,0), 3, 25);
}
}
imshow("Largest_Connected_Component",maxearea);
imshow("Result_Image",src);
double s1=4;double s2=0.03;
double yz1=300;
double yz2=365;
double yz3=650;
if(width<yz1)
{
double linearspeed=s1*(yz1-width)/yz1;
speed[0]=linearspeed;
ROS_WARN("*****GOAHEAD*****");
}
else if (width>yz2 && width<yz3)
{
double linearspeed=s1*(yz2-width)/yz2;
speed[0]=linearspeed;
ROS_WARN("*****BACKFORWARD*****");
}
else
{
double linearspeed=0;
speed[0]=linearspeed;
ROS_WARN("*****STOP*****");
}
if(center.x-src.cols/2<-25){
cout<<"偏差为"<<center.x-src.cols/2;
double anglespeed=-s2*(pow(center.x-src.cols/2,3)/pow(40,3)-1);
speed[1]=anglespeed;
ROS_WARN("*****LEFT*****");
}
else if(center.x-src.cols/2>+25){
cout<<"偏差为"<<center.x-src.cols/2;
double anglespeed=-s2*(pow(center.x-src.cols/2,3)/pow(40,3)-1);
speed[1]=anglespeed;
ROS_WARN("*****RIGHT*****");
}
return speed;
}
int main(int argc,char **argv)
{
VideoCapture capture;
capture.open(1);
ROS_WARN("*****START*****");
ros::init(argc,argv,"ColorMove");
ros::NodeHandle n;
ros::Publisher cmd_pub = n.advertise<geometry_msgs::Twist>("cmd_vel", 5);
geometry_msgs::Twist twist;
if(!capture.isOpened())
{
printf("the camera can't work normally");
return 0;
}
Mat src;
int mode = 0;
cv::Mat maxlabelRGB(1,9,CV_32F,cv::Scalar(10));
init_keyboard();
while(1){
capture.read(src);
int rows = src.rows;
int cols = src.cols;
Rect select=Rect(0,0,cols/2,rows);
src=src(select);
if(src.empty())
{
break;
}
imshow("Initial_Image",src);
if (mode == 0)
{
maxlabelRGB = DoKmeans(src);
if(kbhit())
{
peek_character = -1;
mode = 1;
cout << "已更新目标" << "\n";
cout << "目标颜色R值为" << maxlabelRGB.at<float>(0,0) << "\n";
cout << "目标颜色G值为" << maxlabelRGB.at<float>(0,1) << "\n";
cout << "目标颜色B值为" << maxlabelRGB.at<float>(0,2) << "\n";
cout << "开始跟踪" << "\n";
destroyWindow("Kmeans_Result");
}
}
else if (mode == 1)
{
double *speed;
double s1[2];
speed=Getcenter(src,s1,maxlabelRGB);
twist.linear.x = speed[0];
twist.linear.y = 0;
twist.linear.z = 0;
twist.angular.x = 0;
twist.angular.y = 0;
twist.angular.z = speed[1];
cout<<"twist.linear.x\t"<<twist.linear.x<<"\ttwist.angular.z\t"<<twist.angular.z<<endl;
cmd_pub.publish(twist);
ROS_WARN("****twist****");
if(kbhit())
{
peek_character = -1;
twist.linear.x = 0;
twist.linear.y = 0;
twist.linear.z = 0;
twist.angular.x = 0;
twist.angular.y = 0;
twist.angular.z = 0;
cmd_pub.publish(twist);
mode = 0;
cout << "开始识别新目标" << "\n";
destroyWindow("Largest_Connected_Component");
destroyWindow("Result_Image");
destroyWindow("Color_Search");
}
}
waitKey(1);
}
return 0;
}
实验结果
本课程设计表现出了良好的识别和跟踪性能。在目标人腿颜色为黑色的实验条件下,可以有效去除黑色杆状障碍物的干扰,跟踪及时、迅速、流畅。
绕桩视频如下:
https://www.bilibili.com/video/BV1Qq4y1z7Sn/
演示视频二维码:
本课程设计对纯色目标是通用的,不仅适用于黑色和人腿,在目标颜色为其它纯色的实验条件下也表现出很好的性能。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)