我的故事是,今天灵光一闪,我想到其实每个小图片包含的信息是很多的,只取一个平均值真是太委屈它了,用“高级一点的”图像近似度计算方法肯定可以得到不错的效果,于是我自己瞎掰了一个匹配算法:取缩略图;高斯模糊;比较方差——发现效果果真(相对)还行:
不过试图将这算法用在彩色图片上时,效果相当不靠谱:
嗯……核心代码如下:
// 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