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 |
|||
#define READ_DATABASE_HPP |
|||
#pragma once |
|||
|
|||
#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