首先说明对于人的坐姿是否端正,需要就行高低肩识别,
算法流程
1:通过语义分割算法实现人的分割提取(enet,pspnet,fcn,unet等,我们这里使用fcn,因为从c++可以调用呀)
2:图像填充
3;左右肩斜率计算
1:c++显示fcn.
需要的文件有pascal-classes.txt,fcn8s-heavy-pascal.prototxt,这两个文件是在opencv-3.4.1的源码中,其它版本有没有,我不知呀。
另外的一个文件是fcn的模型文件fcn8s-heavy-pascal.caffemodel,这个需要单独下载,可以去我的资源里下载(包含上面的两个文件)
直接上代码
#include<opencv2/opencv.hpp>
#include<opencv2/dnn.hpp>
#include<iostream>
using namespace cv;
using namespace std;
using namespace cv::dnn;
const size_t width = 500;
const size_t height = 500;//定义图像文件宽高
vector<Vec3b> labels_color();
string label_file = "F:/fcn/pascal-classes.txt";
string deploy_file = "F:/fcn/fcn8s-heavy-pascal.prototxt";
string model_file = "F:/fcn/fcn8s-heavy-pascal.caffemodel";
int main(int argc, char **argv)
{
Mat src = imread("F:/fcn/FT20A1180801279223.jpg");
if (!src.data)
{
cout << "图像文件未找到!!!" << endl;
return -1;
}
resize(src, src, Size(500, 500), 0, 0);
vector<Vec3b>colors = labels_color();
Net net;
net = readNetFromCaffe(deploy_file, model_file);//读取二进制文件和描述文件
float t1 = getTickCount();
Mat inputblob = blobFromImage(src);
net.setInput(inputblob, "data");
Mat score = net.forward("score");
float t2 = getTickCount();
float t = (t2 - t1) / getTickFrequency();
cout << "运行时间:" << t << endl;
const int rows = score.size[2]; //图像的高
const int cols = score.size[3]; //图像的宽
const int chns = score.size[1]; //图像的通道数
Mat maxCl(rows, cols, CV_8UC1);
Mat maxVal(rows, cols, CV_32FC1);
for (int c = 0; c < chns; c++) {
for (int row = 0; row < rows; row++) {
const float *ptrScore = score.ptr<float>(0, c, row);
uchar *ptrMaxCl = maxCl.ptr<uchar>(row);
float *ptrMaxVal = maxVal.ptr<float>(row);
for (int col = 0; col < cols; col++) {
if (ptrScore[col] > ptrMaxVal[col]) {
ptrMaxVal[col] = ptrScore[col];
ptrMaxCl[col] = (uchar)c;
}
}
}
}
// look up colors
Mat result = Mat::zeros(rows, cols, CV_8UC3);
for (int row = 0; row < rows; row++) {
const uchar *ptrMaxCl = maxCl.ptr<uchar>(row);
Vec3b *ptrColor = result.ptr<Vec3b>(row);
for (int col = 0; col < cols; col++) {
ptrColor[col] = colors[ptrMaxCl[col]];
}
}
Mat dst;
addWeighted(src, 0.3, result, 0.7, 0, dst); //图像合并
imshow("FCN-demo", dst);
waitKey(0);
destroyAllWindows();
return 0;
}
vector<Vec3b> labels_color()
{
vector<Vec3b>colors;
ifstream fp(label_file);//打开输入流,读入文件
if (!fp.is_open())
{
printf("文件读入失败!!!\n");
exit(-1);//直接退出
}
string names;
int temp;
Vec3b color;
string line;//标签文件中都有对应的名字
while (!fp.eof())//当文件没有读到结尾
{
getline(fp, line);//读取每一行
stringstream ss(line); //分割字符串
ss >> names; //读的第一个字符串是names
ss >> temp;
color[0] = temp; //读一个字符串就给color
ss >> temp;
color[1] = temp;
ss >> temp;
color[2] = temp;
colors.push_back(color);
}
return colors;
}
看一下结果:
效果一般,没关系,自己找人的分割数据集,对fcn进行重新训练(具体不在展示)
3:肩部斜率计算代码
int row = FO_m_sssroi1.rows;//获取行数
int col = FO_m_sssroi1.cols*FO_m_sssroi1.channels();//注意是列数*通道数
int sum = 0;
int sum_1 = 0;
int Zsum = 0;
int Zsum_1 = 0;
float the_first_col;
float the_end_col;
float the_Zfirst_col;
float the_Zend_col;
float co_1;
float Zco_1;
//cout << row << "," << col << endl;
//ofstream ofs("pixel.txt");
for (int i = 0; i < row; ++i)
{
uchar *data1 = FO_m_sssroi1.ptr<uchar>(i);//获取第i行首地址
uchar *data_1 = FO_m_sssroi_1.ptr<uchar>(i);//获取第i行首地址
uchar *Zdata1 = ZO_m_sssroi1.ptr<uchar>(i);//获取第i行首地址
uchar *Zdata_1 = ZO_m_sssroi_1.ptr<uchar>(i);//获取第i行首地址
if (sum<int(*data1))
{
the_first_col = i;
}
if (sum_1<int(*data_1))
{
the_end_col = i;
}
if (Zsum<int(*Zdata1))
{
the_Zfirst_col = i;
}
if (Zsum_1<int(*Zdata_1))
{
the_Zend_col = i;
}
sum = int(*data1);
sum_1 = int(*data_1);
Zsum = int(*Zdata1);
Zsum_1 = int(*Zdata_1);
data1++;
data_1++;
Zdata1++;
Zdata_1++;
}
//ofs << "*******************************************************************" << endl;
co_1 = (the_end_col - the_first_col) / (w / 6 - 4 - 1);
Zco_1 = (the_Zend_col - the_Zfirst_col) / (w / 6 - 4 - 1);
float atanValue2 = atan(co_1)*180.0f / PI;//肩部的角度
float atanValue3 = atan(Zco_1)*180.0f / PI;//肩部的角度
cout << "右肩倾斜角度为!" << atanValue2 << "°" << "右肩高度为!" << (the_end_col + the_first_col) / 2 << endl;
cout << "左肩倾斜角度为!" << abs(atanValue3) << "°" << "左肩高度为!" << (the_Zend_col + the_Zfirst_col) / 2 << endl;
cout << "plaese open hear" << atanValue2 << endl;
result += "\"zjiaodu\": " + to_string(atanValue2) + ",";
result += "\"yjiaodu\": " + to_string(abs(atanValue3));
代码简单说明;
通过分割位置与矩形位置相交的两点求出斜率,也就是肩部角度。分割模型速度太慢,需要优化,选用unet(下章)