我的故事是,今天灵光一闪,我想到其实每个小图片包含的信息是很多的,只取一个平均值真是太委屈它了,用“高级一点的”图像近似度计算方法肯定可以得到不错的效果,于是我自己瞎掰了一个匹配算法:取缩略图;高斯模糊;比较方差——发现效果果真(相对)还行:
不过试图将这算法用在彩色图片上时,效果相当不靠谱:
嗯……核心代码如下:
// vi:foldmethod=marker // Author: if ( www.if-yu.info) // Version: 0.0.3.100613 // Date: June 13rd, 2010 // Copyright (C): if // License: BSD /* change log: * Version 0.0.3: can see color * Version 0.0.2: got an command-line interface * Version 0.0.1: born */ // misc {{{ #include <exception> #include <iostream> #include <iomanip> #include <string> #include <vector> #include <cmath> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <boost/filesystem.hpp> #include <boost/program_options.hpp> #include <opencv/cv.h> #include <opencv/cv.hpp> #include <opencv/cxcore.h> #include <opencv/cxcore.hpp> #include <opencv/highgui.h> #include "sizedpqueue.hpp" using namespace std; namespace fs = boost::filesystem; namespace po = boost::program_options; int g_thumb_size = 12; int g_image_size = 48; int g_top_n = 13; bool g_color = false; typedef struct Image { cv::Mat thumb; cv::Mat orig; } Image; typedef struct Record { double d; int idx; } Record; class RecordCmp { public: bool operator()(Record const& a, Record const& b)const{ return a.d<b.d; } }; // }}} // mean idea: // calculate a `distance' between two images, which shows their similarity {{{ double dist(cv::Mat const& m1, cv::Mat const& m2) { assert(m1.type() == m2.type() && m1.size() == m2.size()); if(CV_MAT_TYPE(m1.type())==CV_8UC1) { typedef unsigned char uchar; int i,I=m1.rows,j,J=m1.cols,t; double d=0; for(i=0;i<I;++i) for(j=0;j<J;++j) { t=int(m1.at<uchar>(i,j))-int(m2.at<uchar>(i,j)); t*=t; d+=/*fabs*/(t); } return d; } else if(CV_MAT_TYPE(m1.type())==CV_8UC3) { double dM=0.0, t, d[3]={0.}; cv::Scalar mean1 = cv::mean(m1); cv::Scalar mean2 = cv::mean(m2); for(int c=0;c<3;++c) { t=mean1.val[c]-mean2.val[c]; t*=t; dM+=t; } for(int i=0, I=m1.rows; i<I; ++i) { for(int j=0, J=m1.cols; j<J; ++j) { typedef cv::Vec<uchar,3> BGR_color; BGR_color c1=m1.at<BGR_color>(i,j); BGR_color c2=m2.at<BGR_color>(i,j); for(int c=0;c<3;++c) { t = c1[c]-c2[c]; d[c] += t*t; } } } #if 0 // compare their histogram int hist1[3][64]={{0}}, hist2[3][64]={{0}}; for(int i=0,I=m1.rows; i<I; ++i) { for(int j=0,J=m1.cols; j<J; ++j) { typedef cv::Vec<uchar,3> BGR_color; BGR_color c1 = m1.at<BGR_color>(i, j); BGR_color c2 = m2.at<BGR_color>(i, j); for(int c=0;c<3;++c) { ++hist1[c][c1[c]/4]; ++hist2[c][c2[c]/4]; } } } // algorithms: // 全不靠谱…… // correlation { double a=0.,b=0.,up[3]={0.},down[3]={0.},s1[3]={0.},s2[3]={0.}; double avg1[3]={0}, avg2[3]={0}; for(int c=0;c<3;++c) { for(int i=0;i<64;++i) { avg1[c] += hist1[c][i]/64.; avg2[c] += hist2[c][i]/64.; } } for(int c=0;c<3;++c){ for(int i=0;i<64;++i) { a=hist1[c][i]-avg1[c]; b=hist2[c][i]-avg2[c]; up[c] += a*b; s1[c] += a*a; s2[c] += b*b; } } for(int c=0;c<3;++c){ down[c] = sqrt(s1[c]*s2[c]); d[c] = up[c]/down[c]; } } /* // so-called chi-square, sucks for(int i=0;i<64;++i){ for(int c=0;c<3;++c) { t=(hist1[c][i]-hist2[c][i]); t*=t; d[c] += t/(hist1[c][i]+hist2[c][i]); } } */ #endif t = 0; for(int i=0;i<3;++i){ t+=d[i]*d[i]; } return 0.7*dM + 0.3*t; } else { // only support CV_8UC1 and CV_8UC3 so far assert(CV_MAT_TYPE(m1.type()) == CV_8UC1 || CV_MAT_TYPE(m1.type()) == CV_8UC3); // false; } return 0; } // }}} // core features: {{{ void addToPool(std::vector<Image>& pool, string const& dir) { cv::Size expect(g_image_size, g_image_size); cout<<"----- reading "<<(g_color?"color":"gray")<<" images from \""<<dir<<"\" -----"<<endl; for(fs::directory_iterator i(dir),I;i!=I;++i){ Image img; img.orig = cv::imread(i->path().string(), g_color?1:0); if(img.orig.size()!=expect) { cout<<"X"; continue; } cv::resize(img.orig, img.thumb, cv::Size(g_thumb_size, g_thumb_size)); // cv::blur(img.thumb, img.thumb, cv::Size(2,2)); pool.push_back(img); cout<<'+'; } cout<<"\n [done]"<<endl; } int chooseRandomFromPool(vector<Image> const& pool, cv::Mat const& source) { int a=0,c=pool.size(); sizedpq<Record,RecordCmp> top(g_top_n); cv::Mat thumb; cv::resize(source, thumb, cv::Size(g_thumb_size, g_thumb_size)); for(a=0;a<c;++a) { Record r; r.d=dist(pool[a].thumb, thumb); r.idx=a; top.insert(r); } return top[rand()%g_top_n].idx; } void doRandomMosaic(vector<Image> const& pool, cv::Mat const& src, cv::Mat & dst){ cv::Size size(src.size()); size.width /= g_thumb_size; size.height /= g_thumb_size; cv::Size dstSize( size.width*g_image_size, size.height*g_image_size); if(dst.size()!=dstSize) { dst = cv::Mat::zeros(dstSize, g_color?CV_8UC3:CV_8UC1); } cout<<"----- doing what you want me to -----"<<endl; if (dstSize.width>800||dstSize.height>600) { cout<<"image is too big, i do not want to display it."<<endl; } for(int i=0,I=size.height; i<I; ++i) { for(int j=0,J=size.width; j<J; ++j) { cv::Rect r1(j*g_thumb_size,i*g_thumb_size,g_thumb_size,g_thumb_size); cv::Rect r2(j*g_image_size,i*g_image_size,g_image_size,g_image_size); cv::Mat s(src, r1); cv::Mat d(dst, r2); int n = chooseRandomFromPool(pool, s); pool[n].orig.copyTo(d); cout<<'.'; if(!(dstSize.width>800||dstSize.height>600)) { cv::imshow("dst", dst); cv::waitKey(10); } } double precence = double(i+1)/(size.height)*100.0; cout<<" [ "<<setw(6)<<setprecision(2)<<fixed<<precence<<"% ]\n"; } cout<<"\n [done]"<<endl; } // }}} // interface (if it can be called an interface) {{{ void errorOut(string const& msg) { cerr<<msg<<endl; exit(1); } cv::Size calcDisplaySize(cv::Size sz) { int w = sz.width, h = sz.height; int w2 = w, h2 = h; if (w>=h) { if(w>700) { w2= 700; h2= h * w2 / w; } } else { if (h>700) { h2= 700; w2= w * h2 / h; } } return cv::Size(w2,h2); } void display(cv::Mat const& img) { cv::Mat m; cv::Size s=calcDisplaySize(img.size()); if(s!=img.size()) { cv::resize(img,m,s); cv::imshow("display", m); } else { cv::imshow("display", img); } cv::waitKey(0); } int main(int argc, char *argv[]) { srand(time(0)); vector<Image> pool; vector<string> poolPathes; string inPath; string outPath; double zoom=1.0; // boost::program_options is cute! po::variables_map vm; po::options_description desc("options"); try{ desc.add_options() ("help,h", "show this message") ("color,c", "generate colored mosaic") ("zoom,z", po::value<double>(&zoom)->default_value(zoom), "zoom rate") ("pool,p", po::value<vector<string> >(&poolPathes), "image pool") ("image-size,S", po::value<int>(&g_image_size)->default_value(g_image_size), "size of small images") ("thumb-size,s", po::value<int>(&g_thumb_size)->default_value(g_thumb_size), "size of thumbnails to be used for compairing") ("input,i", po::value<string>(&inPath), "input image file") ("output,o", po::value<string>(&outPath), "output image file") ("top-n,t", po::value<int>(&g_top_n)->default_value(g_top_n), "choose a random piece from top-n like ones") ("display,d", "Display the result image") ("open,x", "View the result image using associated program in your system") ; po::positional_options_description p; p.add("input", -1); po::store(po::command_line_parser(argc,argv) .options(desc).positional(p).run(), vm); po::notify(vm); } catch(exception const& e) { errorOut(e.what()); } if(vm.count("help")) { cout<<desc<<endl; return 0; } if(! vm.count("input")) { errorOut("No input file"); } if(! vm.count("output")) { errorOut("No output file"); } if( vm.count("color")) { g_color = true; } if(! vm.count("pool")) { errorOut("Where can i find pieces to make mosaic?"); } else { for(vector<string>::const_iterator citr = poolPathes.begin(); citr != poolPathes.end(); ++citr ) { if(!fs::exists(*citr)) { errorOut("What is \""+*citr+"\" ??"); } else if( fs::is_directory(*citr)) { addToPool(pool, *citr); } } if(pool.empty()) { errorOut("What do you expect when u got no image in pool?"); } } if( g_image_size<g_thumb_size ) { errorOut("You are doing something foolish, human."); } cv::Mat src; cv::Mat dst; try{ if(g_color) { cout<<"Reading source image [colored] "<<endl; src = cv::imread(inPath, 1); } else { cout<<"Reading source image [b&w]"<<endl; src = cv::imread(inPath, 0); } } catch(...) { errorOut(string("Can\'t read your input file: \"")+inPath+"\""); } cv::Size s(src.size()); if(s==cv::Size(0,0)) errorOut(string("Can\'t read your input file: \"")+inPath+"\""); s.width = static_cast<int>(s.width/g_image_size*g_thumb_size*zoom); s.width -= s.width%g_thumb_size; s.height = static_cast<int>(s.height/g_image_size*g_thumb_size*zoom); s.height -= s.height%g_thumb_size; if(s!=src.size()) { cv::resize(src, dst, s); src=dst.clone(); } doRandomMosaic(pool, src, dst); try{ cv::imwrite(outPath,dst); } catch(...) { errorOut("Failed to save the image, please check your RP."); } if(vm.count("display")) { display(dst); } if(vm.count("open")) { system(outPath.c_str()); } return 0; } // }}}
编译成 mosaic.exe 之后,用法如下:
mosaic <源文件> -o <输出图像> <选项们>
选项有:- -p <缩略图库目录> (支持多个目录,至少得要有一个非空的目录)
- -S <缩略图大小> (单位为像素,默认48)
- -s <用于比较的缩略图大小> (将会把缩略图再缩率成这个模样,然后用于比较,单位像素,默认12)
- -z <放大倍数> (实数,默认为 1)
- -c (启用彩色,默认灰白)
- -t <n> (从前n个图像中随机选一个用于填充,默认13)
- -x (完成后在系统关联程序中打开)
- -d (完成后显示一下)
=> 二进制文件及源代码打包下载 [2.4 MB]
No comments:
Post a Comment