一个想法,关于火柴人形式的人体关节检测

RachelKong 2018-03-26 03:31:19
想要做手绘人体骨架的(半)自动纠正,即输入一张随手画的火柴人,系统找到其关键关节,与人体模型库里的各种姿势进行大致的匹配,得到结果后,对比关键关节点,根据模型的人体结构比例什么的纠正一下关节点的位置,重新绘制出这个火柴人。

现在正在做第一步,找到火柴人的关节点。图像的信息很少(没有深度信息),怎么确定“头是头,手是手”是一个问题,初步想法是要求用户手动设定脖子的位置作为root point,然后四面八方找端点,确定手啊,腰部啊之类的关节点位置,胳膊肘之类的就取一半的地方,当然有拐点就设拐点为这个关节。想问问求端点位置这一步是叫做线段的trace吗?因为水平不是很好,所以不知道该怎么搜索相关资料。或者是否需要先将各个部分分段,进行线条的拟合,确定语义,再找关节?
有看一篇叫做Extraction of Human Body Skeleton Based on Silhouette Images的论文,其中找关节点的方法是先求距离变换的梯度向量,根据极值得到一些关键点,然后一步步减少关键点的数量,最后是以与人体轮廓相切的圆来缩减关键点的数量,直到这些小的圆都不重叠,然后设定一个constraint condition,找距离最近的点作为那个关节……因为我是火柴人所以好像缩减关键点数量那里没法做呢……

想听听大家的建议,谢谢^_^



...全文
1381 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
赵4老师 2018-03-29
  • 打赏
  • 举报
回复
仅供参考:
//在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。
//图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。
//所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。
//好的细化算法一定要满足:
//  收敛性;
//  保证细化后细线的连通性;
//  保持原图的基本形状;
//  减少笔画相交处的畸变;
//  细化结果是原图像的中心线;
//  细化的快速性和迭代次数少;
//这里,我们对“Zhang并行快速细化算法”进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <vector>
/**
 * @brief 对输入图像进行细化
 * @param src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
 * @param maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
 * @return 为对src细化后的输出图像,格式与src格式相同,元素中只有0与1,1代表有元素,0代表为空白
 */
cv::Mat thinImage(const cv::Mat & src, const int maxIterations = -1) {
    assert(src.type() == CV_8UC1);
    cv::Mat dst;
    int width  = src.cols;
    int height = src.rows;
    src.copyTo(dst);
    int count = 0;  //记录迭代次数

    while (true) {
        count++;
        if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达
            break;//
        std::vector<uchar *> mFlag; //用于标记需要删除的点
        //对点标记
        for (int i = 0; i < height; ++i) {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j) {
                //如果满足四个条件,进行标记
                //  p9 p2 p3
                //  p8 p1 p4
                //  p7 p6 p5
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + 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 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0) {
                        mFlag.push_back(p+j);//标记
                    }
                }
            }
        }
        //将标记的点删除
        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i) {
            **i = 0;
        }
        //直到没有点满足,算法结束
        if (mFlag.empty()) {
            break;
        } else {
            mFlag.clear();//将mFlag清空
        }
        //对点标记
        for (int i = 0; i < height; ++i) {
            uchar * p = dst.ptr<uchar>(i);
            for (int j = 0; j < width; ++j) {
                //如果满足四个条件,进行标记
                //  p9 p2 p3
                //  p8 p1 p4
                //  p7 p6 p5
                uchar p1 = p[j];
                if (p1 != 1) continue;
                uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
                uchar p8 = (j == 0) ? 0 : *(p + j - 1);
                uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
                uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
                uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
                uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
                uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
                uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + 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 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0) {
                        mFlag.push_back(p+j);//标记
                    }
                }
            }
        }
        //将标记的点删除
        for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i) {
            **i = 0;
        }
        //直到没有点满足,算法结束
        if (mFlag.empty()) {
            break;
        } else {
            mFlag.clear();//将mFlag清空
        }
    }
    return dst;
}
int main(int argc, char*argv[]) {
    //获取图像
    if (argc != 2) {
        std::cout << "参数个数错误!" << std::endl;
        return -1;
    }
    cv::Mat src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
    if (src.empty()) {
        std::cout << "读取文件失败!" << std::endl;
        return -1;
    }
    //将原图像转换为二值图像
    cv::threshold(src, src, 128, 1, cv::THRESH_BINARY);
    //图像细化
    cv::Mat dst = thinImage(src);
    //显示图像
    dst = dst * 255;
    cv::namedWindow("src1", CV_WINDOW_AUTOSIZE);
    cv::namedWindow("dst1", CV_WINDOW_AUTOSIZE);
    cv::imshow("src1", src);
    cv::imshow("dst1", dst);
    cv::waitKey(0);
}
RachelKong 2018-03-29
  • 打赏
  • 举报
回复
感谢这位赵老师提供了骨骼化的经典算法。 我不是要得到骨架长什么样,是要得到关键的关节点的位置。 现在已经找到了端点和交叉点,接下来准备根据大致方位确定点的语义信息(对应哪个关节)…… 只能这样试试看了。
RachelKong 2018-03-28
  • 打赏
  • 举报
回复
引用 4 楼 RachelKong 的回复:
人体姿态跟踪主要两种方式,一个基于深度图,一个是提取特征,根据人体结构检测处关节、姿态, 不过我这个题目火柴人本身结构就不是正确的,以上方法貌似不适用,当然,也是根据图像特征信息&人体结构信息来找关节, 所以目前想到的方法是从一个root point出发,沿着线条从四个方向搜寻,找到端点(手、脚),分叉处(腰部)…… 因此想找线段追踪之类的……这个关键词搜不到东西,感觉是找错了关键词。自己写算法又没什么自信。
同时也不打算用深度学习_(:з」∠)_
RachelKong 2018-03-28
  • 打赏
  • 举报
回复
人体姿态跟踪主要两种方式,一个基于深度图,一个是提取特征,根据人体结构检测处关节、姿态, 不过我这个题目火柴人本身结构就不是正确的,以上方法貌似不适用,当然,也是根据图像特征信息&人体结构信息来找关节, 所以目前想到的方法是从一个root point出发,沿着线条从四个方向搜寻,找到端点(手、脚),分叉处(腰部)…… 因此想找线段追踪之类的……这个关键词搜不到东西,感觉是找错了关键词。自己写算法又没什么自信。
赵4老师 2018-03-28
  • 打赏
  • 举报
回复
应该搜“人体姿态跟踪”
RachelKong 2018-03-27
  • 打赏
  • 举报
回复
终于等来一个回复……谢谢楼上的解答,我确实没百度,我用的必应。 搜opencv line trace 线段trace之类的都没有匹配度高的,我也很纳闷,按理说应该挺多example的……
赵4老师 2018-03-27
  • 打赏
  • 举报
回复
百度搜相关关键字。

19,468

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧