Commit bd922838 authored by Edwin Carlinet's avatar Edwin Carlinet
Browse files

Add challenge 2 app.

        *  apps/smartdoc/CMakeLists.txt,
        *  apps/smartdoc/border_detection.cpp,
        *  apps/smartdoc/border_detection.hpp,
        *  apps/smartdoc/challenge2.cpp,
        *  apps/smartdoc/unhomo.cpp,
        *  apps/smartdoc/unhomo.hpp: New.
parent 8dc9e4e6
......@@ -18,6 +18,8 @@ add_executable(smartdoc_app2 main2.cpp
add_executable(smartdoc_cli smartdoc-cli.cpp smartdoc.cpp)
add_executable(challenge2 challenge2.cpp unhomo.cpp border_detection.cpp)
target_link_libraries(smartdoc_cli ${FreeImage_LIBRARIES})
target_link_libraries(smartdoc_app
......@@ -28,7 +30,10 @@ target_link_libraries(smartdoc_app2
-lavcodec -lavformat -lswscale -lboost_date_time
${FreeImage_LIBRARIES})
target_link_libraries(challenge2 ${FreeImage_LIBRARIES})
set_source_files_properties(video_tools.cpp PROPERTIES
COMPILE_DEFINITIONS __STDC_CONSTANT_MACROS)
#target_set_properties(
\ No newline at end of file
set_property(TARGET challenge2 APPEND PROPERTY
INCLUDE_DIRECTORIES /work/carlinet/src/eigen/)
\ No newline at end of file
#include "border_detection.hpp"
#include <mln/core/neighb2d.hpp>
#include <mln/io/imsave.hpp>
#include <mln/colors/literal.hpp>
#include <mln/labeling/blobs.hpp>
#include <mln/labeling/accumulate.hpp>
#include <mln/accu/accumulators/count.hpp>
#include <mln/accu/accumulators/infsup.hpp>
#include <mln/morpho/structural/opening.hpp>
#include <Eigen/Dense>
typedef Eigen::Array<double, Eigen::Dynamic, 2> MyMatrix;
static
void
regression(const MyMatrix& A, double& a, double& b, double& c)
{
auto psum = A.colwise().sum();
auto psum2 = A.square().colwise().sum();
double sxi = psum(0);
double syi = psum(1);
double sxiyi = A.rowwise().prod().sum();
double sxi2 = psum2(0);
double syi2 = psum2(1);
double delta = (sxi2 * syi2) - (sxiyi*sxiyi);
double alpha = syi2; // /delta;
double beta = -sxiyi; // /delta;
double gamma = sxi2; // / delta;
a = alpha * sxi + beta * syi;
b = beta * sxi + gamma * syi;
c = delta;
//std::cout << "D: " << delta << std::endl;
//std::cout << "a: " << a << " b: " << b << std::endl;
}
static
std::pair<int, int>
detect_start_end_x(const std::vector<mln::point2d>& V)
{
int s = START_END_STEP * BORDER_IN;
int start, end;
int i;
for (i = s; i < (V.size() - s); i += BORDER_IN)
if (abs(V[i][1] - V[i-s][1]) < START_END_STEP_T)
break;
start = i;
for (i = (V.size() - s - 1); i >= 0; i -= BORDER_IN)
if (abs(V[i][1] - V[i+s][1]) < START_END_STEP_T)
break;
end = i;
//std::cout << "Start: " << start << " end: " << end << std::endl;
return {start, end};
}
static
std::pair<int, int>
detect_start_end_y(const std::vector<mln::point2d>& V)
{
int s = START_END_STEP * BORDER_IN;
int start, end;
int i;
for (i = s; i < (V.size() - s); i += BORDER_IN)
if (abs(V[i][0] - V[i-s][0]) < START_END_STEP_T)
break;
start = i;
for (i = (V.size() - s - 1); i >= 0; i -= BORDER_IN)
if (abs(V[i][0] - V[i+s][0]) < START_END_STEP_T)
break;
end = i;
//std::cout << "Start: " << start << " end: " << end << std::endl;
return {start, end};
}
static
mln::point2df
compute_inter(double a1, double b1, double c1,
double a2, double b2, double c2)
{
float y = (a2*c1-a1*c2) / (b1*a2 - a1*b2);
float x = (b2*c1-b1*c2) / (a1*b2 - b1*a2);
return {y,x};
}
pquad_t
border_detection(const mln::image2d<mln::rgb8>& f,
const char* debug_filename)
{
using namespace mln;
mln_entering("border_detection");
image2d<uint8> dist;
resize(dist, f).border(5);
transform(f, [](rgb8 x) -> uint8 {
int d1 = l1norm(x - colors::literal::red);
int d2 = l1norm(x - colors::literal::black);
int d = std::min(d1, d2);
return std::min<int>(d/3, 255);
}, dist);
//auto myse1 = make_rectangle2d(11,1);
auto myse2 = make_rectangle2d(1,11);
{
auto tmp = transpose(dist);
tmp = morpho::structural::opening(tmp, myse2);
transpose(tmp, dist);
}
dist = morpho::structural::opening(dist, myse2);
image2d<unsigned> lbls;
unsigned nlabel;
{
auto bin = dist > RED_THRESHOLD;
std::tie(lbls, nlabel) = labeling::blobs(bin, c8, 0u);
}
auto counts = labeling::p_accumulate(lbls, nlabel, accu::features::count<> ());
// Compute the subdomain of interest
box2d subdomain;
image2d<bool> bin;
{
unsigned imax = 1;
for (unsigned i = 1; i <= nlabel; ++i)
if (counts[i] > counts[imax])
imax = i;
accu::accumulators::infsup<point2d> acc;
acc.init();
mln_foreach(auto px, lbls.pixels())
if (px.val() == imax)
acc.take(px.point());
subdomain = {accu::extractor::inf(acc) - 50,
accu::extractor::sup(acc) + 10};
bin = image2d<bool>(subdomain.shape()[0],
subdomain.shape()[1]);
copy((lbls == imax) | subdomain, bin);
//bin = eval((lbls == imax) | subdomain);
}
// Compute the equations
// ax + by = 1
double a1, b1, c1;
double a2, b2, c2;
double a3, b3, c3;
double a4, b4, c4;
int nrows = bin.nrows(), ncols = bin.ncols();
{
std::vector<point2d> data;
MyMatrix A;
int begin, end;
// Left
{
for (int i = 0; i < nrows; ++i)
{
int count = 0;
for (int j = 0; j < ncols and count < 5; ++j)
if (bin.at(i,j))
{
data.push_back({(short)i,(short)j});
++count;
}
}
std::tie(begin, end) = detect_start_end_x(data);
//std::cout << "Start: " << (data[begin]+subdomain.pmin) << " end: " << (data[end] + subdomain.pmin) << std::endl;
int n = end-begin;
A.resize(n, 2);
for (int i = begin; i < end; ++i) {
A(i-begin,0) = data[i][1];
A(i-begin,1) = data[i][0];
}
regression(A, a1, b1, c1);
}
// Right
{
data.clear();
for (int i = 0; i < nrows; ++i)
{
int count = 0;
for (int j = ncols-1; j >= 0 and count < 5; --j)
if (bin.at(i,j))
{
data.push_back({(short)i,(short)j});
++count;
}
}
std::tie(begin, end) = detect_start_end_x(data);
int n = end-begin;
A.resize(n, 2);
for (int i = begin; i < end; ++i) {
A(i-begin,0) = data[i][1];
A(i-begin,1) = data[i][0];
}
regression(A, a2, b2, c2);
}
// Up
{
data.clear();
for (int j = 0; j < ncols; ++j)
{
int count = 0;
for (int i = 0; i < nrows and count < 5; ++i)
{
if (bin.at(i,j))
{
data.push_back({(short)i,(short)j});
++count;
}
}
}
std::tie(begin, end) = detect_start_end_y(data);
int n = end-begin;
A.resize(n, 2);
for (int i = begin; i < end; ++i) {
A(i-begin,0) = data[i][1];
A(i-begin,1) = data[i][0];
}
regression(A, a3, b3, c3);
}
// Down
{
data.clear();
for (int j = 0; j < ncols; ++j)
{
int count = 0;
for (int i = nrows-1; i >= 0 and count < 5; --i)
{
if (bin.at(i,j))
{
data.push_back({(short)i,(short)j});
++count;
}
}
}
std::tie(begin, end) = detect_start_end_y(data);
int n = end-begin;
A.resize(n, 2);
for (int i = begin; i < end; ++i) {
A(i-begin,0) = data[i][1];
A(i-begin,1) = data[i][0];
}
regression(A, a4, b4, c4);
}
}
if (debug_filename) {
image2d<rgb8> out = clone(f);
for (short x = 0; x < f.ncols(); ++x)
{
short y1 = (c3 - a3 * x) / b3;
short y2 = (c4 - a4 * x) / b4;
point2d p = point2d{y1,x} + subdomain.pmin;
point2d q = point2d{y2,x} + subdomain.pmin;
if (out.domain().has(p)) out(p)[1] += 127;
if (out.domain().has(q)) out(q)[1] += 127;
}
for (short y = 0; y < f.nrows(); ++y)
{
short x1 = (c1 - b1 * y) / a1;
short x2 = (c2 - b2 * y) / a2;
point2d p = point2d{y,x1} + subdomain.pmin;
point2d q = point2d{y,x2} + subdomain.pmin;
if (out.domain().has(p)) out(p)[1] += 127;
if (out.domain().has(q)) out(q)[1] += 127;
}
io::imsave(out, debug_filename);
}
pquad_t res;
point2df tl = compute_inter(a1,b1,c1,a3,b3,c3) + subdomain.pmin;
point2df tr = compute_inter(a2,b2,c2,a3,b3,c3) + subdomain.pmin;
point2df bl = compute_inter(a1,b1,c1,a4,b4,c4) + subdomain.pmin;
point2df br = compute_inter(a2,b2,c2,a4,b4,c4) + subdomain.pmin;
// on est trouné à 90 degrés.
res[0] = bl;
res[1] = tl;
res[2] = br;
res[3] = tr;
mln_exiting();
return res;
}
#ifndef APPS_SMARTDOC_BORDER_DETECTION_HPP
# define APPS_SMARTDOC_BORDER_DETECTION_HPP
#include <mln/core/image/image2d.hpp>
#include <mln/core/colors.hpp>
#include "unhomo.hpp"
/*********************************************/
/*** CONFIGURATION ***/
/*********************************************/
static constexpr unsigned RED_THRESHOLD = 117; //117; //350;
static constexpr unsigned BORDER_IN = 3;
static constexpr unsigned START_END_STEP = 30;
static constexpr unsigned START_END_STEP_T = 10;
/// \brief Detect the document and return the corner points.
/// If \p debug_filename is non-NULL, it outputs the image with
/// the border drawn in blue.
pquad_t
border_detection(const mln::image2d<mln::rgb8>& f,
const char* debug_filename = NULL);
#endif // ! APPS_SMARTDOC_BORDER_DETECTION_HPP
#include "border_detection.hpp"
#include "unhomo.hpp"
#include <mln/io/imread.hpp>
#include <mln/io/imsave.hpp>
int main(int argc, char** argv)
{
if (argc < 3)
{
std::cerr << "Usage: " << argv[0] << " input.jpg output.jpg [detection.jpg]\n"
;
std::exit(1);
}
using namespace mln;
image2d<rgb8> f, out;
io::imread(argv[1], f);
const char* debug_filename = (argc > 3) ? argv[3] : NULL;
pquad_t pts = border_detection(f, debug_filename);
out = unhomo(f, pts);
io::imsave(out, argv[2]);
}
#include "unhomo.hpp"
#include <mln/core/trace.hpp>
struct frgb
{
float red, green, blue;
frgb() {}
frgb(float r, float g, float b) : red(r), green(g), blue(b) {}
frgb(const mln::rgb8& c) : red(c[0]), green(c[1]), blue(c[2]) {}
void set_inter(const frgb& c1, const frgb& c2, float delta)
{
red = c1.red + delta * (c2.red - c1.red);
green = c1.green + delta * (c2.green - c1.green);
blue = c1.blue + delta * (c2.blue - c1.blue);
}
operator mln::rgb8() const
{
return {mln::uint8(red+0.4999), mln::uint8(green+0.4999),
mln::uint8(blue+0.4999)};
}
};
template <unsigned i>
struct B_;
template <>
struct B_<0> {
static float on(float u) { return (1.f - u) * (1.f - u) * (1.f - u); }
};
template <>
struct B_<1> {
static float on(float u) { return 3.f * u * (1.f - u) * (1.f - u); }
};
template <>
struct B_<2> {
static float on(float u) { return 3.f * u * u * (1.f - u); }
};
template <>
struct B_<3> {
static float on(float u) { return u * u * u; }
};
struct bezier
{
mln::point2df p[4][4]; // control points
void interpolate_(mln::point2df start,
mln::point2df end,
mln::point2df& one_third,
mln::point2df& two_third)
{
mln::point2df diff = (end - start) / 3;
one_third = start + diff;
two_third = end - diff;
}
void init()
{
interpolate_(p[0][0], p[0][3], p[0][1], p[0][2]);
interpolate_(p[0][0], p[3][0], p[1][0], p[2][0]);
interpolate_(p[3][0], p[3][3], p[3][1], p[3][2]);
interpolate_(p[0][3], p[3][3], p[1][3], p[2][3]);
interpolate_(p[1][0], p[1][3], p[1][1], p[1][2]);
interpolate_(p[2][0], p[2][3], p[2][1], p[2][2]);
}
mln::point2df Q(float u, float v) const
{
float
B0u = B_<0>::on(u),
B1u = B_<1>::on(u),
B2u = B_<2>::on(u),
B3u = B_<3>::on(u),
B0v = B_<0>::on(v),
B1v = B_<1>::on(v),
B2v = B_<2>::on(v),
B3v = B_<3>::on(v);
return
p[0][0] * B0u * B0v +
p[0][1] * B0u * B1v +
p[0][2] * B0u * B2v +
p[0][3] * B0u * B3v +
p[1][0] * B1u * B0v +
p[1][1] * B1u * B1v +
p[1][2] * B1u * B2v +
p[1][3] * B1u * B3v +
p[2][0] * B2u * B0v +
p[2][1] * B2u * B1v +
p[2][2] * B2u * B2v +
p[2][3] * B2u * B3v +
p[3][0] * B3u * B0v +
p[3][1] * B3u * B1v +
p[3][2] * B3u * B2v +
p[3][3] * B3u * B3v;
}
mln::rgb8 bilinear(const mln::image2d<mln::rgb8>& input,
mln::point2df fp)
{
constexpr mln::point2d down = {1,0};
constexpr mln::point2d right = {0,1};
mln::point2d p = fp;
mln::point2df delta = fp - p;
float dr = delta[0], dc = delta[1];
frgb top, bot, c;
top.set_inter(input(p), input(p + right), dc);
bot.set_inter(input(p + down), input(p + down + right), dc);
c.set_inter(top, bot, dr);
return c;
}
void fill(const mln::image2d<mln::rgb8>& input,
mln::image2d<mln::rgb8>& output)
{
init();
const unsigned
nrows = output.nrows(),
ncols = output.ncols();
for (unsigned row = 0; row < nrows; ++row)
for (unsigned col = 0; col < ncols; ++col)
{
float
u = float(row) / float(nrows),
v = float(col) / float(ncols);
mln::point2df p = Q(u, v);
output.at(row, col) = bilinear(input, p);
}
}
};
mln::image2d<mln::rgb8>
unhomo(const mln::image2d<mln::rgb8>& f,
pquad_t points)
{
mln_entering("unhomo");
bezier b;
b.p[0][0] = points[0];
b.p[0][3] = points[1];
b.p[3][0] = points[2];
b.p[3][3] = points[3];
mln::image2d<mln::rgb8> output(2970, 2100);
b.fill(f, output);
mln_exiting();
return output;
}
#ifndef APPS_SMARTDOC_UNHOMO_HPP
# define APPS_SMARTDOC_UNHOMO_HPP
# include <mln/core/image/image2d.hpp>
# include <mln/core/colors.hpp>
# include <array>
// In the order: Top Left, Top Right, Bottom Left, Bottom Right
typedef std::array<mln::point2df, 4> pquad_t;
mln::image2d<mln::rgb8>
unhomo(const mln::image2d<mln::rgb8>& f,
pquad_t points);
#endif // ! APPS_SMARTDOC_UNHOMO_HPP
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment