Compare commits
No commits in common. "master" and "master" have entirely different histories.
11 changed files with 56 additions and 947 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -19,10 +19,5 @@ compile_commands.json
|
||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
/build/
|
/build/
|
||||||
|
|
||||||
# generated code (unit tests)
|
|
||||||
Test*.cpp
|
|
||||||
|
|
||||||
# executables
|
# executables
|
||||||
/lines
|
/lines
|
||||||
unittest_*
|
|
||||||
gmon.out
|
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
cmake_minimum_required( VERSION 2.8 )
|
cmake_minimum_required( VERSION 2.8 )
|
||||||
project( bs_dsp )
|
project( bs_dsp )
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE Debug)
|
|
||||||
endif(NOT CMAKE_BUILD_TYPE)
|
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -Werror -pedantic")
|
|
||||||
set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Werror -pedantic -pg")
|
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -pedantic -D_DEBUG")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Eigen library
|
# Eigen library
|
||||||
include_directories( "/usr/include/eigen3/" )
|
include_directories( "/usr/include/eigen3/" )
|
||||||
|
|
||||||
|
# OpenCV
|
||||||
|
find_package( OpenCV 3.1 REQUIRED )
|
||||||
|
|
||||||
# CUDA (optional)
|
# CUDA
|
||||||
find_package( CUDA )
|
find_package( CUDA )
|
||||||
if (CUDA_FOUND)
|
if (CUDA_FOUND)
|
||||||
include( FindCUDA )
|
include( FindCUDA )
|
||||||
|
@ -26,31 +17,5 @@ else()
|
||||||
message(STATUS "CUDA not found; will skip bs_dsp")
|
message(STATUS "CUDA not found; will skip bs_dsp")
|
||||||
endif (CUDA_FOUND)
|
endif (CUDA_FOUND)
|
||||||
|
|
||||||
# OpenCV
|
add_executable( lines lines.cpp )
|
||||||
find_package( OpenCV REQUIRED )
|
|
||||||
if(OpenCV_VERSION VERSION_LESS "2.4.8")
|
|
||||||
message(FATAL_ERROR "ERROR: Can't find OpenCV 2.4.8+")
|
|
||||||
endif()
|
|
||||||
if(OpenCV_VERSION VERSION_LESS "3.0")
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAS_OPENCV2")
|
|
||||||
else()
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHAS_OPENCV3")
|
|
||||||
endif (OpenCV_VERSION VERSION_LESS "3.0")
|
|
||||||
|
|
||||||
#CxxTest
|
|
||||||
find_package(CxxTest)
|
|
||||||
if(CXXTEST_FOUND)
|
|
||||||
include_directories(${CXXTEST_INCLUDE_DIR})
|
|
||||||
enable_testing()
|
|
||||||
CXXTEST_ADD_TEST(unittest_changecoord TestChangeCoord.cpp TestChangeCoord.h geometry.cpp)
|
|
||||||
CXXTEST_ADD_TEST(unittest_line TestLine.cpp TestLine.h geometry.cpp)
|
|
||||||
target_link_libraries(unittest_changecoord ${OpenCV_LIBS})
|
|
||||||
target_link_libraries(unittest_line ${OpenCV_LIBS})
|
|
||||||
else()
|
|
||||||
message(STATUS "Warning: Can't find CxxTest; testing will be disabled")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable( lines lines.cpp geometry.cpp cvutils.cpp)
|
|
||||||
target_link_libraries( lines ${OpenCV_LIBS} )
|
target_link_libraries( lines ${OpenCV_LIBS} )
|
||||||
|
|
||||||
# vim: set et ts=4 sw=4:
|
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
using namespace cv;
|
|
||||||
#include <cxxtest/TestSuite.h>
|
|
||||||
|
|
||||||
#include "geometry.h"
|
|
||||||
|
|
||||||
class TestChangeCoord : public CxxTest::TestSuite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void testOrigin(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(1,2);
|
|
||||||
//the angle here is actually irrelevant
|
|
||||||
Point p = map_point(M_PI/2, origin, origin);
|
|
||||||
TS_ASSERT_EQUALS(p.x, 0);
|
|
||||||
TS_ASSERT_EQUALS(p.y, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyTranslation(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(1,2);
|
|
||||||
Point p = map_point(0, origin, Point(0,0));
|
|
||||||
TS_ASSERT_EQUALS(p.x, -1);
|
|
||||||
TS_ASSERT_EQUALS(p.y, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotationOrigin(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = map_point(M_PI, origin, origin);
|
|
||||||
TS_ASSERT_EQUALS(p.x, 0);
|
|
||||||
TS_ASSERT_EQUALS(p.y, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation180(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = map_point(M_PI, origin, Point(1,2));
|
|
||||||
TS_ASSERT_EQUALS(p.x, -1);
|
|
||||||
TS_ASSERT_EQUALS(p.y, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation90(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = map_point(M_PI/2, origin, Point(1,2));
|
|
||||||
TS_ASSERT_EQUALS(p.x, 2);
|
|
||||||
TS_ASSERT_EQUALS(p.y, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation30(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = map_point(M_PI/6, origin, Point(200,0));
|
|
||||||
TS_ASSERT_EQUALS(p.y, -100);
|
|
||||||
TS_ASSERT_LESS_THAN(p.x, 180); //0.866*2 ~= 1.73
|
|
||||||
TS_ASSERT_LESS_THAN(170, p.x); //0.866*2 ~= 1.73
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestChangeBack : public CxxTest::TestSuite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void testOrigin(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(1,2);
|
|
||||||
Point p = unmap_point(M_PI/2, origin, Point(0,0));
|
|
||||||
TS_ASSERT_EQUALS(p.x, 1);
|
|
||||||
TS_ASSERT_EQUALS(p.y, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyTranslation(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(1,2);
|
|
||||||
Point p = unmap_point(0, origin, Point(-1,-2));
|
|
||||||
TS_ASSERT_EQUALS(p.x, 0);
|
|
||||||
TS_ASSERT_EQUALS(p.y, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotationOrigin(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = unmap_point(M_PI, origin, origin);
|
|
||||||
TS_ASSERT_EQUALS(p.x, 0);
|
|
||||||
TS_ASSERT_EQUALS(p.y, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation180(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = unmap_point(M_PI, origin, Point(1,2));
|
|
||||||
TS_ASSERT_EQUALS(p.x, -1);
|
|
||||||
TS_ASSERT_EQUALS(p.y, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation90(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = unmap_point(M_PI/2, origin, Point(-2,1));
|
|
||||||
TS_ASSERT_EQUALS(p.x, -1);
|
|
||||||
TS_ASSERT_EQUALS(p.y, -2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testOnlyRotation30(void)
|
|
||||||
{
|
|
||||||
Point origin = Point(0,0);
|
|
||||||
Point p = unmap_point(M_PI/6, origin, Point(200,0));
|
|
||||||
TS_ASSERT_LESS_THAN(p.x, 175); //0.866*2 ~= 1.73
|
|
||||||
TS_ASSERT_LESS_THAN(170, p.x); //0.866*2 ~= 1.73
|
|
||||||
TS_ASSERT_EQUALS(100, p.y);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TestCoordSystem : public CxxTest::TestSuite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void testCreate(void)
|
|
||||||
{
|
|
||||||
auto cs = CoordinateSystem(Point(100,200), Point(200,300));
|
|
||||||
TS_ASSERT_EQUALS(cs.map(Point(300,400)).y, 0);
|
|
||||||
TS_ASSERT_EQUALS(cs.map(Point(200,100)).x, 0); //moving orthogonally
|
|
||||||
}
|
|
||||||
void testInverse(void)
|
|
||||||
{
|
|
||||||
CoordinateSystem(Point(200,300), Point(100,200));
|
|
||||||
}
|
|
||||||
};
|
|
49
TestLine.h
49
TestLine.h
|
@ -1,49 +0,0 @@
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
using namespace cv;
|
|
||||||
#include <cxxtest/TestSuite.h>
|
|
||||||
|
|
||||||
#include "geometry.h"
|
|
||||||
|
|
||||||
class TestLine : public CxxTest::TestSuite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void testBisector(void)
|
|
||||||
{
|
|
||||||
// x-y = 0
|
|
||||||
auto l = get_line(Point(0,0), Point(1,1));
|
|
||||||
TS_ASSERT_EQUALS(l(0), -l(1));
|
|
||||||
TS_ASSERT_EQUALS(l(2), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testBisectorNegative(void)
|
|
||||||
{
|
|
||||||
// x+y = 0
|
|
||||||
auto l = get_line(Point(0,0), Point(1,-1));
|
|
||||||
TS_ASSERT_EQUALS(l(0), l(1));
|
|
||||||
TS_ASSERT_EQUALS(l(2), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void testConstant(void)
|
|
||||||
{
|
|
||||||
// y = 42
|
|
||||||
// 0x+1y-42=0
|
|
||||||
auto l = get_line(Point(0,42), Point(10,42));
|
|
||||||
TS_ASSERT_EQUALS(l(0), 0);
|
|
||||||
TS_ASSERT_EQUALS(l(2), -42*l(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void testVertical(void)
|
|
||||||
{
|
|
||||||
// x = 42
|
|
||||||
// 1x+0y-42=0
|
|
||||||
auto l = get_line(Point(42,0), Point(42,10));
|
|
||||||
TS_ASSERT_EQUALS(l(1), 0);
|
|
||||||
TS_ASSERT_EQUALS(l(2), -42*l(0));
|
|
||||||
}
|
|
||||||
};
|
|
156
cvutils.cpp
156
cvutils.cpp
|
@ -1,156 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "geometry.h"
|
|
||||||
#include "cvutils.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
||||||
|
|
||||||
// takes the two points that are more distant from the next, ie:
|
|
||||||
// i st distance(hull[i], hull[i+1]) is maximum and 2nd maximum
|
|
||||||
unsigned* max2_distance(std::vector<cv::Point> hull)
|
|
||||||
{
|
|
||||||
#ifdef _DEBUG
|
|
||||||
std::cout << "Hull = ";
|
|
||||||
for(auto p: hull) {
|
|
||||||
std::cout << p << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
#endif
|
|
||||||
unsigned *idx = (unsigned*) calloc(2, sizeof(int));
|
|
||||||
double distance[2] = {0};
|
|
||||||
for( unsigned i=0; hull.size()>i; i++ )
|
|
||||||
{
|
|
||||||
cv::Point thispoint=hull[i];
|
|
||||||
cv::Point nextpoint=hull[(i+1)%hull.size()];
|
|
||||||
double d=dist(thispoint,nextpoint);
|
|
||||||
if( d>distance[0] ) //the biggest till now
|
|
||||||
{
|
|
||||||
idx[1]=idx[0];
|
|
||||||
idx[0]=i;
|
|
||||||
distance[1]=distance[0];
|
|
||||||
distance[0]=d;
|
|
||||||
}
|
|
||||||
else if( d>distance[1] ) // it is the second biggest till now
|
|
||||||
{
|
|
||||||
idx[1]=i;
|
|
||||||
distance[1]=d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* check if a,b,newpoint belong to the same line (with minor approximation) */
|
|
||||||
bool similar_fit(cv::Point a, cv::Point b, cv::Point newpoint) {
|
|
||||||
return similar_fit(a, b, newpoint, 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool similar_fit(cv::Point a, cv::Point b, cv::Point newpoint, float tolerance_degrees) {
|
|
||||||
double angle = inner_angle(a, b, newpoint) * 180 / M_PI;
|
|
||||||
|
|
||||||
if( angle > (180-tolerance_degrees) && angle < (180+tolerance_degrees) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool similar_fit(std::vector<cv::Point> group, cv::Point newpoint) {
|
|
||||||
// TODO: it should perform a fit instead of just taking first and last
|
|
||||||
assert(group.size()>=2);
|
|
||||||
return similar_fit(group[0], group[group.size()-1], newpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simplify_hull will group all the points of the hull in groups that fit
|
|
||||||
* each group is a "line"
|
|
||||||
*/
|
|
||||||
std::vector<std::vector<cv::Point>>
|
|
||||||
simplify_hull(std::vector<cv::Point> hull) {
|
|
||||||
return simplify_hull( hull, 0 );
|
|
||||||
}
|
|
||||||
std::vector<std::vector<cv::Point>>
|
|
||||||
simplify_hull(std::vector<cv::Point> hull, double mindistance) {
|
|
||||||
//each element is a group of point; a group of point is a vector of points that were
|
|
||||||
//contigous and that "fit together"
|
|
||||||
std::vector<std::vector<cv::Point>> pointgroups;
|
|
||||||
std::vector<cv::Point> *group = new std::vector<cv::Point>();
|
|
||||||
cv::Point last, current;
|
|
||||||
group->push_back(hull[0]);
|
|
||||||
group->push_back(hull[1]);
|
|
||||||
for( unsigned i=2; hull.size()>i; i++ )
|
|
||||||
{
|
|
||||||
current = hull[i];
|
|
||||||
last = (*group)[group->size()-1];
|
|
||||||
if( similar_fit((*group)[0], last, hull[i]) )
|
|
||||||
{
|
|
||||||
group->push_back( hull[i] );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( dist((*group)[0],last)>=mindistance )
|
|
||||||
pointgroups.push_back(*group);
|
|
||||||
group = new std::vector<cv::Point>();
|
|
||||||
group->push_back(last);
|
|
||||||
group->push_back(hull[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pointgroups.push_back(*group);
|
|
||||||
//TODO: first and last might just be the same
|
|
||||||
return pointgroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* find_longest_line: walks along the hull starting at "begin" and ending at "end", and find the
|
|
||||||
* longest (well, not strictl: it's a very simple eurhistics) group of points
|
|
||||||
* that are aligned we don't care about the number of points, but about the
|
|
||||||
* euclidean distance between the first and the last
|
|
||||||
* arg hull: the whole convex hull
|
|
||||||
* arg begin: index of the array to start from
|
|
||||||
* arg end: index of the array to end. It can be lower than start, in which case the hull is used
|
|
||||||
* "circularly"
|
|
||||||
* returns: a vector of useful points. Just use the first and the last, do not expect that the points in
|
|
||||||
* between are added to the vector
|
|
||||||
*/
|
|
||||||
std::vector<cv::Point> //two points, actually
|
|
||||||
find_longest_line(std::vector<cv::Point> hull, unsigned begin, unsigned end) //the hull can be open
|
|
||||||
{
|
|
||||||
std::vector<cv::Point> bestline, thisline;
|
|
||||||
int bestdistance = 0;
|
|
||||||
thisline.push_back(hull[(begin)%hull.size()]);
|
|
||||||
thisline.push_back(hull[(begin+1)%hull.size()]);
|
|
||||||
for(unsigned i=(begin+2)%hull.size(); i!=end; i++)
|
|
||||||
{
|
|
||||||
assert(2<=thisline.size());
|
|
||||||
if(i==hull.size()) {
|
|
||||||
i=0;
|
|
||||||
}
|
|
||||||
assert(i < hull.size());
|
|
||||||
if(similar_fit(thisline, hull[i])) {
|
|
||||||
thisline.push_back(hull[i]);
|
|
||||||
} else { // considering if the just-finished line is the best
|
|
||||||
double thisdistance = dist(thisline[0],thisline[thisline.size()-1]);
|
|
||||||
if(thisdistance>bestdistance) {
|
|
||||||
bestline = thisline;
|
|
||||||
bestdistance = thisdistance;
|
|
||||||
}
|
|
||||||
thisline.clear();
|
|
||||||
assert(bestline.size()>=2);
|
|
||||||
thisline.push_back(hull[(i-1)%hull.size()]);
|
|
||||||
thisline.push_back(hull[i]);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
double thisdistance = dist(thisline[0],thisline[thisline.size()-1]);
|
|
||||||
if(thisdistance>bestdistance) { // this is relevant if the best line ends at the last point
|
|
||||||
bestline = thisline;
|
|
||||||
bestdistance = thisdistance;
|
|
||||||
}
|
|
||||||
assert(bestline.size()>=2);
|
|
||||||
return bestline;
|
|
||||||
}
|
|
||||||
|
|
22
cvutils.h
22
cvutils.h
|
@ -1,22 +0,0 @@
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
|
|
||||||
unsigned* max2_distance(std::vector<cv::Point> hull);
|
|
||||||
|
|
||||||
bool
|
|
||||||
similar_fit(cv::Point a, cv::Point b, cv::Point newpoint);
|
|
||||||
bool
|
|
||||||
similar_fit(cv::Point a, cv::Point b, cv::Point newpoint, float tolerance_degrees);
|
|
||||||
bool
|
|
||||||
similar_fit(std::vector<cv::Point> group, cv::Point newpoint);
|
|
||||||
|
|
||||||
std::vector<std::vector<cv::Point>>
|
|
||||||
simplify_hull(std::vector<cv::Point> hull);
|
|
||||||
std::vector<std::vector<cv::Point>>
|
|
||||||
simplify_hull(std::vector<cv::Point> hull, double mindistance);
|
|
||||||
|
|
||||||
std::vector<cv::Point>
|
|
||||||
find_longest_line(std::vector<cv::Point> hull, unsigned begin, unsigned end);
|
|
|
@ -1,53 +0,0 @@
|
||||||
#!/usr/bin/env python2
|
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
import sys
|
|
||||||
import cv2
|
|
||||||
cv_version = int(cv2.__version__.split('.')[0])
|
|
||||||
GRAY = cv2.CV_LOAD_IMAGE_GRAYSCALE if cv_version == 2 else cv2.IMREAD_GRAYSCALE
|
|
||||||
COLOR = cv2.CV_LOAD_IMAGE_COLOR if cv_version == 2 else cv2.IMREAD_COLOR
|
|
||||||
|
|
||||||
|
|
||||||
def show(img):
|
|
||||||
#cv2.namedWindow("immagine",cv2.WINDOW_AUTOSIZE)
|
|
||||||
cv2.imshow("immagine", img)
|
|
||||||
cv2.waitKey(0)
|
|
||||||
|
|
||||||
|
|
||||||
def contourIndex(lista):
|
|
||||||
# ritorna l'indice del contorno con perimetro maggiore
|
|
||||||
val=-1
|
|
||||||
index=0
|
|
||||||
for num, cont in enumerate(lista):
|
|
||||||
value=cv2.arcLength(cont,True)
|
|
||||||
if val<value:
|
|
||||||
val=value
|
|
||||||
index=num
|
|
||||||
return index
|
|
||||||
|
|
||||||
img=cv2.imread(sys.argv[1], GRAY)
|
|
||||||
#img=cv2.imread(sys.argv[1],cv2.CV_LOAD_IMAGE_COLOR)
|
|
||||||
#la riga successiva serve a rendere visualizzabile l immagine sul nostro pc
|
|
||||||
#img=cv2.resize(img,(350,262))
|
|
||||||
#assumendo che il background e nero nelle condizioni attuali (27/12/2016) del bookscanner il valore buono e 100 (su 255)
|
|
||||||
buono=100
|
|
||||||
mask=cv2.inRange(img,buono,255)
|
|
||||||
if cv_version == 2:
|
|
||||||
contorni, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
else:
|
|
||||||
_, contorni, hier = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
|
||||||
bianco=np.zeros(mask.shape,dtype=np.uint8) # crea un coso nero
|
|
||||||
|
|
||||||
# indexImportante indica il contorno piu grande, che crediamo sia quello del libro
|
|
||||||
indexImportante=contourIndex(contorni)
|
|
||||||
cv2.drawContours(img,contorni,indexImportante,255,thickness=3)
|
|
||||||
cv2.fillPoly(bianco,[contorni[indexImportante]], 255)
|
|
||||||
|
|
||||||
img=cv2.imread(sys.argv[1], COLOR)
|
|
||||||
img = cv2.bitwise_and(img, img, mask=bianco)
|
|
||||||
if len(sys.argv) > 2:
|
|
||||||
cv2.imwrite(sys.argv[2], img)
|
|
||||||
else:
|
|
||||||
show(img)
|
|
||||||
|
|
||||||
# vim: set ts=4 sw=4 et:
|
|
136
geometry.cpp
136
geometry.cpp
|
@ -1,136 +0,0 @@
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
using namespace cv;
|
|
||||||
|
|
||||||
#include "geometry.h"
|
|
||||||
|
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
||||||
|
|
||||||
double dist(Point a, Point b)
|
|
||||||
{
|
|
||||||
return cv::norm(a-b);
|
|
||||||
}
|
|
||||||
|
|
||||||
double inner_angle(Point a, Point middle, Point b)
|
|
||||||
{
|
|
||||||
Point v1 = a - middle;
|
|
||||||
Point v2 = b - middle;
|
|
||||||
double v1_length = dist(Point(0, 0), v1);
|
|
||||||
double v2_length = dist(Point(0, 0), v2);
|
|
||||||
return acos( (v1.x*v2.x + v1.y*v2.y) / (v1_length*v2_length) );
|
|
||||||
}
|
|
||||||
|
|
||||||
CoordinateSystem::CoordinateSystem(double angle, Point a)
|
|
||||||
{
|
|
||||||
theta = angle;
|
|
||||||
origin = a;
|
|
||||||
precompute();
|
|
||||||
assert(map(a) == Point(0,0));
|
|
||||||
assert(unmap(Point(0,0)) == a);
|
|
||||||
}
|
|
||||||
CoordinateSystem::CoordinateSystem(Point a, Point b)
|
|
||||||
{
|
|
||||||
theta = atan2(b.y-a.y, b.x-a.x);
|
|
||||||
origin = a;
|
|
||||||
precompute();
|
|
||||||
assert(map(a) == Point(0,0));
|
|
||||||
assert(unmap(Point(0,0)) == a);
|
|
||||||
assert(map(b).y == 0);
|
|
||||||
assert(map(b).x > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CoordinateSystem::precompute() {
|
|
||||||
double costheta = cos(theta);
|
|
||||||
double sintheta = sin(theta);
|
|
||||||
R(0,0) = costheta;
|
|
||||||
R(0,1) = -sintheta;
|
|
||||||
R(1,0) = sintheta;
|
|
||||||
R(1,1) = costheta;
|
|
||||||
Rt(0,0) = costheta;
|
|
||||||
Rt(0,1) = sintheta;
|
|
||||||
Rt(1,0) = -sintheta;
|
|
||||||
Rt(1,1) = costheta;
|
|
||||||
}
|
|
||||||
Point CoordinateSystem::map(Point tomap) {
|
|
||||||
//that's the algorithm: res = Rt*(tomap - origin)
|
|
||||||
cv::Vec<double,2> p_as_vector;
|
|
||||||
Point p;
|
|
||||||
p = tomap - origin;
|
|
||||||
p_as_vector[0] = p.x;
|
|
||||||
p_as_vector[1] = p.y;
|
|
||||||
|
|
||||||
cv::Vec<double,2> res_as_vector = Rt*p_as_vector;
|
|
||||||
return Point(res_as_vector);
|
|
||||||
}
|
|
||||||
Point CoordinateSystem::unmap(Point tounmap) {
|
|
||||||
cv::Vec<double,2> q_as_vector;
|
|
||||||
q_as_vector[0] = tounmap.x;
|
|
||||||
q_as_vector[1] = tounmap.y;
|
|
||||||
Point RQ = Point(R*q_as_vector);
|
|
||||||
return Point(RQ + origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
Point
|
|
||||||
map_point(double theta, Point origin, Point tomap)
|
|
||||||
{
|
|
||||||
auto cs = CoordinateSystem(theta, origin);
|
|
||||||
return cs.map(tomap);
|
|
||||||
}
|
|
||||||
|
|
||||||
Point
|
|
||||||
unmap_point(double theta, Point origin, Point tounmap)
|
|
||||||
{
|
|
||||||
return CoordinateSystem(theta, origin).unmap(tounmap);
|
|
||||||
}
|
|
||||||
//that's the algorithm: res = Rt*(tomap - origin)
|
|
||||||
cv::Vec<double,3>
|
|
||||||
get_line(Point p, Point q)
|
|
||||||
{
|
|
||||||
cv::Vec<double,3> line; //the three positions are a,b,c as in: ax + by + c = 0
|
|
||||||
Point vector = q-p;
|
|
||||||
line(0) = - vector.y;
|
|
||||||
line(1) = vector.x;
|
|
||||||
line(2) = vector.y * p.x - vector.x * p.y;
|
|
||||||
assert(is_in_line(line, p));
|
|
||||||
assert(is_in_line(line, q));
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
is_in_line(cv::Vec<double,3> line, Point p)
|
|
||||||
{
|
|
||||||
return line_value(line, p) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
is_above_line(cv::Vec<double,3> line, Point p)
|
|
||||||
{
|
|
||||||
return line_value(line, p) > 0;
|
|
||||||
}
|
|
||||||
double line_value(cv::Vec<double,3> line, Point p)
|
|
||||||
{
|
|
||||||
return line(0)*p.x + line(1)*p.y + line(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Point
|
|
||||||
further_point_from_line(cv::Vec<double,3> line, std::vector<Point> points)
|
|
||||||
{
|
|
||||||
//distance = abs(a*p.x+b*p.y+c) / sqrt(a^2+b^2)
|
|
||||||
Point further = points[0];
|
|
||||||
double maxdist = -1;
|
|
||||||
for(Point p: points)
|
|
||||||
{
|
|
||||||
double numerator = abs(line(0)*p.x + line(1)*p.y + line(2));
|
|
||||||
if(numerator > maxdist) {
|
|
||||||
maxdist = numerator;
|
|
||||||
further = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return further;
|
|
||||||
}
|
|
30
geometry.h
30
geometry.h
|
@ -1,30 +0,0 @@
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
|
|
||||||
double dist(cv::Point, cv::Point);
|
|
||||||
double inner_angle(cv::Point, cv::Point, cv::Point);
|
|
||||||
cv::Point map_point(double, cv::Point, cv::Point);
|
|
||||||
cv::Point unmap_point(double, cv::Point, cv::Point);
|
|
||||||
cv::Vec<double,3> get_line(cv::Point, cv::Point);
|
|
||||||
bool is_in_line(cv::Vec<double,3> line, cv::Point p);
|
|
||||||
bool is_above_line(cv::Vec<double,3> line, cv::Point p);
|
|
||||||
double line_value(cv::Vec<double,3>, cv::Point);
|
|
||||||
cv::Point further_point_from_line(cv::Vec<double,3>, std::vector<cv::Point>);
|
|
||||||
|
|
||||||
class CoordinateSystem
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
double theta;
|
|
||||||
cv::Matx<double,2,2> R, Rt;
|
|
||||||
cv::Point origin;
|
|
||||||
void precompute();
|
|
||||||
public:
|
|
||||||
CoordinateSystem(cv::Point, cv::Point);
|
|
||||||
CoordinateSystem(double angle, cv::Point origin_);
|
|
||||||
cv::Point map(cv::Point p);
|
|
||||||
cv::Point unmap(cv::Point p);
|
|
||||||
};
|
|
||||||
|
|
367
lines.cpp
367
lines.cpp
|
@ -1,326 +1,63 @@
|
||||||
#include<cmath>
|
#include<opencv2/opencv.hpp>
|
||||||
#include<algorithm>
|
|
||||||
#include<unistd.h>
|
|
||||||
#include<fstream>
|
|
||||||
#include<iomanip>
|
|
||||||
|
|
||||||
#ifdef HAS_OPENCV3
|
|
||||||
#include <opencv2/core.hpp> //Any OPENCV3 code
|
|
||||||
#else
|
|
||||||
#include <opencv2/core/core.hpp> //Any Opencv2 code
|
|
||||||
#endif
|
|
||||||
#include"lines.h"
|
|
||||||
#include "geometry.h"
|
|
||||||
#include "cvutils.h"
|
|
||||||
using namespace cv;
|
|
||||||
|
|
||||||
#define likely(x) __builtin_expect(!!(x), 1)
|
|
||||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
||||||
|
|
||||||
// all those colors are still visible on b/w (the first component is not so low)
|
|
||||||
auto WHITE = cv::Scalar(255,255,255);
|
|
||||||
auto BLUE = cv::Scalar(200,50,50);
|
|
||||||
auto MAGENTA = cv::Scalar(110,10,200);
|
|
||||||
auto BROWN = cv::Scalar(90,100,60);
|
|
||||||
auto BLACK = cv::Scalar(0,0,0);
|
|
||||||
auto YELLOW = cv::Scalar(20,200,200);
|
|
||||||
|
|
||||||
void usage(char* program, std::ostream &buf) {
|
|
||||||
buf << "Usage: " << program << "[options] IMAGE-INPUT" << std::endl;
|
|
||||||
buf << "Identify the image as a book in perspective, get deperspectivized content" << std::endl;
|
|
||||||
buf << std::endl;
|
|
||||||
buf << "Options:" << std::endl;
|
|
||||||
buf << " -p PROFILE Save the transformation matrix in PROFILE" << std::endl;
|
|
||||||
buf << " -l LEFTPAGE Save the left page in LEFTPAGE" << std::endl;
|
|
||||||
buf << " -r RIGHTPAGE Save the right page in RIGHTPAGE" << std::endl;
|
|
||||||
buf << " -h HELP Show this help and exit" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Options
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
char *left;
|
|
||||||
char *right;
|
|
||||||
char *profile;
|
|
||||||
char *input;
|
|
||||||
Options();
|
|
||||||
Options(int argc, char*argv[]);
|
|
||||||
bool parse(int argc, char*argv[]);
|
|
||||||
~Options();
|
|
||||||
};
|
|
||||||
Options::Options()
|
|
||||||
{
|
|
||||||
left = right = profile = input = NULL;
|
|
||||||
}
|
|
||||||
Options::~Options()
|
|
||||||
{
|
|
||||||
if(left == NULL) {
|
|
||||||
free(left);
|
|
||||||
}
|
|
||||||
if(right == NULL) {
|
|
||||||
free(right);
|
|
||||||
}
|
|
||||||
if(profile == NULL) {
|
|
||||||
free(profile);
|
|
||||||
}
|
|
||||||
//input doesn't need to be free-d: it points to argv[argc-1]
|
|
||||||
}
|
|
||||||
Options::Options(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
left = right = profile = input = NULL;
|
|
||||||
parse(argc, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Options::parse parse arguments, return true if help is requested */
|
|
||||||
bool Options::parse(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int c;
|
|
||||||
while((c=getopt(argc, argv, "p:l:r:h")) != -1)
|
|
||||||
{
|
|
||||||
switch(c)
|
|
||||||
{
|
|
||||||
case 'l':
|
|
||||||
left = strdup(optarg);
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
right = strdup(optarg);
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
profile = strdup(optarg);
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Parsing error: invalid argument");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(optind >= argc) {
|
|
||||||
std::cerr << "Error: " << argv[0] << " needs an argument" << std::endl;
|
|
||||||
throw std::runtime_error("Parsing error: argument needed");
|
|
||||||
}
|
|
||||||
input = argv[optind++];
|
|
||||||
if(optind < argc) {
|
|
||||||
std::cerr << "Error: too many arguments supplied" << std::endl;
|
|
||||||
throw std::runtime_error("Parsing error: too many arguments");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
class BookShape {
|
|
||||||
private:
|
|
||||||
cv::Mat trasf[2];
|
|
||||||
void precompute();
|
|
||||||
cv::Point2f trapezoids[2][4]; //transformation matrix
|
|
||||||
public:
|
|
||||||
BookShape(cv::Point tl, cv::Point tm, cv::Point tf,
|
|
||||||
cv::Point bl, cv::Point bm, cv::Point br);
|
|
||||||
double xsize();
|
|
||||||
double ysize();
|
|
||||||
cv::Mat* getTrasfs();
|
|
||||||
};
|
|
||||||
//topleft topmiddle topright
|
|
||||||
//botleft botmiddle botright
|
|
||||||
BookShape::BookShape(cv::Point tl, cv::Point tm, cv::Point tr, cv::Point bl, cv::Point bm, cv::Point br)
|
|
||||||
{
|
|
||||||
trapezoids[0][0] = tl;
|
|
||||||
trapezoids[0][1] = tm;
|
|
||||||
trapezoids[0][2] = bm;
|
|
||||||
trapezoids[0][3] = bl;
|
|
||||||
trapezoids[1][0] = tm;
|
|
||||||
trapezoids[1][1] = tr;
|
|
||||||
trapezoids[1][2] = br;
|
|
||||||
trapezoids[1][3] = bm;
|
|
||||||
precompute();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BookShape::precompute(void) {
|
|
||||||
cv::Point2f outsizes[4] = {cv::Point2f(0, 0), cv::Point(xsize(), 0),
|
|
||||||
cv::Point(xsize(), ysize()), cv::Point(0, ysize())};
|
|
||||||
for(int i=0; i < 2; i++) {
|
|
||||||
trasf[i] = cv::getPerspectiveTransform(trapezoids[i], outsizes);
|
|
||||||
assert(9==trasf[i].total()); // 3x3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cv::Mat* BookShape::getTrasfs()
|
|
||||||
{
|
|
||||||
return trasf;
|
|
||||||
}
|
|
||||||
double BookShape::xsize()
|
|
||||||
{
|
|
||||||
return dist(trapezoids[0][0], trapezoids[0][1]);
|
|
||||||
}
|
|
||||||
double BookShape::ysize()
|
|
||||||
{
|
|
||||||
return dist(trapezoids[0][1], trapezoids[0][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BookShape get_book_shape(cv::Mat img)
|
|
||||||
{
|
|
||||||
//dotwidth is just a simple size so that lines and dots are visible on big images
|
|
||||||
//but not huge on small images
|
|
||||||
#ifdef _DEBUG
|
|
||||||
unsigned short dotwidth = img.cols >> 6; // divide by 64, so efficient
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::vector< std::vector<cv::Point> > contours;
|
|
||||||
std::vector<cv::Vec4i> hierarchy; //this is not really useful
|
|
||||||
cv::findContours(img,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
|
|
||||||
|
|
||||||
|
|
||||||
if( 0==contours.size() ) {
|
|
||||||
std::cerr << "No contours found" << std::endl;
|
|
||||||
throw "Error getting contours";
|
|
||||||
}
|
|
||||||
auto contour = contours[0];
|
|
||||||
if( 1!=contours.size() )
|
|
||||||
{
|
|
||||||
// choosing the biggest contour in the image
|
|
||||||
// you can test it with files/2contours.png
|
|
||||||
for(auto cont: contours) {
|
|
||||||
if(cont.size() > contour.size())
|
|
||||||
contour = cont;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//hull: points forming an external convex border
|
|
||||||
std::vector<cv::Point> hull;
|
|
||||||
cv::convexHull(cv::Mat(contour),hull,false);
|
|
||||||
|
|
||||||
//because of the "butterfly" shape of a book, the most distant points in the hull
|
|
||||||
//are the one between the corner.
|
|
||||||
unsigned *maxdistances = max2_distance(hull);
|
|
||||||
cv::Point corn_1, corn_2, corn_3, corn_4;
|
|
||||||
corn_1 = hull[maxdistances[0]];
|
|
||||||
corn_2 = hull[(maxdistances[0]+1)%hull.size()];
|
|
||||||
corn_3 = hull[maxdistances[1]];
|
|
||||||
corn_4 = hull[(maxdistances[1]+1)%hull.size()];
|
|
||||||
|
|
||||||
std::vector<cv::Point> vertical_points[2];
|
|
||||||
cv::Vec<double,3> verticals[2];
|
|
||||||
// Between the two corners on the same side, the longest line is the vertical border of the book
|
|
||||||
vertical_points[0] = find_longest_line(hull, (maxdistances[0]+1)%hull.size(), maxdistances[1]);
|
|
||||||
std::cout << maxdistances[1] << std::endl;
|
|
||||||
std::cout << maxdistances[1]+1 << std::endl;
|
|
||||||
std::cout << (maxdistances[1]+1)%hull.size() << std::endl;
|
|
||||||
vertical_points[1] = find_longest_line(hull, (maxdistances[1]+1)%hull.size(), maxdistances[0]);
|
|
||||||
free(maxdistances);
|
|
||||||
#ifdef _DEBUG
|
|
||||||
img = cv::imread("files/masckera.png", CV_LOAD_IMAGE_COLOR);
|
|
||||||
cv::circle(img, vertical_points[0][0], img.cols>>7, cv::Scalar(200, 0, 0), -1);
|
|
||||||
cv::circle(img, vertical_points[0][vertical_points[0].size()-1], img.cols>>7, cv::Scalar(200, 0, 0), -1);
|
|
||||||
cv::circle(img, vertical_points[1][0], img.cols>>7, cv::Scalar(200, 200, 0), -1);
|
|
||||||
cv::circle(img, vertical_points[1][vertical_points[1].size()-1], img.cols>>7, cv::Scalar(200, 0, 0), -1);
|
|
||||||
cv::circle(img, corn_1, img.cols>>7, cv::Scalar(200, 0, 200), -1);
|
|
||||||
cv::namedWindow("aaa", CV_GUI_NORMAL);
|
|
||||||
cv::imshow("aaa", img);
|
|
||||||
cv::waitKey(0);
|
|
||||||
#endif
|
|
||||||
verticals[0] = get_line(vertical_points[0][0], vertical_points[0][vertical_points[0].size()-1]);
|
|
||||||
std::cout << verticals[0] << std::endl;
|
|
||||||
std::cout << line_value(verticals[0], corn_1) << std::endl;
|
|
||||||
verticals[0][2] -= line_value(verticals[0], corn_1);
|
|
||||||
std::cout << line_value(verticals[0], corn_1) << "=0?" << std::endl;
|
|
||||||
verticals[1] = get_line(vertical_points[1][0], vertical_points[1][vertical_points[1].size()-1]);
|
|
||||||
|
|
||||||
// theta is the angle of the line connecting point 1 and 2; it will be the
|
|
||||||
// rotation of our new coordinate system
|
|
||||||
auto cs = CoordinateSystem(corn_1, corn_2);
|
|
||||||
assert(cs.map(corn_1).x == 0);
|
|
||||||
assert(cs.map(corn_1).y == 0);
|
|
||||||
assert(cs.map(corn_2).x > 0);
|
|
||||||
assert(cs.map(corn_2).y == 0);
|
|
||||||
cv::Vec<double,3> diag1, diag2;
|
|
||||||
diag1 = get_line(cs.map(corn_1), cs.map(corn_3));
|
|
||||||
diag2 = get_line(cs.map(corn_4), cs.map(corn_2));
|
|
||||||
std::cout << "mapped diag1: " << diag1 << std::endl;
|
|
||||||
std::cout << "mapped diag2: " << diag2 << std::endl;
|
|
||||||
|
|
||||||
std::vector<cv::Point> points1, points2;
|
|
||||||
for(cv::Point p: contour)
|
|
||||||
{ // the point is interesting where it is above both lines or below both lines
|
|
||||||
cv::Point mapped = cs.map(p);
|
|
||||||
if(is_above_line(diag1, mapped)) {
|
|
||||||
if(is_above_line(diag2, mapped)) {
|
|
||||||
points1.push_back(p);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(!is_above_line(diag2, mapped)) {
|
|
||||||
points2.push_back(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cv::Point middle1 = further_point_from_line(get_line(corn_1, corn_2), points2);
|
|
||||||
cv::Point middle2 = further_point_from_line(get_line(corn_3, corn_4), points1);
|
|
||||||
|
|
||||||
BookShape bs = BookShape(
|
|
||||||
vertical_points[1][vertical_points[1].size()-1], middle1, vertical_points[0][0],
|
|
||||||
vertical_points[1][0], middle2, vertical_points[0][vertical_points[0].size()-1]
|
|
||||||
);
|
|
||||||
return bs;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
Options args;
|
cv::Mat img=cv::imread("files/masckera.png",CV_LOAD_IMAGE_GRAYSCALE);
|
||||||
try{
|
|
||||||
if(args.parse(argc, argv)) {
|
std::vector< std::vector<cv::Point> > contours;
|
||||||
usage(argv[0], std::cout);
|
std::vector<cv::Vec4i> hierarchy;
|
||||||
return EXIT_SUCCESS;
|
cv::findContours(img,contours,hierarchy,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
|
||||||
}
|
|
||||||
} catch(std::runtime_error) {
|
cv::drawContours(img,contours,-1,cv::Scalar(255,255,255),5);
|
||||||
usage(argv[0], std::cerr);
|
|
||||||
return EXIT_FAILURE;
|
if( 1!=contours.size() )
|
||||||
|
{
|
||||||
|
std::cout << "non ci piace" << std::endl;
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cv::Mat img;
|
std::vector<cv::Point> hull;
|
||||||
img=cv::imread(args.input,CV_LOAD_IMAGE_GRAYSCALE);
|
cv::convexHull(cv::Mat(contours[0]),hull,false);
|
||||||
if( img.empty() ) {
|
|
||||||
std::cerr << "Error opening image, aborting" << std::endl;
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
BookShape bs = get_book_shape(img);
|
|
||||||
|
|
||||||
if(args.profile) {
|
int i;
|
||||||
// format: each line is a tab-delimited 3x3 transformation matrix (as get by getPerspectiveTransform)
|
int idx[4]={0};
|
||||||
// first three elements is the first line, etc
|
int distance[2]={0};
|
||||||
std::ofstream profilebuf;
|
for( i=0; hull.size()>i; i++ )
|
||||||
profilebuf.open(args.profile);
|
{
|
||||||
for(int t=0; t<2; t++) {
|
cv::Point one=hull[i];
|
||||||
for(int i=0; i<3; i++) {
|
cv::Point two=hull[(i+1)%hull.size()];
|
||||||
double* row= bs.getTrasfs()[t].ptr<double>(i);
|
int d=pow(one.x-two.x,2)+pow(one.y-two.y,2);
|
||||||
for(int j=0; j < 3; j++) {
|
if( d>distance[0] )
|
||||||
profilebuf << std::fixed << std::setprecision(16);
|
{
|
||||||
profilebuf << row[j] << "\t"; //yes, there's a trailing tab
|
idx[1]=idx[0];
|
||||||
}
|
idx[3]=(idx[1]+1)%hull.size();
|
||||||
}
|
idx[0]=i;
|
||||||
profilebuf << std::endl;
|
idx[2]=(idx[0]+1)%hull.size();
|
||||||
|
distance[1]=distance[0];
|
||||||
|
distance[0]=d;
|
||||||
}
|
}
|
||||||
profilebuf.close();
|
else if( d>distance[1] )
|
||||||
}
|
{
|
||||||
|
idx[1]=i;
|
||||||
//TODO: distinguish left and right
|
idx[3]=(i+1)%hull.size();
|
||||||
std::vector<int> params;
|
distance[1]=d;
|
||||||
if(args.left || args.right) {
|
|
||||||
//we are rereading in full color to get colored output
|
|
||||||
img = cv::imread(args.input,CV_LOAD_IMAGE_COLOR);
|
|
||||||
cv::Mat rect[2]; //final pages, transformed in a nice rectangle
|
|
||||||
|
|
||||||
params.push_back(CV_IMWRITE_PNG_COMPRESSION);
|
|
||||||
params.push_back(9);
|
|
||||||
if(args.left) {
|
|
||||||
cv::warpPerspective(img, rect[0], bs.getTrasfs()[0], cv::Size(bs.xsize(), bs.ysize()));
|
|
||||||
cv::imwrite(args.left, rect[0], params);
|
|
||||||
}
|
|
||||||
if(args.right) {
|
|
||||||
cv::warpPerspective(img, rect[1], bs.getTrasfs()[1], cv::Size(bs.xsize(), bs.ysize()));
|
|
||||||
cv::imwrite(args.right, rect[1], params);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
cv::circle(img,hull[idx[0]],30,255,-1);
|
||||||
|
cv::circle(img,hull[(idx[0]+1)%hull.size()],30,255,-1);
|
||||||
|
|
||||||
|
cv::circle(img,hull[idx[1]],20,255,-1);
|
||||||
|
cv::circle(img,hull[(idx[1]+1)%hull.size()],20,255,-1);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << hull.size() << ", " << contours[0].size() << std::endl;
|
||||||
|
std::cout
|
||||||
|
<< "(" << idx[0] << "," << distance[0] << ") -- "
|
||||||
|
<< "(" << idx[1] << "," << distance[1] << ")" << std::endl;
|
||||||
|
cv::namedWindow("test",CV_WINDOW_NORMAL);
|
||||||
|
cv::imshow("test",img);
|
||||||
|
cv::waitKey(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: set noet ts=4 sw=4:
|
|
||||||
|
|
9
lines.h
9
lines.h
|
@ -1,9 +0,0 @@
|
||||||
#include<algorithm>
|
|
||||||
|
|
||||||
#include<opencv2/opencv.hpp>
|
|
||||||
|
|
||||||
std::vector<std::vector<cv::Point>> simplify_hull(std::vector<cv::Point> hull);
|
|
||||||
std::vector<std::vector<cv::Point>> simplify_hull(std::vector<cv::Point> hull, double mindistance);
|
|
||||||
|
|
||||||
bool similar_fit(cv::Point a, cv::Point b, cv::Point newpoint);
|
|
||||||
bool similar_fit(cv::Point a, cv::Point b, cv::Point newpoint, float tolerance_degrees);
|
|
Loading…
Reference in a new issue