小白向牛人请教一个关于图片中识别每片叶子,并计算叶子中颜色比例的问题?

stephen_xiayan 2013-08-21 05:01:23
小白在此向各位牛人请教一个关于图片中识别每片叶子,并计算叶子中颜色比例的问题?

昨天在网上查了下,有人说可以用openCV库实现,用背景差分 取出叶子部分,再边缘检测出每个叶子的边缘,遍历每个叶子统计每个像素点的rgb值,最后计算其比例

不过我是学生物的,计算机背景很弱,不会写C++的代码,但是现在又有几百张的水稻叶子的图片需要处理,根据颜色来研究其衰老的品种~~

希望牛人能够帮忙给予指点,有具体代码最好了~~在此感谢大家了~~

图片:


...全文
820 62 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
62 条回复
切换为时间正序
请发表友善的回复…
发表回复
stereoMatching 2013-10-03
  • 打赏
  • 举报
回复
33楼的图片相对容易解决,我想楼主也该解决了,幸好这类图片占据了90%

我的方法比较迂回曲折
第一步是用watershed切割出叶子
第二步用erosion切掉相连的部分
第三步找出contours
第四步删除太小的contours
第五步个别对切割出来的contours做dilation

到这里33楼的问题基本可说是解决了吧?

代码和图

#include <iostream>
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <imageAlgorithm.hpp>

//not a big deal to use global parameter or
//global typedef in this small program

typedef std::vector<std::vector<cv::Point> > ContoursType;

std::string const Folder("/Users/Qt/program/blogsCodes/pic/");

/**
* @brief segment all of the leafs
* @param the number of the images want to segment
* @return the leafs
*/
std::vector<cv::Mat> contours_method(std::initializer_list<std::string> number)
{
std::vector<cv::Mat> results;
for(auto const &num : number){
cv::Mat color_image = cv::imread(Folder + "leaf" + num + ".png");
if(color_image.empty()){
std::cerr<<"input is empty"<<std::endl;
return results;
}

cv::Mat image;
cv::cvtColor(color_image, image, CV_BGR2GRAY);

//step 1 : binarize the image
cv::Mat binary;
cv::threshold(image, binary, 140, 255, cv::THRESH_BINARY);

//step 2 : remove small objects and noise
auto const structure = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));
cv::Mat fore_ground;
cv::erode(binary, fore_ground, structure, cv::Point(-1, -1), 4);

//step 3 : find possible foreground(set as 255)
ContoursType contours;
cv::findContours(fore_ground, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
OCV::remove_contours(contours, 8000, 50000);
fore_ground.setTo(0);
cv::drawContours(fore_ground, contours, -1, cv::Scalar(255), CV_FILLED);
cv::imwrite("foregroundFinal" + num + ".png", fore_ground);

//step 4 : find possible background(set as 128)
//0 represent uncertain pixels
cv::Mat back_ground;
cv::dilate(binary, back_ground, structure, cv::Point(-1, -1), 4);
cv::threshold(back_ground, back_ground, 1, 128, cv::THRESH_BINARY_INV);
cv::imwrite("backGround" + num + ".png", back_ground);

//step 5 : create markers and mask
cv::Mat markers = back_ground + fore_ground;
cv::imwrite("markers" + num + ".png", markers);
cv::Mat mask;
markers.convertTo(mask, CV_32S);
cv::watershed(color_image, mask);
mask.convertTo(mask, CV_8U);
cv::threshold(mask, mask, 150, 255, CV_THRESH_BINARY);
cv::imwrite("mask" + num + ".png", mask);

//step 6 : final results
cv::Mat result;
cv::imwrite("finalMask" + num + ".png", mask);
color_image.copyTo(result, mask);
cv::imwrite("result" + num + ".png", result);

results.push_back(result);
}

return results;
}

/**
* @brief cut the leafs to single leaf
* @param input input image
* @param name name of the image want to be saved
*/
void cut_to_single_leaf_simple(cv::Mat const &input, std::string const &name)
{
cv::Mat mask;
//step 1 : convert the input to gray image
cv::cvtColor(input, mask, CV_BGR2GRAY);
//step 2 : binarize it
cv::threshold(mask, mask, 128, 255, CV_THRESH_BINARY);
//step 3 : do serious erosion
cv::erode(mask, mask, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)), cv::Point(-1, -1), 5);
//step 4 : find and remove the outliers contours
ContoursType contours;
cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
OCV::remove_contours(contours, 500, 120000);
cv::Mat result;
//step 5 : draw the contours one by one
for(size_t i = 0; i != contours.size(); ++i){
mask.setTo(0);
cv::drawContours(mask, contours, i, cv::Scalar(255), CV_FILLED);
//step 6 : dilate and close the contours
cv::dilate(mask, mask, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)), cv::Point(-1, -1), 6);
cv::morphologyEx(mask, mask, CV_MOP_CLOSE, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)));
input.copyTo(result, mask);
cv::imwrite(name + std::to_string(i) + ".png", result);
//cv::imshow("", result);
//cv::waitKey();
result.setTo(0);
}
}

int main()
{
std::vector<cv::Mat> results = contours_method({"01"});
cut_to_single_leaf_simple(results[1], "singleLeaf01_");

return 0;
}


remove_contours的部分稍微做了些修改,性能较高

/**
* @brief remove the contours by contours area(pixels surrounded by the contours)
* @param contours : the contours
* @param cmin : contour size lower than cmin will be removed
* @param cmax : contour size higher than cmax will be removed
*/
void remove_contours(std::vector<std::vector<cv::Point> > &contours, double cmin, double cmax)
{
auto it = std::partition(std::begin(contours), std::end(contours), [=](std::vector<cv::Point> const &data)
{
auto const size = cv::contourArea(data);
return !(size < cmin || size > cmax);
});
contours.erase(it, std::end(contours));
}


图片








完整代码可到github抓
https://github.com/stereomatchingkiss/blogCodes2/tree/master/extractLeaf/waterShed


这一来大概有90%左右的的图片都可以交给机器解决了吧?

至于那两张有一堆叶子粘接在一起的图
下一帖我会说明该怎么解决
结果暂时还不够好就是

过去我对computer vision只有粗浅的涉猎过
最近在复习和加深对他的了解
如果将来发现了什么好方法会再发帖解释怎么切割
希望computer vision的专家可以给点建议
赵4老师 2013-09-29
  • 打赏
  • 举报
回复
引用 36 楼 zhao4zhong1 的回复:
劝帖主还是用“人件”来解决比较靠谱: 设图片的文件名依次为0001.jpg,0002.jpg,...,9999.jpg 遍一个程序,逐一读取每幅图片并全屏显示, 人工用鼠标左键依次点击一个叶片的三个角,右键点击最后一个角,程序在图片上画出框住叶片的矩形,本图片中的所有叶片框完后,按比如空格键,将本图片中的叶片数和每个叶片四个顶点的坐标保存到和当前图片文件名0001.jpg对应的纯文本文件0001.txt中,重复直到9999.jpg中的每个叶片的顶点坐标保存到对应的纯文本文件9999.txt中。 GAME OVER
自顶!
stereoMatching 2013-09-28
  • 打赏
  • 举报
回复
引用 57 楼 jingyexiaoyue 的回复:
这个我建议还是用Matlab,用C/C++难度太大。 你这个我能想到的办法就是用一个固定的黄色RGB值做参考,用这个图片的像素点减去这个固定值取出某个符合范围的值,如: 绿色-黄色=10~20,像素差值落在10-20的,就认定为绿色。 黄色-黄色=-5~5,像素差值落在-5~5的,就认定为黄色。 然后取出绿色除以黄色就是这个比率。
用纯c的openCV是在自虐,但是用c++的openCV2,代码应该很简洁才是 如果要用你的方法,可以考虑histogram back projection 如果采用histogram back projection 由于楼主要辨识叶子在不同时期的颜色变化 不同时期的叶子可能得采用不同的sample来比较 效果大概不会差到哪里去就是了 其他方法我试一试后如果效果还可以,会再把结果贴出来 希望懂computer vision的人可以提供一些意见
晓敬 2013-09-28
  • 打赏
  • 举报
回复
引用 57 楼 jingyexiaoyue 的回复:
这个我建议还是用Matlab,用C/C++难度太大。 你这个我能想到的办法就是用一个固定的黄色RGB值做参考,用这个图片的像素点减去这个固定值取出某个符合范围的值,如: 绿色-黄色=10~20,像素差值落在10-20的,就认定为绿色。 黄色-黄色=-5~5,像素差值落在-5~5的,就认定为黄色。 然后取出绿色除以黄色就是这个比率。
注意这个仅仅是比喻。
晓敬 2013-09-28
  • 打赏
  • 举报
回复
这个我建议还是用Matlab,用C/C++难度太大。 你这个我能想到的办法就是用一个固定的黄色RGB值做参考,用这个图片的像素点减去这个固定值取出某个符合范围的值,如: 绿色-黄色=10~20,像素差值落在10-20的,就认定为绿色。 黄色-黄色=-5~5,像素差值落在-5~5的,就认定为黄色。 然后取出绿色除以黄色就是这个比率。
stereoMatching 2013-09-28
  • 打赏
  • 举报
回复
利用watershed和contours弄出来的最终结果

代码可以到github抓
https://github.com/stereomatchingkiss/blogCodes2/tree/master/extractLeaf/waterShed





这个是之前的结果(第56贴)
代码可到github抓
https://github.com/stereomatchingkiss/blogCodes2/tree/master/extractLeaf/thresholdAndContours





如果颜色变化不是太大的话(整片黄色或绿色),采用histogram back projection或许是不错的选择
此外也可以考虑用k-means对图像做clustering
这一来可能会比较容易切割
stereoMatching 2013-09-27
  • 打赏
  • 举报
回复
remove_contours小改一下效果会比较好


void remove_contours(ContoursType &contours, double cmin, double cmax)
{
auto it = std::begin(contours);
while(it != std::end(contours)){
auto size = cv::contourArea(*it);
if(size < cmin || size > cmax){
it = contours.erase(it);
}else{
++it;
}
}
}


输入的参数改成10000,50000

remove_contours(contours, 10000, 50000);


结果





stereoMatching 2013-09-27
  • 打赏
  • 举报
回复
remove_contours(contours, 600, 3000);
的600改成500的结果







openCV有提供java的binding,楼主不一定要用自己不熟练的c++

stereoMatching 2013-09-27
  • 打赏
  • 举报
回复
最近在学习computer vision,正好可以拿楼主得问题来试一试 给楼主一个简单的范例,切割得不是很漂亮

#include <iostream>
#include <string>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

typedef std::vector<std::vector<cv::Point> > ContoursType;

//not a big deal to use global parameter in this small program
std::string const Folder("/Users/Qt/program/blogsCodes/pic/");

void remove_contours(ContoursType &contours, ContoursType::size_type cmin, ContoursType::size_type cmax)
{
    auto it = std::begin(contours);
    while(it != std::end(contours)){
        auto const size = it->size();
        if(size < cmin || size > cmax){
            it = contours.erase(it);
        }else{
            ++it;
        }
    }
}

int main()
{   
    cv::Mat image = cv::imread(Folder + "leaf02.png", 0);
    if(image.empty()){
        std::cerr<<"input is empty"<<std::endl;
        return -1;
    }
    cv::imshow("image", image);

    cv::Mat binary;
    //cv::threshold(image, binary, 0, 255, cv::THRESH_BINARY + cv::THRESH_OTSU);
    cv::threshold(image, binary, 140, 255, cv::THRESH_BINARY);
    auto const structure = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(7, 7));
    cv::erode(binary, binary, structure);
    //cv::morphologyEx(binary, binary, cv::MORPH_OPEN, structure);
    cv::imshow("binary", binary);

    ContoursType contours;
    cv::findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    remove_contours(contours, 600, 3000);

    cv::Mat result(image.size(), CV_8U, cv::Scalar(255));
    cv::drawContours(result, contours, -1, cv::Scalar(0), 2);
    cv::imshow("result", result);

    cv::waitKey();

    return 0;
}
还有watershed,canny等方法没尝试 楼主如果不舍得花钱,可以找大学专门搞computer vision的实验室帮忙吧? 第一,二线线的大学研究室应该有足够的能力解决这种问题的
stephen_xiayan 2013-08-23
  • 打赏
  • 举报
回复
引用 50 楼 attop 的回复:
[quote=引用 47 楼 xiayanhao 的回复:] [quote=引用 46 楼 attop 的回复:] 楼主可以google一下 deep learning,或许有帮助
恩恩,谢谢啦~~我问一个牛人,说用种子蔓延法,水曼法处理部分重叠的部分,不过没查到~~不知道有没有人知道??[/quote] 方法是肯定有,你这个问题有难度,不是一两句话能说清楚,但还是可以解决的。 [/quote] 嗯嗯,这个我知道挺麻烦的~~我再继续查查~~希望能顺利解决~~实在不行就用最笨的方法~~~
stephen_xiayan 2013-08-23
  • 打赏
  • 举报
回复
引用 51 楼 zjq9931 的回复:
我是那种有心无力的人。。。 只是说思想没有实际行动能力。。。报歉了楼主。。。
恩恩,不管怎么样还是谢谢热心的朋友们,至少让我知道我这个问题不是那么简单就能处理的,方法还需要继续寻找~~特此感谢各位~~~
woshinia 2013-08-22
  • 打赏
  • 举报
回复
每种图像文件都有特定的格式,像jpg,png都需要解码才能得到rgb,bmp相对来说是最简单的,所以最好用bmp格式,你也别自己写了,网上这种代码或者库应该很多,OpenCV里应该也有。你都不会写c++代码,把这些整合起来可不容易。 即使外面的框架好了,你的那个叶子算法想好了么,图中虽然只有绿色和黄色,但解析出来的rpg的值就多了,你怎么划绿色和黄色的取值范围。衰老程度的计算公式有么?还有几百张肯定要综合考虑,取平均值还是依据什么算法来算? 科学讲究严谨,只要数学上能建好模型,编程不是难事。
max_min_ 2013-08-22
  • 打赏
  • 举报
回复
引用 3 楼 xiayanhao 的回复:
怎么都没有人回答呢?是太简单了吗?边缘识别倒是由函数可以实现,但是把每片叶子分别识别出来,并且计算叶子中的像素点的RGB值的比例,这个我就不知道用什么方法了?求大牛指导啊~~
不精通这方面的,还是重点看看学习OpenCV,对自己的问题重点看看!
stephen_xiayan 2013-08-22
  • 打赏
  • 举报
回复
怎么都没有人回答呢?是太简单了吗?边缘识别倒是由函数可以实现,但是把每片叶子分别识别出来,并且计算叶子中的像素点的RGB值的比例,这个我就不知道用什么方法了?求大牛指导啊~~
  • 打赏
  • 举报
回复
我是那种有心无力的人。。。 只是说思想没有实际行动能力。。。报歉了楼主。。。
attop 2013-08-22
  • 打赏
  • 举报
回复
引用 47 楼 xiayanhao 的回复:
[quote=引用 46 楼 attop 的回复:] 楼主可以google一下 deep learning,或许有帮助
恩恩,谢谢啦~~我问一个牛人,说用种子蔓延法,水曼法处理部分重叠的部分,不过没查到~~不知道有没有人知道??[/quote] 方法是肯定有,你这个问题有难度,不是一两句话能说清楚,但还是可以解决的。
chmn364 2013-08-22
  • 打赏
  • 举报
回复
楼主很可爱啊 肯定是个心肠很好的菇凉
stephen_xiayan 2013-08-22
  • 打赏
  • 举报
回复
在此,感谢各位朋友的热心帮忙~~我会继续找办法,如果找到办法会过来结贴~~我觉得会想到办法的~~
stephen_xiayan 2013-08-22
  • 打赏
  • 举报
回复
引用 46 楼 attop 的回复:
楼主可以google一下 deep learning,或许有帮助
恩恩,谢谢啦~~我问一个牛人,说用种子蔓延法,水曼法处理部分重叠的部分,不过没查到~~不知道有没有人知道??
attop 2013-08-22
  • 打赏
  • 举报
回复
楼主可以google一下 deep learning,或许有帮助
加载更多回复(41)

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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