我认为这是以前由Michele和Joshua重复的几个问题。但我需要再次提出这个问题,因为其他人都没有得到答复。
我正在尝试使用BoW方法进行图像分类。我已经查看了一些在OpenCV网站上显示谷歌搜索和文档的教程。对于特征,我首先将图像划分为网格并计算直方图并将它们堆叠以形成每个图像的一组描述符。然后,对于在我的数据库中为训练图像提取的所有描述符,我使用BOWKMeansTrainer来构建字典。
对于我的测试图像,当我尝试使用BOWImgDescriptorExtractor生成BoW特征向量时,BoW的直方图对于我的所有测试图像都是空的。以下是代码的简短部分......
class histFeatureExtractor : public cv::DescriptorExtractor
{
...
};
...
cv::Ptr<cv::DescriptorMatcher> matcher(new cv::FlannBasedMatcher);
cv::Ptr<cv::DescriptorExtractor> extractor(new histFeatureExtractor);
cv::BOWImgDescriptorExtractor bowDE(extractor,matcher);
bowDE.setVocabulary(dictionary); /// dictionary computed earlier
然后,对于我的每个训练图像,我计算,
bowDE.compute(img,keypoints1,bowDescTest);
注意:keypoints1不包含任何关键点,但由于opencv需要非空容器,因此我在其中放入了一些垃圾值。因为我使用自己的描述符提取器(被正确调用)...我不需要任何关键点。
最后,我的bowDescTest应该包含为测试图像计算的描述符,但结果是 [ 0 0 0 0 0 0 0 0 0 ]
我不明白我的逻辑是否不正确或者功能的选择是否错误......或者OpenCV功能的使用中我有什么错误......有人可以帮忙。
编辑:2013年12月19日:20:59 ////// ************ ////// 计算直方图描述符的类
class histFeatureExtractor : public cv::DescriptorExtractor
{
public:
histFeatureExtractor() : cv::DescriptorExtractor()/*, m_patchSize(cv::Size(136,160))*/
{
/// Width, Height
m_patchSize = cv::Size(80,160);
}
~histFeatureExtractor()
{
}
void compute( const cv::Mat& __img,
CV_OUT CV_IN_OUT std::vector<cv::KeyPoint>& __keypoints,
CV_OUT cv::Mat& __descriptors ) const
{
this->computeImpl(__img, __keypoints, __descriptors);
}
int descriptorSize() const {return 128;}
int descriptorType() const {return CV_32F;}
bool empty() const { return false; }
protected:
void computeImpl( const cv::Mat& __img, std::vector<cv::KeyPoint>& keypoints, cv::Mat& __descriptors ) const
{
//cv::namedWindow("Patch");
float range[] = { 0, 256 } ;
const float* histRange = { range };
bool uniform = true; bool accumulate = false;
int histSize = 256;
//int count = 0;
cv::Mat transp;
for (int r=0; r<__img.rows/m_patchSize.height ; ++r)
{
for (int c=0; c<__img.cols/m_patchSize.width ; ++c)
{
//std::cout << "[" << r << " " << c << "]: ";
/// Construct image patches
std::vector<cv::Mat> bgr_planes;
cv::Point patchTopLeft( m_patchSize.width*c, m_patchSize.height*r);
cv::Rect imgPatch(patchTopLeft, m_patchSize);
cv::split( __img(imgPatch), bgr_planes );
//cv::imshow("Patch", __img(imgPatch));
cv::Mat b_hist, g_hist, r_hist;
cv::Mat transp_CV32F;
/// Compute the histograms:
cv::calcHist( &bgr_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
transp = b_hist.t();
transp.convertTo(transp_CV32F, CV_32F);
__descriptors.push_back(transp_CV32F);
cv::calcHist( &bgr_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
transp = g_hist.t();
transp.convertTo(transp_CV32F, CV_32F);
__descriptors.push_back(transp_CV32F);
cv::calcHist( &bgr_planes[2], 1, 0, cv::Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
transp = r_hist.t();
transp.convertTo(transp_CV32F, CV_32F);
__descriptors.push_back(transp_CV32F);
}
}
}
private:
cv::Size m_patchSize;
};
主要代码部分
histFeatureExtractor featureExtractor;
std::cout << "Generating vocabulary...";
for (int i=0 ; i<ntrainImgs ; ++i)
{
cv::Mat desc;
std::string imgName( __mTrainData.getImage( i ) );
std::string imgFullPath = __mTrainData.m_workingDirectory + "/" + imgName;
img = cv::imread( imgFullPath );
featureExtractor.compute( img, keypoints, desc );
cv::Mat desc_CV32F;
desc.convertTo(desc_CV32F, CV_32F); /// I READ SOMEWHERE THAT BoW class is looking for CV_32F format, so forcing every thing to be in that format.
bowTrainer.add(desc_CV32F);
cv::imshow("Current Frame", img);
std::cout << ".";
}
/// Generating BoW descriptors
cv::Ptr<cv::DescriptorMatcher> matcher(new cv::FlannBasedMatcher);
cv::Ptr<cv::DescriptorExtractor> extractor(new histFeatureExtractor);
cv::BOWImgDescriptorExtractor bowDE(extractor,matcher);
bowDE.setVocabulary(dictionary);
for (int i=0 ; i<ntestImgs ; ++i)
{
std::string imgName( __mTestData.getImage( i ) );
std::string imgFullPath = __mTestData.m_workingDirectory + "/" + imgName;
img = cv::imread( imgFullPath );
cv::Mat bowDescTest;
/// NOTE: EMPTY KEYPOINTS
/// I checked this part in the opencv file
/// /DEV/opencv_2-4-0/sources/OpenCV-2.4.0/modules/features2d/src/bagofwords.cpp
/// function BOWImgDescriptorExtractor::compute(...)
/// Line 141 onwards. This function is exclusively used
/// by the extractor, which in my case is not required.
std::vector<cv::KeyPoint> keypoints1(30);
bowDE.compute(img,keypoints1,bowDescTest);
std::cout << bowDescTest.type() << std::endl;
/// Print the BoW descriptors
///
/// HERE I GET THE DESCRIPTORS TO BE ALL ZEROS.
std::cout << i << " : [ ";
for (int r=0 ; r<bowDescTest.rows ; ++r)
{
for (int c=0 ; c<bowDescTest.cols ; ++c)
{
std::cout << cvRound( bowDescTest.at<float>(r,c) ) << " ";
}
}
std::cout << "]\n";
}