Joshua Moerman
10 years ago
21 changed files with 595 additions and 211 deletions
@ -0,0 +1,6 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
//! best function ever
|
||||
|
inline double square(double x){ |
||||
|
return x*x; |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
#include "rgb.hpp" |
||||
|
#include "math.hpp" |
||||
|
|
||||
|
#include <image_io.hpp> |
||||
|
#include <av/sws.hpp> |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include <iostream> |
||||
|
|
||||
|
namespace fingerprints { |
||||
|
|
||||
|
static raw_rgb_image downscale_step(av::frame const & frame, int factor) { |
||||
|
raw_rgb_image image(frame->width / factor, frame->height / factor); |
||||
|
|
||||
|
auto context = sws::create_context(frame, image.frame); |
||||
|
sws::scale(context, frame, image.frame); |
||||
|
|
||||
|
return image; |
||||
|
} |
||||
|
|
||||
|
static raw_rgb_image downscale_to(av::frame const & frame, int w, int h){ |
||||
|
// ffmpeg doesnt let us downscale all the way to 5 at once :(, so we do a loop
|
||||
|
raw_rgb_image image; |
||||
|
auto* new_frame = &frame; |
||||
|
while((*new_frame)->width > 8*w && (*new_frame)->height > 8*h){ |
||||
|
image = downscale_step(*new_frame, 4); |
||||
|
new_frame = &image.frame; |
||||
|
} |
||||
|
return to_raw_rgb_image(image.frame, w, h); |
||||
|
} |
||||
|
|
||||
|
rgb rgb::pre_calculate(av::frame const & frame){ |
||||
|
auto const image = downscale_to(crop_to_square(frame), 5, 5); |
||||
|
|
||||
|
rgb ret; |
||||
|
ret.data.assign(image.data.size(), 0); |
||||
|
|
||||
|
std::copy(image.data.begin(), image.data.end(), ret.data.begin()); |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
rgb rgb::calculate(const av::frame& frame){ |
||||
|
return pre_calculate(frame); |
||||
|
} |
||||
|
|
||||
|
double rgb::distance_to(const rgb& fingerprint) const { |
||||
|
assert(data.size() == fingerprint.data.size()); |
||||
|
|
||||
|
double distance = 0; |
||||
|
for(size_t i = 0; i < data.size(); ++i){ |
||||
|
distance += square(data[i] - fingerprint.data[i]); |
||||
|
} |
||||
|
|
||||
|
return distance; |
||||
|
} |
||||
|
|
||||
|
} // namespace fingerprints
|
@ -0,0 +1,30 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <av/av.hpp> |
||||
|
#include <fingerprint_traits.hpp> |
||||
|
|
||||
|
#include <boost/serialization/access.hpp> |
||||
|
#include <boost/serialization/vector.hpp> |
||||
|
|
||||
|
#include <string> |
||||
|
#include <vector> |
||||
|
|
||||
|
|
||||
|
namespace fingerprints { |
||||
|
struct rgb { |
||||
|
std::vector<uint8_t> data; |
||||
|
|
||||
|
static rgb pre_calculate(av::frame const & frame); |
||||
|
static rgb calculate(av::frame const & frame); |
||||
|
double distance_to(rgb const & fingerprint) const; |
||||
|
|
||||
|
static std::string name(){ return "rgb-25-25-25"; } |
||||
|
|
||||
|
private: |
||||
|
friend class boost::serialization::access; |
||||
|
template<class Archive> |
||||
|
void serialize(Archive & ar, const unsigned int /*version*/){ |
||||
|
ar & data; |
||||
|
} |
||||
|
}; |
||||
|
} // namespace fingerprints
|
@ -0,0 +1,88 @@ |
|||||
|
#include "wvlt_rgb.hpp" |
||||
|
#include "math.hpp" |
||||
|
|
||||
|
#include <image_io.hpp> |
||||
|
#include <utilities.hpp> |
||||
|
#include <wvlt/wavelet.hpp> |
||||
|
|
||||
|
#include <ostream> |
||||
|
#include <cmath> |
||||
|
|
||||
|
namespace fingerprints { |
||||
|
|
||||
|
static const int size = 512; |
||||
|
|
||||
|
wvlt_rgb::pre_fingerprint wvlt_rgb::pre_calculate(av::frame const & frame) { |
||||
|
auto const image = to_raw_rgb_image(crop_to_square(frame), size, size); |
||||
|
wvlt_rgb::pre_fingerprint ret; |
||||
|
|
||||
|
// for every color
|
||||
|
for(unsigned int color = 0; color < 3; ++color){ |
||||
|
auto & vector = ret[color]; |
||||
|
vector.assign(make_u(image.width() * image.height()), 0); |
||||
|
|
||||
|
for(unsigned int n = 0; n < make_u(image.width() * image.height()); ++n){ |
||||
|
vector[n] = 2.0 * image.data[3*n + color] / double(255) - 1.0; |
||||
|
} |
||||
|
|
||||
|
wvlt::wavelet_2D(vector.data(), make_u(image.width()), make_u(image.height())); |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
wvlt_rgb wvlt_rgb::calculate(av::frame const & frame){ |
||||
|
auto const image = to_raw_rgb_image(crop_to_square(frame), size, size); |
||||
|
wvlt_rgb ret; |
||||
|
|
||||
|
std::vector<double> vector(make_u(image.width() * image.height()), 0); |
||||
|
|
||||
|
// for every color
|
||||
|
for(unsigned int color = 0; color < 3; ++color){ |
||||
|
auto& coefficient_array = color == 0 ? ret.reds : (color == 1 ? ret.greens : ret.blues); |
||||
|
unsigned int array_index = 0; |
||||
|
|
||||
|
for(unsigned int n = 0; n < make_u(image.width() * image.height()); ++n){ |
||||
|
vector[n] = 2.0 * image.data[3*n + color] / double(255) - 1.0; |
||||
|
} |
||||
|
|
||||
|
wvlt::wavelet_2D(vector.data(), make_u(image.width()), make_u(image.height())); |
||||
|
|
||||
|
auto copy = vector; |
||||
|
for(auto & x : copy) x = std::abs(x); |
||||
|
|
||||
|
auto const n_coefficients = coefficient_array.size(); |
||||
|
std::nth_element(copy.begin(), copy.begin() + n_coefficients, copy.end(), std::greater<double>()); |
||||
|
auto const threshold = copy[n_coefficients-1]; |
||||
|
|
||||
|
for(unsigned int n = 0; n < vector.size(); ++n){ |
||||
|
auto const x = vector[n]; |
||||
|
if(std::abs(x) >= threshold) { |
||||
|
coefficient_array[array_index++] = std::make_pair(n, x); |
||||
|
} |
||||
|
if(array_index >= coefficient_array.size()) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
double wvlt_rgb::distance_to(pre_fingerprint const & fingerprint) const { |
||||
|
double distance = 0; |
||||
|
|
||||
|
for(unsigned int color = 0; color < 3; ++color){ |
||||
|
auto const & coefficients = color == 0 ? reds : (color == 1 ? greens : blues); |
||||
|
|
||||
|
for(auto&& p : coefficients){ |
||||
|
auto const x = p.second; |
||||
|
auto const y = fingerprint[color][p.first]; |
||||
|
distance += square(x - y) - square(y); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return distance; |
||||
|
} |
||||
|
|
||||
|
} // namespace fingerprints
|
@ -0,0 +1,50 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <av/av.hpp> |
||||
|
#include <fingerprint_traits.hpp> |
||||
|
|
||||
|
#include <boost/serialization/access.hpp> |
||||
|
#include <boost/serialization/array.hpp> |
||||
|
#include <boost/serialization/utility.hpp> |
||||
|
|
||||
|
#include <array> |
||||
|
#include <string> |
||||
|
#include <utility> |
||||
|
#include <vector> |
||||
|
|
||||
|
namespace boost { |
||||
|
namespace serialization { |
||||
|
template<class Archive, class T, size_t N> |
||||
|
void serialize(Archive & ar, std::array<T,N> & a, const unsigned int /*version*/) { |
||||
|
ar & make_array(a.data(), a.size()); |
||||
|
} |
||||
|
} // namespace serialization
|
||||
|
} // namespace boost
|
||||
|
|
||||
|
|
||||
|
namespace fingerprints { |
||||
|
struct wvlt_rgb { |
||||
|
// a double for (x, y) location represented in a single int
|
||||
|
using coefficient = std::pair<int, double>; |
||||
|
using pre_fingerprint = std::array<std::vector<double>, 3>; |
||||
|
|
||||
|
std::array<coefficient, 20> reds; |
||||
|
std::array<coefficient, 20> greens; |
||||
|
std::array<coefficient, 20> blues; |
||||
|
|
||||
|
static pre_fingerprint pre_calculate(av::frame const & frame); |
||||
|
static wvlt_rgb calculate(av::frame const & frame); |
||||
|
double distance_to(pre_fingerprint const & fingerprint) const; |
||||
|
|
||||
|
static std::string name(){ return "wvlt-rgb-20x20x20"; } |
||||
|
|
||||
|
private: |
||||
|
friend class boost::serialization::access; |
||||
|
template<class Archive> |
||||
|
void serialize(Archive & ar, const unsigned int /*version*/){ |
||||
|
ar & reds; |
||||
|
ar & greens; |
||||
|
ar & blues; |
||||
|
} |
||||
|
}; |
||||
|
} // namespace fingerprints
|
@ -1,4 +1,43 @@ |
|||||
#ifndef READ_DATABASE_HPP |
#pragma once |
||||
#define READ_DATABASE_HPP |
|
||||
|
|
||||
#endif // READ_DATABASE_HPP
|
#include <database.hpp> |
||||
|
#include <utilities.hpp> |
||||
|
|
||||
|
#include <boost/archive/binary_iarchive.hpp> |
||||
|
#include <boost/archive/binary_oarchive.hpp> |
||||
|
#include <boost/filesystem.hpp> |
||||
|
|
||||
|
#include <iostream> |
||||
|
#include <fstream> |
||||
|
#include <string> |
||||
|
|
||||
|
template <typename T> |
||||
|
auto read_database(std::string const & database_directory, bool output_files = false){ |
||||
|
namespace fs = boost::filesystem; |
||||
|
namespace ar = boost::archive; |
||||
|
|
||||
|
image_database<T> db; |
||||
|
auto const database_file = database_directory + "-" + db.fingerprint_name() + ".db"; |
||||
|
|
||||
|
if (!boost::filesystem::exists(database_file)){ |
||||
|
fs::path const directory(database_directory); |
||||
|
fs::directory_iterator eod; |
||||
|
for(fs::directory_iterator it(directory); it != eod; ++it){ |
||||
|
auto const path = it->path(); |
||||
|
auto const ext = to_lower(path.extension().string()); |
||||
|
if(ext != ".png" && ext != ".jpg" && ext != ".jpeg" && ext != ".gif") continue; |
||||
|
if(output_files) std::cout << path << std::endl; |
||||
|
db.add(path.string()); |
||||
|
} |
||||
|
|
||||
|
std::ofstream file(database_file); |
||||
|
ar::binary_oarchive archive(file); |
||||
|
archive << db; |
||||
|
} else { |
||||
|
std::ifstream file(database_file); |
||||
|
ar::binary_iarchive archive(file); |
||||
|
archive >> db; |
||||
|
} |
||||
|
|
||||
|
return db; |
||||
|
} |
||||
|
@ -0,0 +1,3 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include "wavelet_2.hpp" |
@ -0,0 +1,109 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <cassert> |
||||
|
#include "wavelet_utilities.hpp" |
||||
|
#include "wavelet_constants.hpp" |
||||
|
|
||||
|
/* Rewrite of the basic functions
|
||||
|
* This will make the adaption for the parallel case easier, |
||||
|
* because we can explicitly pass the two elements which are out of range |
||||
|
* (these are normally wrap-around values) |
||||
|
* |
||||
|
* These are also faster (testcase: size = 8, stride = 1, iterations = 100000) |
||||
|
* V2 0.00377901 |
||||
|
* V1 0.0345114 |
||||
|
* |
||||
|
* But also less abstract (which can be both a good thing and bad thing) |
||||
|
* |
||||
|
* wavelet function does not shuffle! |
||||
|
*/ |
||||
|
|
||||
|
namespace wvlt{ |
||||
|
inline namespace V2 { |
||||
|
inline double inner_product(double* x, double const* coef, unsigned int stride){ |
||||
|
return x[0] * coef[0] + x[stride] * coef[1] + x[2*stride] * coef[2] + x[3*stride] * coef[3]; |
||||
|
} |
||||
|
|
||||
|
// will calculate part of wavelete transform (in place!)
|
||||
|
// size is size of vector x (so x[size-1] is valid)
|
||||
|
// does not calculate "last two" elements (it does not assume periodicity)
|
||||
|
// calculates size/stride - 2 elements of the output
|
||||
|
inline void wavelet_mul_base(double* x, unsigned int size, unsigned int stride){ |
||||
|
assert(x && is_even(size) && is_pow_of_two(stride) && 4*stride <= size); |
||||
|
|
||||
|
for(unsigned int i = 0; i < size - 2*stride; i += 2*stride){ |
||||
|
double y1 = inner_product(x + i, evn_coef, stride); |
||||
|
double y2 = inner_product(x + i, odd_coef, stride); |
||||
|
x[i] = y1; |
||||
|
x[i+stride] = y2; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// x1 and x2 are next elements, or wrap around
|
||||
|
// calculates size/stride elements of the output
|
||||
|
inline void wavelet_mul(double* x, double x1, double x2, unsigned int size, unsigned int stride){ |
||||
|
assert(x && is_even(size) && is_pow_of_two(stride) && 2*stride <= size); |
||||
|
if(4*stride <= size) |
||||
|
wavelet_mul_base(x, size, stride); |
||||
|
|
||||
|
unsigned int i = size - 2*stride; |
||||
|
double y1 = x[i] * evn_coef[0] + x[i+stride] * evn_coef[1] + x1 * evn_coef[2] + x2 * evn_coef[3]; |
||||
|
double y2 = x[i] * odd_coef[0] + x[i+stride] * odd_coef[1] + x1 * odd_coef[2] + x2 * odd_coef[3]; |
||||
|
x[i] = y1; |
||||
|
x[i+stride] = y2; |
||||
|
} |
||||
|
|
||||
|
// will overwrite x, x2 and x1 are previous elements, or wrap around
|
||||
|
// size is size of vector x (so x[size-1] is valid)
|
||||
|
inline void wavelet_inv(double* x, double x1, double x2, unsigned int size, unsigned int stride){ |
||||
|
assert(x && is_even(size) && is_pow_of_two(stride) && 4*stride <= size); |
||||
|
|
||||
|
for(unsigned int i = size - 2*stride; i >= 2*stride; i -= 2*stride){ |
||||
|
double y1 = inner_product(x + i - 2*stride, evn_coef_inv, stride); |
||||
|
double y2 = inner_product(x + i - 2*stride, odd_coef_inv, stride); |
||||
|
x[i] = y1; |
||||
|
x[i+stride] = y2; |
||||
|
} |
||||
|
|
||||
|
unsigned int i = 0; |
||||
|
double y1 = x2 * evn_coef_inv[0] + x1 * evn_coef_inv[1] + x[i] * evn_coef_inv[2] + x[i+stride] * evn_coef_inv[3]; |
||||
|
double y2 = x2 * odd_coef_inv[0] + x1 * odd_coef_inv[1] + x[i] * odd_coef_inv[2] + x[i+stride] * odd_coef_inv[3]; |
||||
|
x[i] = y1; |
||||
|
x[i+stride] = y2; |
||||
|
} |
||||
|
|
||||
|
// size indicates number of elements to process (so this is different from above!)
|
||||
|
inline void wavelet(double* x, unsigned int size, unsigned int stride){ |
||||
|
assert(x && is_pow_of_two(size) && size >= 4); |
||||
|
auto full_size = stride*size; |
||||
|
for(unsigned int i = 1; i <= size / 4; i <<= 1){ |
||||
|
auto j = stride * i; |
||||
|
wavelet_mul(x, x[0], x[j], full_size, j); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
inline void wavelet_2D(double* in, unsigned int width, unsigned int height){ |
||||
|
for(unsigned int y = 0; y < height; ++y) |
||||
|
wavelet(in + y*width, width, 1); |
||||
|
for(unsigned int x = 0; x < width; ++x) |
||||
|
wavelet(in + x, height, width); |
||||
|
} |
||||
|
|
||||
|
// size indicates number of elements to process (so this is different from above!)
|
||||
|
inline void unwavelet(double* x, unsigned int size, unsigned int stride){ |
||||
|
assert(x && is_pow_of_two(size) && size >= 4); |
||||
|
auto full_size = stride*size; |
||||
|
for(unsigned int i = size / 4; i >= 1; i >>= 1){ |
||||
|
auto j = stride * i; |
||||
|
wavelet_inv(x, x[full_size-j], x[full_size-2*j], full_size, j); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
inline void unwavelet_2D(double* in, unsigned int width, unsigned int height){ |
||||
|
for(unsigned int x = 0; x < width; ++x) |
||||
|
unwavelet(in + x, height, width); |
||||
|
for(unsigned int y = 0; y < height; ++y) |
||||
|
unwavelet(in + y*width, width, 1); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,37 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <cmath> |
||||
|
|
||||
|
namespace wvlt { |
||||
|
// first row of the matrix Wn
|
||||
|
static double const evn_coef[] = { |
||||
|
(1.0 + std::sqrt(3.0))/(std::sqrt(32.0)), |
||||
|
(3.0 + std::sqrt(3.0))/(std::sqrt(32.0)), |
||||
|
(3.0 - std::sqrt(3.0))/(std::sqrt(32.0)), |
||||
|
(1.0 - std::sqrt(3.0))/(std::sqrt(32.0)) |
||||
|
}; |
||||
|
|
||||
|
// second row of the matrix Wn
|
||||
|
static double const odd_coef[] = { |
||||
|
evn_coef[3], |
||||
|
-evn_coef[2], |
||||
|
evn_coef[1], |
||||
|
-evn_coef[0] |
||||
|
}; |
||||
|
|
||||
|
// first (shifted) row of the matrix Wn^-1
|
||||
|
static double const evn_coef_inv[] = { |
||||
|
evn_coef[2], |
||||
|
evn_coef[1], |
||||
|
evn_coef[0], |
||||
|
evn_coef[3] |
||||
|
}; |
||||
|
|
||||
|
// second (shifted) row of the matrix Wn^-1
|
||||
|
static double const odd_coef_inv[] = { |
||||
|
evn_coef[3], |
||||
|
-evn_coef[0], |
||||
|
evn_coef[1], |
||||
|
-evn_coef[2] |
||||
|
}; |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
template <typename Int> |
||||
|
bool is_pow_of_two(Int n){ |
||||
|
return n && !(n & (n - 1)); |
||||
|
} |
||||
|
|
||||
|
template <typename Int> |
||||
|
bool is_even(Int n){ |
||||
|
return (n & 1) == 0; |
||||
|
} |
||||
|
|
||||
|
// calculates integer 2-log such that:
|
||||
|
// 2^(two_log(x)) >= x > 2^(two_log(x) - 1)
|
||||
|
inline unsigned int two_log(unsigned int x){ |
||||
|
if(x <= 1) return 0; |
||||
|
return 8*sizeof(unsigned int) - unsigned(__builtin_clz(x-1)); |
||||
|
} |
||||
|
|
@ -1,21 +0,0 @@ |
|||||
#include <fingerprints.hpp> |
|
||||
#include <image_io.hpp> |
|
||||
|
|
||||
extern "C" { |
|
||||
#include <libavformat/avformat.h> |
|
||||
} |
|
||||
|
|
||||
#include <iostream> |
|
||||
|
|
||||
using namespace std; |
|
||||
|
|
||||
int main(){ |
|
||||
av_register_all(); |
|
||||
auto const image = open_image("test.jpg"); |
|
||||
|
|
||||
auto const x = rgb_wavelet_coefficients::calculate(image); |
|
||||
auto const y = rgb_wavelet_coefficients::pre_calculate(image); |
|
||||
|
|
||||
cout << x << endl; |
|
||||
cout << x.distance_to(y) << endl; |
|
||||
} |
|
Loading…
Reference in new issue