vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* 3calibration.cpp -- Calibrate 3 cameras in a horizontal line together.
|
||||
*/
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/stereo.hpp"
|
||||
#include "opencv2/calib.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
printf( "\nThis is a camera calibration sample that calibrates 3 horizontally placed cameras together.\n"
|
||||
"Usage: %s\n"
|
||||
" -w=<board_width> # the number of inner corners per one of board dimension\n"
|
||||
" -h=<board_height> # the number of inner corners per another board dimension\n"
|
||||
" [-s=<squareSize>] # square size in some user-defined units (1 by default)\n"
|
||||
" [-o=<out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
|
||||
" [-zt] # assume zero tangential distortion\n"
|
||||
" [-a=<aspectRatio>] # fix aspect ratio (fx/fy)\n"
|
||||
" [-p] # fix the principal point at the center\n"
|
||||
" [input_data] # input data - text file with a list of the images of the board\n"
|
||||
"\n", argv[0] );
|
||||
|
||||
}
|
||||
|
||||
static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners)
|
||||
{
|
||||
corners.resize(0);
|
||||
|
||||
for( int i = 0; i < boardSize.height; i++ )
|
||||
for( int j = 0; j < boardSize.width; j++ )
|
||||
corners.push_back(Point3f(float(j*squareSize),
|
||||
float(i*squareSize), 0));
|
||||
}
|
||||
|
||||
static bool run3Calibration(vector<vector<Point2f> > imagePoints1,
|
||||
vector<vector<Point2f> > imagePoints2,
|
||||
vector<vector<Point2f> > imagePoints3,
|
||||
Size imageSize, Size boardSize,
|
||||
float squareSize, float aspectRatio,
|
||||
int flags,
|
||||
Mat& cameraMatrix1, Mat& distCoeffs1,
|
||||
Mat& cameraMatrix2, Mat& distCoeffs2,
|
||||
Mat& cameraMatrix3, Mat& distCoeffs3,
|
||||
Mat& R12, Mat& T12, Mat& R13, Mat& T13)
|
||||
{
|
||||
int c, i;
|
||||
|
||||
// step 1: calibrate each camera individually
|
||||
vector<vector<Point3f> > objpt(1);
|
||||
vector<vector<Point2f> > imgpt;
|
||||
calcChessboardCorners(boardSize, squareSize, objpt[0]);
|
||||
vector<Mat> rvecs, tvecs;
|
||||
|
||||
for( c = 1; c <= 3; c++ )
|
||||
{
|
||||
const vector<vector<Point2f> >& imgpt0 = c == 1 ? imagePoints1 : c == 2 ? imagePoints2 : imagePoints3;
|
||||
imgpt.clear();
|
||||
int N = 0;
|
||||
for( i = 0; i < (int)imgpt0.size(); i++ )
|
||||
if( !imgpt0[i].empty() )
|
||||
{
|
||||
imgpt.push_back(imgpt0[i]);
|
||||
N += (int)imgpt0[i].size();
|
||||
}
|
||||
|
||||
if( imgpt.size() < 3 )
|
||||
{
|
||||
printf("Error: not enough views for camera %d\n", c);
|
||||
return false;
|
||||
}
|
||||
|
||||
objpt.resize(imgpt.size(),objpt[0]);
|
||||
|
||||
Mat cameraMatrix = Mat::eye(3, 3, CV_64F);
|
||||
if( flags & CALIB_FIX_ASPECT_RATIO )
|
||||
cameraMatrix.at<double>(0,0) = aspectRatio;
|
||||
|
||||
Mat distCoeffs = Mat::zeros(5, 1, CV_64F);
|
||||
|
||||
double err = calibrateCamera(objpt, imgpt, imageSize, cameraMatrix,
|
||||
distCoeffs, rvecs, tvecs,
|
||||
flags|CALIB_FIX_K3/*|CALIB_FIX_K4|CALIB_FIX_K5|CALIB_FIX_K6*/);
|
||||
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
|
||||
if(!ok)
|
||||
{
|
||||
printf("Error: camera %d was not calibrated\n", c);
|
||||
return false;
|
||||
}
|
||||
printf("Camera %d calibration reprojection error = %g\n", c, sqrt(err/N));
|
||||
|
||||
if( c == 1 )
|
||||
cameraMatrix1 = cameraMatrix, distCoeffs1 = distCoeffs;
|
||||
else if( c == 2 )
|
||||
cameraMatrix2 = cameraMatrix, distCoeffs2 = distCoeffs;
|
||||
else
|
||||
cameraMatrix3 = cameraMatrix, distCoeffs3 = distCoeffs;
|
||||
}
|
||||
|
||||
vector<vector<Point2f> > imgpt_right;
|
||||
|
||||
// step 2: calibrate (1,2) and (3,2) pairs
|
||||
for( c = 2; c <= 3; c++ )
|
||||
{
|
||||
const vector<vector<Point2f> >& imgpt0 = c == 2 ? imagePoints2 : imagePoints3;
|
||||
|
||||
imgpt.clear();
|
||||
imgpt_right.clear();
|
||||
int N = 0;
|
||||
|
||||
for( i = 0; i < (int)std::min(imagePoints1.size(), imgpt0.size()); i++ )
|
||||
if( !imagePoints1.empty() && !imgpt0[i].empty() )
|
||||
{
|
||||
imgpt.push_back(imagePoints1[i]);
|
||||
imgpt_right.push_back(imgpt0[i]);
|
||||
N += (int)imgpt0[i].size();
|
||||
}
|
||||
|
||||
if( imgpt.size() < 3 )
|
||||
{
|
||||
printf("Error: not enough shared views for cameras 1 and %d\n", c);
|
||||
return false;
|
||||
}
|
||||
|
||||
objpt.resize(imgpt.size(),objpt[0]);
|
||||
Mat cameraMatrix = c == 2 ? cameraMatrix2 : cameraMatrix3;
|
||||
Mat distCoeffs = c == 2 ? distCoeffs2 : distCoeffs3;
|
||||
Mat R, T, E, F;
|
||||
double err = stereoCalibrate(objpt, imgpt, imgpt_right, cameraMatrix1, distCoeffs1,
|
||||
cameraMatrix, distCoeffs,
|
||||
imageSize, R, T, E, F,
|
||||
CALIB_FIX_INTRINSIC,
|
||||
TermCriteria(TermCriteria::COUNT, 30, 0));
|
||||
printf("Pair (1,%d) calibration reprojection error = %g\n", c, sqrt(err/(N*2)));
|
||||
if( c == 2 )
|
||||
{
|
||||
cameraMatrix2 = cameraMatrix;
|
||||
distCoeffs2 = distCoeffs;
|
||||
R12 = R; T12 = T;
|
||||
}
|
||||
else
|
||||
{
|
||||
R13 = R; T13 = T;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readStringList( const string& filename, vector<string>& l )
|
||||
{
|
||||
l.resize(0);
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
FileNode n = fs.getFirstTopLevelNode();
|
||||
if( n.type() != FileNode::SEQ )
|
||||
return false;
|
||||
FileNodeIterator it = n.begin(), it_end = n.end();
|
||||
for( ; it != it_end; ++it )
|
||||
l.push_back((string)*it);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
int i, k;
|
||||
int flags = 0;
|
||||
Size boardSize, imageSize;
|
||||
float squareSize, aspectRatio;
|
||||
string outputFilename;
|
||||
string inputFilename = "";
|
||||
|
||||
vector<vector<Point2f> > imgpt[3];
|
||||
vector<string> imageList;
|
||||
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help ||}{w||}{h||}{s|1|}{o|out_camera_data.yml|}"
|
||||
"{zt||}{a|1|}{p||}{@input||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
boardSize.width = parser.get<int>("w");
|
||||
boardSize.height = parser.get<int>("h");
|
||||
squareSize = parser.get<float>("s");
|
||||
aspectRatio = parser.get<float>("a");
|
||||
if (parser.has("a"))
|
||||
flags |= CALIB_FIX_ASPECT_RATIO;
|
||||
if (parser.has("zt"))
|
||||
flags |= CALIB_ZERO_TANGENT_DIST;
|
||||
if (parser.has("p"))
|
||||
flags |= CALIB_FIX_PRINCIPAL_POINT;
|
||||
outputFilename = parser.get<string>("o");
|
||||
inputFilename = parser.get<string>("@input");
|
||||
if (!parser.check())
|
||||
{
|
||||
help(argv);
|
||||
parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
if (boardSize.width <= 0)
|
||||
return fprintf( stderr, "Invalid board width\n" ), -1;
|
||||
if (boardSize.height <= 0)
|
||||
return fprintf( stderr, "Invalid board height\n" ), -1;
|
||||
if (squareSize <= 0)
|
||||
return fprintf( stderr, "Invalid board square width\n" ), -1;
|
||||
if (aspectRatio <= 0)
|
||||
return printf("Invalid aspect ratio\n" ), -1;
|
||||
if( inputFilename.empty() ||
|
||||
!readStringList(inputFilename, imageList) ||
|
||||
imageList.size() == 0 || imageList.size() % 3 != 0 )
|
||||
{
|
||||
printf("Error: the input image list is not specified, or can not be read, or the number of files is not divisible by 3\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat view, viewGray;
|
||||
Mat cameraMatrix[3], distCoeffs[3], R[3], P[3], R12, T12;
|
||||
for( k = 0; k < 3; k++ )
|
||||
{
|
||||
cameraMatrix[k] = Mat_<double>::eye(3,3);
|
||||
cameraMatrix[k].at<double>(0,0) = aspectRatio;
|
||||
cameraMatrix[k].at<double>(1,1) = 1;
|
||||
distCoeffs[k] = Mat_<double>::zeros(5,1);
|
||||
}
|
||||
Mat R13=Mat_<double>::eye(3,3), T13=Mat_<double>::zeros(3,1);
|
||||
|
||||
FileStorage fs;
|
||||
namedWindow( "Image View", 0 );
|
||||
|
||||
for( k = 0; k < 3; k++ )
|
||||
imgpt[k].resize(imageList.size()/3);
|
||||
|
||||
for( i = 0; i < (int)(imageList.size()/3); i++ )
|
||||
{
|
||||
for( k = 0; k < 3; k++ )
|
||||
{
|
||||
int k1 = k == 0 ? 2 : k == 1 ? 0 : 1;
|
||||
printf("%s\n", imageList[i*3+k].c_str());
|
||||
view = imread(imageList[i*3+k], IMREAD_COLOR);
|
||||
|
||||
if(!view.empty())
|
||||
{
|
||||
vector<Point2f> ptvec;
|
||||
imageSize = view.size();
|
||||
cvtColor(view, viewGray, COLOR_BGR2GRAY);
|
||||
bool found = findChessboardCorners( view, boardSize, ptvec, CALIB_CB_ADAPTIVE_THRESH );
|
||||
|
||||
drawChessboardCorners( view, boardSize, Mat(ptvec), found );
|
||||
if( found )
|
||||
{
|
||||
imgpt[k1][i].resize(ptvec.size());
|
||||
std::copy(ptvec.begin(), ptvec.end(), imgpt[k1][i].begin());
|
||||
}
|
||||
//imshow("view", view);
|
||||
//int c = waitKey(0) & 255;
|
||||
//if( c == 27 || c == 'q' || c == 'Q' )
|
||||
// return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("Running calibration ...\n");
|
||||
|
||||
run3Calibration(imgpt[0], imgpt[1], imgpt[2], imageSize,
|
||||
boardSize, squareSize, aspectRatio, flags|CALIB_FIX_K4|CALIB_FIX_K5,
|
||||
cameraMatrix[0], distCoeffs[0],
|
||||
cameraMatrix[1], distCoeffs[1],
|
||||
cameraMatrix[2], distCoeffs[2],
|
||||
R12, T12, R13, T13);
|
||||
|
||||
fs.open(outputFilename, FileStorage::WRITE);
|
||||
|
||||
fs << "cameraMatrix1" << cameraMatrix[0];
|
||||
fs << "cameraMatrix2" << cameraMatrix[1];
|
||||
fs << "cameraMatrix3" << cameraMatrix[2];
|
||||
|
||||
fs << "distCoeffs1" << distCoeffs[0];
|
||||
fs << "distCoeffs2" << distCoeffs[1];
|
||||
fs << "distCoeffs3" << distCoeffs[2];
|
||||
|
||||
fs << "R12" << R12;
|
||||
fs << "T12" << T12;
|
||||
fs << "R13" << R13;
|
||||
fs << "T13" << T13;
|
||||
|
||||
fs << "imageWidth" << imageSize.width;
|
||||
fs << "imageHeight" << imageSize.height;
|
||||
|
||||
Mat Q;
|
||||
|
||||
// step 3: find rectification transforms
|
||||
double ratio = rectify3Collinear(cameraMatrix[0], distCoeffs[0], cameraMatrix[1],
|
||||
distCoeffs[1], cameraMatrix[2], distCoeffs[2],
|
||||
imgpt[0], imgpt[2],
|
||||
imageSize, R12, T12, R13, T13,
|
||||
R[0], R[1], R[2], P[0], P[1], P[2], Q, -1.,
|
||||
imageSize, 0, 0, CALIB_ZERO_DISPARITY);
|
||||
Mat map1[3], map2[3];
|
||||
|
||||
fs << "R1" << R[0];
|
||||
fs << "R2" << R[1];
|
||||
fs << "R3" << R[2];
|
||||
|
||||
fs << "P1" << P[0];
|
||||
fs << "P2" << P[1];
|
||||
fs << "P3" << P[2];
|
||||
|
||||
fs << "disparityRatio" << ratio;
|
||||
fs.release();
|
||||
|
||||
printf("Disparity ratio = %g\n", ratio);
|
||||
|
||||
for( k = 0; k < 3; k++ )
|
||||
initUndistortRectifyMap(cameraMatrix[k], distCoeffs[k], R[k], P[k], imageSize, CV_16SC2, map1[k], map2[k]);
|
||||
|
||||
Mat canvas(imageSize.height, imageSize.width*3, CV_8UC3), small_canvas;
|
||||
destroyWindow("view");
|
||||
canvas = Scalar::all(0);
|
||||
|
||||
for( i = 0; i < (int)(imageList.size()/3); i++ )
|
||||
{
|
||||
canvas = Scalar::all(0);
|
||||
for( k = 0; k < 3; k++ )
|
||||
{
|
||||
int k1 = k == 0 ? 2 : k == 1 ? 0 : 1;
|
||||
int k2 = k == 0 ? 1 : k == 1 ? 0 : 2;
|
||||
view = imread(imageList[i*3+k], IMREAD_COLOR);
|
||||
|
||||
if(view.empty())
|
||||
continue;
|
||||
|
||||
Mat rview = canvas.colRange(k2*imageSize.width, (k2+1)*imageSize.width);
|
||||
remap(view, rview, map1[k1], map2[k1], INTER_LINEAR);
|
||||
}
|
||||
printf("%s %s %s\n", imageList[i*3].c_str(), imageList[i*3+1].c_str(), imageList[i*3+2].c_str());
|
||||
resize( canvas, small_canvas, Size(1500, 1500/3), 0, 0, INTER_LINEAR_EXACT );
|
||||
for( k = 0; k < small_canvas.rows; k += 16 )
|
||||
line(small_canvas, Point(0, k), Point(small_canvas.cols, k), Scalar(0,255,0), 1);
|
||||
imshow("rectified", small_canvas);
|
||||
char c = (char)waitKey(0);
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
ocv_install_example_src(cpp *.cpp *.hpp CMakeLists.txt)
|
||||
|
||||
set(OPENCV_CPP_SAMPLES_REQUIRED_DEPS
|
||||
opencv_core
|
||||
opencv_imgproc
|
||||
opencv_flann
|
||||
opencv_imgcodecs
|
||||
opencv_videoio
|
||||
opencv_highgui
|
||||
opencv_video
|
||||
opencv_objdetect
|
||||
opencv_photo
|
||||
opencv_features
|
||||
opencv_geometry
|
||||
opencv_stereo
|
||||
opencv_calib
|
||||
opencv_stitching
|
||||
opencv_dnn
|
||||
${OPENCV_MODULES_PUBLIC}
|
||||
${OpenCV_LIB_COMPONENTS})
|
||||
ocv_check_dependencies(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS})
|
||||
|
||||
if(NOT BUILD_EXAMPLES OR NOT OCV_DEPENDENCIES_FOUND)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(DEPS_example_snippet_imgproc_segmentation opencv_core opencv_photo)
|
||||
set(DEPS_example_cpp_intelligent_scissors opencv_core opencv_photo opencv_imgcodecs opencv_highgui)
|
||||
|
||||
project(cpp_samples)
|
||||
ocv_include_modules_recurse(${OPENCV_CPP_SAMPLES_REQUIRED_DEPS})
|
||||
file(GLOB_RECURSE cpp_samples RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
|
||||
if(NOT HAVE_opencv_cudaarithm OR NOT HAVE_opencv_cudafilters)
|
||||
ocv_list_filterout(cpp_samples "/gpu/")
|
||||
endif()
|
||||
ocv_list_filterout(cpp_samples "real_time_pose_estimation/")
|
||||
ocv_list_filterout(cpp_samples "parallel_backend/")
|
||||
foreach(sample_filename ${cpp_samples})
|
||||
set(package "cpp")
|
||||
if(sample_filename MATCHES "tutorial_code/snippet")
|
||||
set(package "snippet")
|
||||
elseif(sample_filename MATCHES "tutorial_code")
|
||||
set(package "tutorial")
|
||||
endif()
|
||||
ocv_define_sample(tgt ${sample_filename} ${package})
|
||||
set(deps ${OPENCV_CPP_SAMPLES_REQUIRED_DEPS})
|
||||
if(DEFINED DEPS_${tgt})
|
||||
set(deps ${DEPS_${tgt}})
|
||||
endif()
|
||||
ocv_target_link_libraries(${tgt} PRIVATE ${OPENCV_LINKER_LIBS} ${deps})
|
||||
if(sample_filename MATCHES "/gpu/" AND HAVE_opencv_cudaarithm AND HAVE_opencv_cuda_filters)
|
||||
ocv_target_link_libraries(${tgt} PRIVATE opencv_cudaarithm opencv_cudafilters)
|
||||
endif()
|
||||
if(sample_filename MATCHES "/viz/")
|
||||
ocv_target_link_libraries(${tgt} PRIVATE ${VTK_LIBRARIES})
|
||||
target_compile_definitions(${tgt} PRIVATE -DUSE_VTK)
|
||||
endif()
|
||||
if(HAVE_OPENGL AND sample_filename MATCHES "detect_mser")
|
||||
target_compile_definitions(${tgt} PRIVATE HAVE_OPENGL)
|
||||
ocv_target_link_libraries(${tgt} PRIVATE "${OPENGL_LIBRARIES}")
|
||||
endif()
|
||||
if(sample_filename MATCHES "simd_")
|
||||
# disabled intentionally - demonstration purposes only
|
||||
#target_include_directories(${tgt} PRIVATE "${CMAKE_CURRENT_LIST_DIR}")
|
||||
#target_compile_definitions(${tgt} PRIVATE OPENCV_SIMD_CONFIG_HEADER=opencv_simd_config_custom.hpp)
|
||||
#target_compile_definitions(${tgt} PRIVATE OPENCV_SIMD_CONFIG_INCLUDE_DIR=1)
|
||||
#target_compile_options(${tgt} PRIVATE -mavx2)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
include("tutorial_code/calib3d/real_time_pose_estimation/CMakeLists.txt" OPTIONAL)
|
||||
|
||||
# Standalone samples only
|
||||
if(OpenCV_FOUND)
|
||||
add_subdirectory("example_cmake")
|
||||
endif()
|
||||
if(OpenCV_FOUND AND NOT OPENCV_EXAMPLES_SKIP_PARALLEL_BACKEND)
|
||||
add_subdirectory("tutorial_code/core/parallel_backend")
|
||||
endif()
|
||||
@@ -0,0 +1,95 @@
|
||||
/* OpenCV Application Tracing support demo. */
|
||||
#include <iostream>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/core/utils/trace.hpp>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void process_frame(const cv::UMat& frame)
|
||||
{
|
||||
CV_TRACE_FUNCTION(); // OpenCV Trace macro for function
|
||||
|
||||
imshow("Live", frame);
|
||||
|
||||
UMat gray, processed;
|
||||
cv::cvtColor(frame, gray, COLOR_BGR2GRAY);
|
||||
Canny(gray, processed, 32, 64, 3);
|
||||
imshow("Processed", processed);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help h ? | | help message}"
|
||||
"{n | 100 | number of frames to process }"
|
||||
"{@video | 0 | video filename or cameraID }"
|
||||
);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
VideoCapture capture;
|
||||
std::string video = parser.get<string>("@video");
|
||||
if (video.size() == 1 && isdigit(video[0]))
|
||||
capture.open(parser.get<int>("@video"));
|
||||
else
|
||||
capture.open(samples::findFileOrKeep(video)); // keep GStreamer pipelines
|
||||
int nframes = 0;
|
||||
if (capture.isOpened())
|
||||
{
|
||||
nframes = (int)capture.get(CAP_PROP_FRAME_COUNT);
|
||||
cout << "Video " << video <<
|
||||
": width=" << capture.get(CAP_PROP_FRAME_WIDTH) <<
|
||||
", height=" << capture.get(CAP_PROP_FRAME_HEIGHT) <<
|
||||
", nframes=" << nframes << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Could not initialize video capturing...\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int N = parser.get<int>("n");
|
||||
if (nframes > 0 && N > nframes)
|
||||
N = nframes;
|
||||
|
||||
cout << "Start processing..." << endl
|
||||
<< "Press ESC key to terminate" << endl;
|
||||
|
||||
UMat frame;
|
||||
for (int i = 0; N > 0 ? (i < N) : true; i++)
|
||||
{
|
||||
CV_TRACE_REGION("FRAME"); // OpenCV Trace macro for named "scope" region
|
||||
{
|
||||
CV_TRACE_REGION("read");
|
||||
capture.read(frame);
|
||||
|
||||
if (frame.empty())
|
||||
{
|
||||
cerr << "Can't capture frame: " << i << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
// OpenCV Trace macro for NEXT named region in the same C++ scope
|
||||
// Previous "read" region will be marked complete on this line.
|
||||
// Use this to eliminate unnecessary curly braces.
|
||||
CV_TRACE_REGION_NEXT("process");
|
||||
process_frame(frame);
|
||||
|
||||
CV_TRACE_REGION_NEXT("delay");
|
||||
if (waitKey(1) == 27/*ESC*/)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
#include <opencv2/objdetect/aruco_detector.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static int _getSelfDistance(const Mat &marker) {
|
||||
|
||||
Mat bytes = aruco::Dictionary::getByteListFromBits(marker);
|
||||
|
||||
double minHamming = (double)marker.total() + 1;
|
||||
for(int r = 1; r < 4; r++) {
|
||||
cv::Mat tmp1(1, bytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < bytes.cols; ++i) {
|
||||
rot0[i] = bytes.ptr()[i];
|
||||
rot1[i] = bytes.ptr()[bytes.cols*r + i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if (currentHamming < minHamming) minHamming = currentHamming;
|
||||
}
|
||||
Mat b;
|
||||
flip(marker, b, 0);
|
||||
Mat flipBytes = aruco::Dictionary::getByteListFromBits(b);
|
||||
for(int r = 0; r < 4; r++) {
|
||||
cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < bytes.cols; ++i) {
|
||||
rot0[i] = flipBytes.ptr()[i];
|
||||
rot1[i] = bytes.ptr()[bytes.cols*r + i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if(currentHamming < minHamming) minHamming = currentHamming;
|
||||
}
|
||||
flip(marker, b, 1);
|
||||
flipBytes = aruco::Dictionary::getByteListFromBits(b);
|
||||
for(int r = 0; r < 4; r++) {
|
||||
cv::Mat tmp1(1, flipBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, bytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < bytes.cols; ++i) {
|
||||
rot0[i] = flipBytes.ptr()[i];
|
||||
rot1[i] = bytes.ptr()[bytes.cols*r + i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if(currentHamming < minHamming) minHamming = currentHamming;
|
||||
}
|
||||
return cvRound(minHamming);
|
||||
}
|
||||
|
||||
static inline int getFlipDistanceToId(const aruco::Dictionary& dict, InputArray bits, int id, bool allRotations = true) {
|
||||
Mat bytesList = dict.bytesList;
|
||||
CV_Assert(id >= 0 && id < bytesList.rows);
|
||||
|
||||
unsigned int nRotations = 4;
|
||||
if(!allRotations) nRotations = 1;
|
||||
|
||||
Mat candidateBytes = aruco::Dictionary::getByteListFromBits(bits.getMat());
|
||||
double currentMinDistance = int(bits.total() * bits.total());
|
||||
for(unsigned int r = 0; r < nRotations; r++) {
|
||||
|
||||
cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < candidateBytes.cols; ++i) {
|
||||
rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i];
|
||||
rot1[i] = candidateBytes.ptr()[i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if(currentHamming < currentMinDistance) {
|
||||
currentMinDistance = currentHamming;
|
||||
}
|
||||
}
|
||||
Mat b;
|
||||
flip(bits.getMat(), b, 0);
|
||||
candidateBytes = aruco::Dictionary::getByteListFromBits(b);
|
||||
for(unsigned int r = 0; r < nRotations; r++) {
|
||||
cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < candidateBytes.cols; ++i) {
|
||||
rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i];
|
||||
rot1[i] = candidateBytes.ptr()[i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if (currentHamming < currentMinDistance) {
|
||||
currentMinDistance = currentHamming;
|
||||
}
|
||||
}
|
||||
|
||||
flip(bits.getMat(), b, 1);
|
||||
candidateBytes = aruco::Dictionary::getByteListFromBits(b);
|
||||
for(unsigned int r = 0; r < nRotations; r++) {
|
||||
cv::Mat tmp1(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
cv::Mat tmp2(1, candidateBytes.cols, CV_8UC1, Scalar::all(0));
|
||||
uchar* rot0 = tmp1.ptr();
|
||||
uchar* rot1 = tmp2.ptr();
|
||||
|
||||
for (int i = 0; i < candidateBytes.cols; ++i) {
|
||||
rot0[i] = bytesList.ptr(id)[r*candidateBytes.cols + i];
|
||||
rot1[i] = candidateBytes.ptr()[i];
|
||||
}
|
||||
|
||||
double currentHamming = cv::norm(tmp1, tmp2, cv::NORM_HAMMING);
|
||||
if (currentHamming < currentMinDistance) {
|
||||
currentMinDistance = currentHamming;
|
||||
}
|
||||
}
|
||||
return cvRound(currentMinDistance);
|
||||
}
|
||||
|
||||
static inline aruco::Dictionary generateCustomAsymmetricDictionary(int nMarkers, int markerSize,
|
||||
const aruco::Dictionary &baseDictionary,
|
||||
int randomSeed) {
|
||||
RNG rng((uint64)(randomSeed));
|
||||
|
||||
aruco::Dictionary out;
|
||||
out.markerSize = markerSize;
|
||||
|
||||
// theoretical maximum intermarker distance
|
||||
// See S. Garrido-Jurado, R. Muñoz-Salinas, F. J. Madrid-Cuevas, and M. J. Marín-Jiménez. 2014.
|
||||
// "Automatic generation and detection of highly reliable fiducial markers under occlusion".
|
||||
// Pattern Recogn. 47, 6 (June 2014), 2280-2292. DOI=10.1016/j.patcog.2014.01.005
|
||||
int C = (int)std::floor(float(markerSize * markerSize) / 4.f);
|
||||
int tau = 2 * (int)std::floor(float(C) * 4.f / 3.f);
|
||||
|
||||
// if baseDictionary is provided, calculate its intermarker distance
|
||||
if(baseDictionary.bytesList.rows > 0) {
|
||||
CV_Assert(baseDictionary.markerSize == markerSize);
|
||||
out.bytesList = baseDictionary.bytesList.clone();
|
||||
|
||||
int minDistance = markerSize * markerSize + 1;
|
||||
for(int i = 0; i < out.bytesList.rows; i++) {
|
||||
Mat markerBytes = out.bytesList.rowRange(i, i + 1);
|
||||
Mat markerBits = aruco::Dictionary::getBitsFromByteList(markerBytes, markerSize);
|
||||
minDistance = min(minDistance, _getSelfDistance(markerBits));
|
||||
for(int j = i + 1; j < out.bytesList.rows; j++) {
|
||||
minDistance = min(minDistance, getFlipDistanceToId(out, markerBits, j));
|
||||
}
|
||||
}
|
||||
tau = minDistance;
|
||||
}
|
||||
|
||||
// current best option
|
||||
int bestTau = 0;
|
||||
Mat bestMarker;
|
||||
|
||||
// after these number of unproductive iterations, the best option is accepted
|
||||
const int maxUnproductiveIterations = 5000;
|
||||
int unproductiveIterations = 0;
|
||||
|
||||
while(out.bytesList.rows < nMarkers) {
|
||||
Mat currentMarker(markerSize, markerSize, CV_8UC1, Scalar::all(0));
|
||||
rng.fill(currentMarker, RNG::UNIFORM, 0, 2);
|
||||
|
||||
int selfDistance = _getSelfDistance(currentMarker);
|
||||
int minDistance = selfDistance;
|
||||
|
||||
// if self distance is better or equal than current best option, calculate distance
|
||||
// to previous accepted markers
|
||||
if(selfDistance >= bestTau) {
|
||||
for(int i = 0; i < out.bytesList.rows; i++) {
|
||||
int currentDistance = getFlipDistanceToId(out, currentMarker, i);
|
||||
minDistance = min(currentDistance, minDistance);
|
||||
if(minDistance <= bestTau) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if distance is high enough, accept the marker
|
||||
if(minDistance >= tau) {
|
||||
unproductiveIterations = 0;
|
||||
bestTau = 0;
|
||||
Mat bytes = aruco::Dictionary::getByteListFromBits(currentMarker);
|
||||
out.bytesList.push_back(bytes);
|
||||
} else {
|
||||
unproductiveIterations++;
|
||||
|
||||
// if distance is not enough, but is better than the current best option
|
||||
if(minDistance > bestTau) {
|
||||
bestTau = minDistance;
|
||||
bestMarker = currentMarker;
|
||||
}
|
||||
|
||||
// if number of unproductive iterarions has been reached, accept the current best option
|
||||
if(unproductiveIterations == maxUnproductiveIterations) {
|
||||
unproductiveIterations = 0;
|
||||
tau = bestTau;
|
||||
bestTau = 0;
|
||||
Mat bytes = aruco::Dictionary::getByteListFromBits(bestMarker);
|
||||
out.bytesList.push_back(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the maximum number of correction bits for the generated dictionary
|
||||
out.maxCorrectionBits = (tau - 1) / 2;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline int getMinDistForDict(const aruco::Dictionary& dict) {
|
||||
const int dict_size = dict.bytesList.rows;
|
||||
const int marker_size = dict.markerSize;
|
||||
int minDist = marker_size * marker_size;
|
||||
for (int i = 0; i < dict_size; i++) {
|
||||
Mat row = dict.bytesList.row(i);
|
||||
Mat marker = dict.getBitsFromByteList(row, marker_size);
|
||||
for (int j = 0; j < dict_size; j++) {
|
||||
if (j != i) {
|
||||
minDist = min(dict.getDistanceToId(marker, j), minDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
return minDist;
|
||||
}
|
||||
|
||||
static inline int getMinAsymDistForDict(const aruco::Dictionary& dict) {
|
||||
const int dict_size = dict.bytesList.rows;
|
||||
const int marker_size = dict.markerSize;
|
||||
int minDist = marker_size * marker_size;
|
||||
for (int i = 0; i < dict_size; i++)
|
||||
{
|
||||
Mat row = dict.bytesList.row(i);
|
||||
Mat marker = dict.getBitsFromByteList(row, marker_size);
|
||||
for (int j = 0; j < dict_size; j++)
|
||||
{
|
||||
if (j != i)
|
||||
{
|
||||
minDist = min(getFlipDistanceToId(dict, marker, j), minDist);
|
||||
}
|
||||
}
|
||||
}
|
||||
return minDist;
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
"{@outfile |<none> | Output file with custom dict }"
|
||||
"{r | false | Calculate the metric considering flipped markers }"
|
||||
"{d | | Dictionary Name: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250,"
|
||||
"DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, "
|
||||
"DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50,"
|
||||
"DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL,"
|
||||
"DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10,"
|
||||
"DICT_APRILTAG_36h11, DICT_ARUCO_MIP_36h12}"
|
||||
"{nMarkers | | Number of markers in the dictionary }"
|
||||
"{markerSize | | Marker size }"
|
||||
"{cd | | Input file with custom dictionary }";
|
||||
|
||||
const char* about =
|
||||
"This program can be used to calculate the ArUco dictionary metric.\n"
|
||||
"To calculate the metric considering flipped markers use -'r' flag.\n"
|
||||
"This program can be used to create and write the custom ArUco dictionary.\n";
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about(about);
|
||||
if(argc < 2) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
string outputFile = parser.get<String>(0);
|
||||
int nMarkers = parser.get<int>("nMarkers");
|
||||
int markerSize = parser.get<int>("markerSize");
|
||||
bool checkFlippedMarkers = parser.get<bool>("r");
|
||||
|
||||
aruco::Dictionary dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);
|
||||
|
||||
if (parser.has("d")) {
|
||||
string arucoDictName = parser.get<string>("d");
|
||||
cv::aruco::PredefinedDictionaryType arucoDict;
|
||||
if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; }
|
||||
else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; }
|
||||
else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; }
|
||||
else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; }
|
||||
else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; }
|
||||
else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; }
|
||||
else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; }
|
||||
else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; }
|
||||
else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; }
|
||||
else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; }
|
||||
else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; }
|
||||
else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; }
|
||||
else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; }
|
||||
else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; }
|
||||
else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; }
|
||||
else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; }
|
||||
else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; }
|
||||
else if (arucoDictName == "DICT_ARUCO_MIP_36h12") { arucoDict = cv::aruco::DICT_ARUCO_MIP_36h12; }
|
||||
else {
|
||||
cout << "incorrect name of aruco dictionary \n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
dictionary = aruco::getPredefinedDictionary(arucoDict);
|
||||
}
|
||||
else if (parser.has("cd")) {
|
||||
FileStorage fs(parser.get<std::string>("cd"), FileStorage::READ);
|
||||
bool readOk = dictionary.readDictionary(fs.root());
|
||||
if(!readOk) {
|
||||
cerr << "Invalid dictionary file" << endl;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (outputFile.empty() || nMarkers == 0 || markerSize == 0) {
|
||||
cerr << "Dictionary not specified" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!outputFile.empty() && nMarkers > 0 && markerSize > 0)
|
||||
{
|
||||
FileStorage fs(outputFile, FileStorage::WRITE);
|
||||
if (checkFlippedMarkers)
|
||||
dictionary = generateCustomAsymmetricDictionary(nMarkers, markerSize, dictionary, 0);
|
||||
else
|
||||
dictionary = aruco::extendDictionary(nMarkers, markerSize, dictionary, 0);
|
||||
dictionary.writeDictionary(fs);
|
||||
}
|
||||
|
||||
if (checkFlippedMarkers) {
|
||||
cout << "Hamming distance: " << getMinAsymDistForDict(dictionary) << endl;
|
||||
}
|
||||
else {
|
||||
cout << "Hamming distance: " << getMinDistForDict(dictionary) << endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/geometry/3d.hpp>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#ifdef HAVE_OPENCV_XFEATURES2D
|
||||
#include "opencv2/xfeatures2d.hpp"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout
|
||||
<< "This is a sample usage of AffineFeature detector/extractor.\n"
|
||||
<< "And this is a C++ version of samples/python/asift.py\n"
|
||||
<< "Usage: " << argv[0] << "\n"
|
||||
<< " [ --feature=<sift|orb|brisk> ] # Feature to use.\n"
|
||||
<< " [ --flann ] # use Flann-based matcher instead of bruteforce.\n"
|
||||
<< " [ --maxlines=<number(50 as default)> ] # The maximum number of lines in visualizing the matching result.\n"
|
||||
<< " [ --image1=<image1(aero1.jpg as default)> ]\n"
|
||||
<< " [ --image2=<image2(aero3.jpg as default)> ] # Path to images to compare."
|
||||
<< endl;
|
||||
}
|
||||
|
||||
static double timer()
|
||||
{
|
||||
return getTickCount() / getTickFrequency();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
vector<String> fileName;
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help h ||}"
|
||||
"{feature|orb|}"
|
||||
"{flann||}"
|
||||
"{maxlines|50|}"
|
||||
"{image1|aero1.jpg|}{image2|aero3.jpg|}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
string feature = parser.get<string>("feature");
|
||||
bool useFlann = parser.has("flann");
|
||||
int maxlines = parser.get<int>("maxlines");
|
||||
fileName.push_back(samples::findFile(parser.get<string>("image1")));
|
||||
fileName.push_back(samples::findFile(parser.get<string>("image2")));
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
cout << "See --help (or missing '=' between argument name and value?)" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Mat img1 = imread(fileName[0], IMREAD_GRAYSCALE);
|
||||
Mat img2 = imread(fileName[1], IMREAD_GRAYSCALE);
|
||||
if (img1.empty())
|
||||
{
|
||||
cerr << "Image " << fileName[0] << " is empty or cannot be found" << endl;
|
||||
return 1;
|
||||
}
|
||||
if (img2.empty())
|
||||
{
|
||||
cerr << "Image " << fileName[1] << " is empty or cannot be found" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Ptr<Feature2D> backend;
|
||||
Ptr<DescriptorMatcher> matcher;
|
||||
|
||||
if (feature == "sift")
|
||||
{
|
||||
backend = SIFT::create();
|
||||
if (useFlann)
|
||||
matcher = DescriptorMatcher::create("FlannBased");
|
||||
else
|
||||
matcher = DescriptorMatcher::create("BruteForce");
|
||||
}
|
||||
else if (feature == "orb")
|
||||
{
|
||||
backend = ORB::create();
|
||||
if (useFlann)
|
||||
matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
|
||||
else
|
||||
matcher = DescriptorMatcher::create("BruteForce-Hamming");
|
||||
}
|
||||
else if (feature == "brisk")
|
||||
{
|
||||
#ifdef HAVE_OPENCV_XFEATURES2D
|
||||
backend = xfeatures2d::BRISK::create();
|
||||
if (useFlann)
|
||||
matcher = makePtr<FlannBasedMatcher>(makePtr<flann::LshIndexParams>(6, 12, 1));
|
||||
else
|
||||
matcher = DescriptorMatcher::create("BruteForce-Hamming");
|
||||
#else
|
||||
cout << "OpenCV is built without opencv_contrib modules. BRISK algorithm is not available!" << std::endl;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << feature << " is not supported. See --help" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << "extracting with " << feature << "..." << endl;
|
||||
Ptr<AffineFeature> ext = AffineFeature::create(backend);
|
||||
vector<KeyPoint> kp1, kp2;
|
||||
Mat desc1, desc2;
|
||||
|
||||
ext->detectAndCompute(img1, Mat(), kp1, desc1);
|
||||
ext->detectAndCompute(img2, Mat(), kp2, desc2);
|
||||
cout << "img1 - " << kp1.size() << " features, "
|
||||
<< "img2 - " << kp2.size() << " features"
|
||||
<< endl;
|
||||
|
||||
cout << "matching with " << (useFlann ? "flann" : "bruteforce") << "..." << endl;
|
||||
double start = timer();
|
||||
// match and draw
|
||||
vector< vector<DMatch> > rawMatches;
|
||||
vector<Point2f> p1, p2;
|
||||
vector<float> distances;
|
||||
matcher->knnMatch(desc1, desc2, rawMatches, 2);
|
||||
// filter_matches
|
||||
for (size_t i = 0; i < rawMatches.size(); i++)
|
||||
{
|
||||
const vector<DMatch>& m = rawMatches[i];
|
||||
if (m.size() == 2 && m[0].distance < m[1].distance * 0.75)
|
||||
{
|
||||
p1.push_back(kp1[m[0].queryIdx].pt);
|
||||
p2.push_back(kp2[m[0].trainIdx].pt);
|
||||
distances.push_back(m[0].distance);
|
||||
}
|
||||
}
|
||||
vector<uchar> status;
|
||||
vector< pair<Point2f, Point2f> > pointPairs;
|
||||
Mat H = findHomography(p1, p2, status, RANSAC);
|
||||
int inliers = 0;
|
||||
for (size_t i = 0; i < status.size(); i++)
|
||||
{
|
||||
if (status[i])
|
||||
{
|
||||
pointPairs.push_back(make_pair(p1[i], p2[i]));
|
||||
distances[inliers] = distances[i];
|
||||
// CV_Assert(inliers <= (int)i);
|
||||
inliers++;
|
||||
}
|
||||
}
|
||||
distances.resize(inliers);
|
||||
|
||||
cout << "execution time: " << fixed << setprecision(2) << (timer()-start)*1000 << " ms" << endl;
|
||||
cout << inliers << " / " << status.size() << " inliers/matched" << endl;
|
||||
|
||||
cout << "visualizing..." << endl;
|
||||
vector<int> indices(inliers);
|
||||
cv::sortIdx(distances, indices, SORT_EVERY_ROW+SORT_ASCENDING);
|
||||
|
||||
// explore_match
|
||||
int h1 = img1.size().height;
|
||||
int w1 = img1.size().width;
|
||||
int h2 = img2.size().height;
|
||||
int w2 = img2.size().width;
|
||||
Mat vis = Mat::zeros(max(h1, h2), w1+w2, CV_8U);
|
||||
img1.copyTo(Mat(vis, Rect(0, 0, w1, h1)));
|
||||
img2.copyTo(Mat(vis, Rect(w1, 0, w2, h2)));
|
||||
cvtColor(vis, vis, COLOR_GRAY2BGR);
|
||||
|
||||
vector<Point2f> corners(4);
|
||||
corners[0] = Point2f(0, 0);
|
||||
corners[1] = Point2f((float)w1, 0);
|
||||
corners[2] = Point2f((float)w1, (float)h1);
|
||||
corners[3] = Point2f(0, (float)h1);
|
||||
vector<Point2i> icorners;
|
||||
perspectiveTransform(corners, corners, H);
|
||||
transform(corners, corners, Matx23f(1,0,(float)w1,0,1,0));
|
||||
Mat(corners).convertTo(icorners, CV_32S);
|
||||
polylines(vis, icorners, true, Scalar(255,255,255));
|
||||
|
||||
for (int i = 0; i < min(inliers, maxlines); i++)
|
||||
{
|
||||
int idx = indices[i];
|
||||
const Point2f& pi1 = pointPairs[idx].first;
|
||||
const Point2f& pi2 = pointPairs[idx].second;
|
||||
circle(vis, pi1, 2, Scalar(0,255,0), -1);
|
||||
circle(vis, pi2 + Point2f((float)w1,0), 2, Scalar(0,255,0), -1);
|
||||
line(vis, pi1, pi2 + Point2f((float)w1,0), Scalar(0,255,0));
|
||||
}
|
||||
if (inliers > maxlines)
|
||||
cout << "only " << maxlines << " inliers are visualized" << endl;
|
||||
imshow("affine find_obj", vis);
|
||||
|
||||
// Mat vis2 = Mat::zeros(max(h1, h2), w1+w2, CV_8U);
|
||||
// Mat warp1;
|
||||
// warpPerspective(img1, warp1, H, Size(w1, h1));
|
||||
// warp1.copyTo(Mat(vis2, Rect(0, 0, w1, h1)));
|
||||
// img2.copyTo(Mat(vis2, Rect(w1, 0, w2, h2)));
|
||||
// imshow("warped", vis2);
|
||||
|
||||
waitKey();
|
||||
cout << "done" << endl;
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,221 @@
|
||||
#include <iostream>
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static const Scalar greenColor(0, 255, 0);
|
||||
static const Scalar redColor(0, 0, 255);
|
||||
static const Scalar yellowColor(0, 255, 255);
|
||||
static Scalar randColor()
|
||||
{
|
||||
RNG &rng = theRNG();
|
||||
return Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
||||
struct TheApp
|
||||
{
|
||||
Ptr<barcode::BarcodeDetector> bardet;
|
||||
//! [output]
|
||||
vector<Point> corners;
|
||||
vector<string> decode_info;
|
||||
vector<string> decode_type;
|
||||
//! [output]
|
||||
bool detectOnly;
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
corners.clear();
|
||||
decode_info.clear();
|
||||
decode_type.clear();
|
||||
}
|
||||
|
||||
inline string modeString() const
|
||||
{
|
||||
return detectOnly ? "<detect>" : "<detectAndDecode>";
|
||||
}
|
||||
|
||||
void drawResults(Mat &frame) const
|
||||
{
|
||||
//! [visualize]
|
||||
for (size_t i = 0; i < corners.size(); i += 4)
|
||||
{
|
||||
const size_t idx = i / 4;
|
||||
const bool isDecodable = idx < decode_info.size()
|
||||
&& idx < decode_type.size()
|
||||
&& !decode_type[idx].empty();
|
||||
const Scalar lineColor = isDecodable ? greenColor : redColor;
|
||||
// draw barcode rectangle
|
||||
vector<Point> contour(corners.begin() + i, corners.begin() + i + 4);
|
||||
const vector< vector<Point> > contours {contour};
|
||||
drawContours(frame, contours, 0, lineColor, 1);
|
||||
// draw vertices
|
||||
for (size_t j = 0; j < 4; j++)
|
||||
circle(frame, contour[j], 2, randColor(), -1);
|
||||
// write decoded text
|
||||
if (isDecodable)
|
||||
{
|
||||
ostringstream buf;
|
||||
buf << "[" << decode_type[idx] << "] " << decode_info[idx];
|
||||
putText(frame, buf.str(), contour[1], FONT_HERSHEY_COMPLEX, 0.8, yellowColor, 1);
|
||||
}
|
||||
}
|
||||
//! [visualize]
|
||||
}
|
||||
|
||||
void drawFPS(Mat &frame, double fps) const
|
||||
{
|
||||
ostringstream buf;
|
||||
buf << modeString()
|
||||
<< " (" << corners.size() / 4 << "/" << decode_type.size() << "/" << decode_info.size() << ") "
|
||||
<< cv::format("%.2f", fps) << " FPS ";
|
||||
putText(frame, buf.str(), Point(25, 25), FONT_HERSHEY_COMPLEX, 0.8, redColor, 2);
|
||||
}
|
||||
|
||||
inline void call_decode(Mat &frame)
|
||||
{
|
||||
cleanup();
|
||||
if (detectOnly)
|
||||
{
|
||||
//! [detect]
|
||||
bardet->detectMulti(frame, corners);
|
||||
//! [detect]
|
||||
}
|
||||
else
|
||||
{
|
||||
//! [detectAndDecode]
|
||||
bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);
|
||||
//! [detectAndDecode]
|
||||
}
|
||||
}
|
||||
|
||||
int liveBarCodeDetect()
|
||||
{
|
||||
VideoCapture cap(0);
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "Cannot open a camera" << endl;
|
||||
return 2;
|
||||
}
|
||||
Mat frame;
|
||||
Mat result;
|
||||
cap >> frame;
|
||||
cout << "Image size: " << frame.size() << endl;
|
||||
cout << "Press 'd' to switch between <detect> and <detectAndDecode> modes" << endl;
|
||||
cout << "Press 'ESC' to exit" << endl;
|
||||
for (;;)
|
||||
{
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
cout << "End of video stream" << endl;
|
||||
break;
|
||||
}
|
||||
if (frame.channels() == 1)
|
||||
cvtColor(frame, frame, COLOR_GRAY2BGR);
|
||||
TickMeter timer;
|
||||
timer.start();
|
||||
call_decode(frame);
|
||||
timer.stop();
|
||||
drawResults(frame);
|
||||
drawFPS(frame, timer.getFPS());
|
||||
imshow("barcode", frame);
|
||||
const char c = (char)waitKey(1);
|
||||
if (c == 'd')
|
||||
{
|
||||
detectOnly = !detectOnly;
|
||||
cout << "Mode switched to " << modeString() << endl;
|
||||
}
|
||||
else if (c == 27)
|
||||
{
|
||||
cout << "'ESC' is pressed. Exiting..." << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imageBarCodeDetect(const string &in_file, const string &out_file)
|
||||
{
|
||||
Mat frame = imread(in_file, IMREAD_COLOR);
|
||||
cout << "Image size: " << frame.size() << endl;
|
||||
cout << "Mode is " << modeString() << endl;
|
||||
const int count_experiments = 100;
|
||||
TickMeter timer;
|
||||
for (size_t i = 0; i < count_experiments; i++)
|
||||
{
|
||||
timer.start();
|
||||
call_decode(frame);
|
||||
timer.stop();
|
||||
}
|
||||
cout << "FPS: " << timer.getFPS() << endl;
|
||||
drawResults(frame);
|
||||
if (!out_file.empty())
|
||||
{
|
||||
cout << "Saving result: " << out_file << endl;
|
||||
imwrite(out_file, frame);
|
||||
}
|
||||
imshow("barcode", frame);
|
||||
cout << "Press any key to exit ..." << endl;
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const string keys = "{h help ? | | print help messages }"
|
||||
"{i in | | input image path (also switches to image detection mode) }"
|
||||
"{detect | false | detect 1D barcode only (skip decoding) }"
|
||||
"{o out | | path to result file (only for single image decode) }"
|
||||
"{sr_model | | optional super resolution ONNX model path (e.g. sr.onnx) }";
|
||||
CommandLineParser cmd_parser(argc, argv, keys);
|
||||
cmd_parser.about("This program detects the 1D barcodes from camera or images using the OpenCV library.");
|
||||
if (cmd_parser.has("help"))
|
||||
{
|
||||
cmd_parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
const string in_file = cmd_parser.get<string>("in");
|
||||
const string out_file = cmd_parser.get<string>("out");
|
||||
const string sr_model = cmd_parser.get<string>("sr_model");
|
||||
if (!cmd_parser.check())
|
||||
{
|
||||
cmd_parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TheApp app;
|
||||
app.detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect");
|
||||
//! [initialize]
|
||||
try
|
||||
{
|
||||
app.bardet = makePtr<barcode::BarcodeDetector>(sr_model);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
cout <<
|
||||
"\n---------------------------------------------------------------\n"
|
||||
"Failed to initialize super resolution.\n"
|
||||
"Please, download 'sr.onnx' from\n"
|
||||
"https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n"
|
||||
"and put it into the current directory.\n"
|
||||
"Or you can leave sr_model unspecified.\n"
|
||||
"---------------------------------------------------------------\n";
|
||||
cout << e.what() << endl;
|
||||
return -1;
|
||||
}
|
||||
//! [initialize]
|
||||
|
||||
if (in_file.empty())
|
||||
return app.liveBarCodeDetect();
|
||||
else
|
||||
return app.imageBarCodeDetect(in_file, out_file);
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
const String keys = "{c camera | 0 | use video stream from camera (device index starting from 0) }"
|
||||
"{fn file_name | | use video file as input }"
|
||||
"{m method | mog2 | method: background subtraction algorithm ('knn', 'mog2')}"
|
||||
"{h help | | show help message}";
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("This sample demonstrates background segmentation.");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
int camera = parser.get<int>("camera");
|
||||
String file = parser.get<String>("file_name");
|
||||
String method = parser.get<String>("method");
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
|
||||
VideoCapture cap;
|
||||
if (file.empty())
|
||||
cap.open(camera);
|
||||
else
|
||||
{
|
||||
file = samples::findFileOrKeep(file); // ignore gstreamer pipelines
|
||||
cap.open(file.c_str());
|
||||
}
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "Can not open video stream: '" << (file.empty() ? "<camera>" : file) << "'" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
Ptr<BackgroundSubtractor> model;
|
||||
if (method == "knn")
|
||||
model = createBackgroundSubtractorKNN();
|
||||
else if (method == "mog2")
|
||||
model = createBackgroundSubtractorMOG2();
|
||||
if (!model)
|
||||
{
|
||||
cout << "Can not create background model using provided method: '" << method << "'" << endl;
|
||||
return 3;
|
||||
}
|
||||
|
||||
cout << "Press <space> to toggle background model update" << endl;
|
||||
cout << "Press 's' to toggle foreground mask smoothing" << endl;
|
||||
cout << "Press ESC or 'q' to exit" << endl;
|
||||
bool doUpdateModel = true;
|
||||
bool doSmoothMask = false;
|
||||
|
||||
Mat inputFrame, frame, foregroundMask, foreground, background;
|
||||
for (;;)
|
||||
{
|
||||
// prepare input frame
|
||||
cap >> inputFrame;
|
||||
if (inputFrame.empty())
|
||||
{
|
||||
cout << "Finished reading: empty frame" << endl;
|
||||
break;
|
||||
}
|
||||
const Size scaledSize(640, 640 * inputFrame.rows / inputFrame.cols);
|
||||
resize(inputFrame, frame, scaledSize, 0, 0, INTER_LINEAR);
|
||||
|
||||
// pass the frame to background model
|
||||
model->apply(frame, foregroundMask, doUpdateModel ? -1 : 0);
|
||||
|
||||
// show processed frame
|
||||
imshow("image", frame);
|
||||
|
||||
// show foreground image and mask (with optional smoothing)
|
||||
if (doSmoothMask)
|
||||
{
|
||||
GaussianBlur(foregroundMask, foregroundMask, Size(11, 11), 3.5, 3.5);
|
||||
threshold(foregroundMask, foregroundMask, 10, 255, THRESH_BINARY);
|
||||
}
|
||||
if (foreground.empty())
|
||||
foreground.create(scaledSize, frame.type());
|
||||
foreground = Scalar::all(0);
|
||||
frame.copyTo(foreground, foregroundMask);
|
||||
imshow("foreground mask", foregroundMask);
|
||||
imshow("foreground image", foreground);
|
||||
|
||||
// show background image
|
||||
model->getBackgroundImage(background);
|
||||
if (!background.empty())
|
||||
imshow("mean background image", background );
|
||||
|
||||
// interact with user
|
||||
const char key = (char)waitKey(30);
|
||||
if (key == 27 || key == 'q') // ESC
|
||||
{
|
||||
cout << "Exit requested" << endl;
|
||||
break;
|
||||
}
|
||||
else if (key == ' ')
|
||||
{
|
||||
doUpdateModel = !doUpdateModel;
|
||||
cout << "Toggle background update: " << (doUpdateModel ? "ON" : "OFF") << endl;
|
||||
}
|
||||
else if (key == 's')
|
||||
{
|
||||
doSmoothMask = !doSmoothMask;
|
||||
cout << "Toggle foreground mask smoothing: " << (doSmoothMask ? "ON" : "OFF") << endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/calib.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/objdetect/charuco_detector.hpp"
|
||||
|
||||
#include <cctype>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
const char * usage =
|
||||
" \nexample command line for calibration from a live feed.\n"
|
||||
" calibration -w=4 -h=5 -s=0.025 -o=camera.yml -op -oe\n"
|
||||
" \n"
|
||||
" example command line for calibration from a list of stored images:\n"
|
||||
" imagelist_creator image_list.xml *.png\n"
|
||||
" calibration -w=4 -h=5 -s=0.025 -o=camera.yml -op -oe image_list.xml\n"
|
||||
" where image_list.xml is the standard OpenCV XML/YAML\n"
|
||||
" use imagelist_creator to create the xml or yaml list\n"
|
||||
" file consisting of the list of strings, e.g.:\n"
|
||||
" \n"
|
||||
"<?xml version=\"1.0\"?>\n"
|
||||
"<opencv_storage>\n"
|
||||
"<images>\n"
|
||||
"view000.png\n"
|
||||
"view001.png\n"
|
||||
"<!-- view002.png -->\n"
|
||||
"view003.png\n"
|
||||
"view010.png\n"
|
||||
"one_extra_view.jpg\n"
|
||||
"</images>\n"
|
||||
"</opencv_storage>\n";
|
||||
|
||||
const char* liveCaptureHelp =
|
||||
"When the live video from camera is used as input, the following hot-keys may be used:\n"
|
||||
" <ESC>, 'q' - quit the program\n"
|
||||
" 'g' - start capturing images\n"
|
||||
" 'u' - switch undistortion on/off\n";
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
printf( "This is a camera calibration sample.\n"
|
||||
"Usage: %s\n"
|
||||
" -w=<board_width> # the calibration board horizontal size in inner corners "
|
||||
"for chessboard and in squares or circles for others like ChArUco or circles grid\n"
|
||||
" -h=<board_height> # the calibration board verical size in inner corners "
|
||||
"for chessboard and in squares or circles for others like ChArUco or circles grid\n"
|
||||
" [-pt=<pattern>] # the type of pattern: chessboard, charuco, circles, acircles\n"
|
||||
" [-n=<number_of_frames>] # the number of frames to use for calibration\n"
|
||||
" # (if not specified, it will be set to the number\n"
|
||||
" # of board views actually available)\n"
|
||||
" [-d=<delay>] # a minimum delay in ms between subsequent attempts to capture a next view\n"
|
||||
" # (used only for video capturing)\n"
|
||||
" [-s=<squareSize>] # square size in some user-defined units (1 by default)\n"
|
||||
" [-ms=<markerSize>] # marker size in some user-defined units (0.5 by default)\n"
|
||||
" [-ad=<arucoDict>] # Aruco dictionary name for ChArUco board. "
|
||||
"Available ArUco dictionaries: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, "
|
||||
"DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, "
|
||||
"DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, "
|
||||
"DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL, "
|
||||
"DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10, DICT_APRILTAG_36h11, DICT_ARUCO_MIP_36h12\n"
|
||||
" [-adf=<dictFilename>] # Custom aruco dictionary file for ChArUco board\n"
|
||||
" [-o=<out_camera_params>] # the output filename for intrinsic [and extrinsic] parameters\n"
|
||||
" [-op] # write detected feature points\n"
|
||||
" [-oe] # write extrinsic parameters\n"
|
||||
" [-oo] # write refined 3D object points\n"
|
||||
" [-zt] # assume zero tangential distortion\n"
|
||||
" [-a=<aspectRatio>] # fix aspect ratio (fx/fy)\n"
|
||||
" [-p] # fix the principal point at the center\n"
|
||||
" [-v] # flip the captured images around the horizontal axis\n"
|
||||
" [-V] # use a video file, and not an image list, uses\n"
|
||||
" # [input_data] string for the video file name\n"
|
||||
" [-su] # show undistorted images after calibration\n"
|
||||
" [-ws=<number_of_pixel>] # half of search window for cornerSubPix (11 by default)\n"
|
||||
" [-fx=<X focal length>] # focal length in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n"
|
||||
" [-fy=<Y focal length>] # focal length in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n"
|
||||
" [-cx=<X center point>] # camera center point in X-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n"
|
||||
" [-cy=<Y center point>] # camera center point in Y-dir as an initial intrinsic guess (if this flag is used, fx, fy, cx, cy must be set)\n"
|
||||
" [-imshow-scale # image resize scaling factor when displaying the results (must be >= 1)\n"
|
||||
" [-enable-k3=<0/1> # to enable (1) or disable (0) K3 coefficient for the distortion model\n"
|
||||
" [-dt=<distance>] # actual distance between top-left and top-right corners of\n"
|
||||
" # the calibration grid. If this parameter is specified, a more\n"
|
||||
" # accurate calibration method will be used which may be better\n"
|
||||
" # with inaccurate, roughly planar target.\n"
|
||||
" [-imread-apply-exif-rot] # use this flag to apply the exif orientation when reading images from list\n"
|
||||
" [input_data] # input data, one of the following:\n"
|
||||
" # - text file with a list of the images of the board\n"
|
||||
" # the text file can be generated with imagelist_creator\n"
|
||||
" # - name of video file with a video of the board\n"
|
||||
" # if input_data not specified, a live view from the camera is used\n"
|
||||
"\n", argv[0] );
|
||||
printf("\n%s",usage);
|
||||
printf( "\n%s", liveCaptureHelp );
|
||||
}
|
||||
|
||||
enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 };
|
||||
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID, CHARUCOBOARD};
|
||||
|
||||
static double computeReprojectionErrors(
|
||||
const vector<vector<Point3f> >& objectPoints,
|
||||
const vector<vector<Point2f> >& imagePoints,
|
||||
const vector<Mat>& rvecs, const vector<Mat>& tvecs,
|
||||
const Mat& cameraMatrix, const Mat& distCoeffs,
|
||||
vector<float>& perViewErrors )
|
||||
{
|
||||
vector<Point2f> imagePoints2;
|
||||
int i, totalPoints = 0;
|
||||
double totalErr = 0, err;
|
||||
perViewErrors.resize(objectPoints.size());
|
||||
|
||||
for( i = 0; i < (int)objectPoints.size(); i++ )
|
||||
{
|
||||
projectPoints(Mat(objectPoints[i]), rvecs[i], tvecs[i],
|
||||
cameraMatrix, distCoeffs, imagePoints2);
|
||||
err = norm(Mat(imagePoints[i]), Mat(imagePoints2), NORM_L2);
|
||||
int n = (int)objectPoints[i].size();
|
||||
perViewErrors[i] = (float)std::sqrt(err*err/n);
|
||||
totalErr += err*err;
|
||||
totalPoints += n;
|
||||
}
|
||||
|
||||
return std::sqrt(totalErr/totalPoints);
|
||||
}
|
||||
|
||||
static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
|
||||
{
|
||||
corners.resize(0);
|
||||
|
||||
switch(patternType)
|
||||
{
|
||||
case CHESSBOARD:
|
||||
case CIRCLES_GRID:
|
||||
for( int i = 0; i < boardSize.height; i++ )
|
||||
for( int j = 0; j < boardSize.width; j++ )
|
||||
corners.push_back(Point3f(float(j*squareSize),
|
||||
float(i*squareSize), 0));
|
||||
break;
|
||||
|
||||
case ASYMMETRIC_CIRCLES_GRID:
|
||||
for( int i = 0; i < boardSize.height; i++ )
|
||||
for( int j = 0; j < boardSize.width; j++ )
|
||||
corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
|
||||
float(i*squareSize), 0));
|
||||
break;
|
||||
|
||||
case CHARUCOBOARD:
|
||||
for( int i = 0; i < boardSize.height-1; i++ )
|
||||
for( int j = 0; j < boardSize.width-1; j++ )
|
||||
corners.push_back(Point3f(float(j*squareSize),
|
||||
float(i*squareSize), 0));
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
|
||||
}
|
||||
}
|
||||
|
||||
static bool runCalibration( vector<vector<Point2f> > imagePoints,
|
||||
Size imageSize, Size boardSize, Pattern patternType,
|
||||
float squareSize, float aspectRatio,
|
||||
float grid_width, bool release_object,
|
||||
int flags, Mat& cameraMatrix, Mat& distCoeffs,
|
||||
vector<Mat>& rvecs, vector<Mat>& tvecs,
|
||||
vector<float>& reprojErrs,
|
||||
vector<Point3f>& newObjPoints,
|
||||
double& totalAvgErr)
|
||||
{
|
||||
if( flags & CALIB_FIX_ASPECT_RATIO )
|
||||
cameraMatrix.at<double>(0,0) = aspectRatio;
|
||||
|
||||
distCoeffs = Mat::zeros(8, 1, CV_64F);
|
||||
|
||||
vector<vector<Point3f> > objectPoints(1);
|
||||
calcChessboardCorners(boardSize, squareSize, objectPoints[0], patternType);
|
||||
// Board imperfectness correction introduced in PR #12772
|
||||
// The correction does not make sense for asymmetric and assymetric circles grids
|
||||
if (patternType == CHESSBOARD)
|
||||
{
|
||||
int offset = boardSize.width - 1;
|
||||
objectPoints[0][offset].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
else if (patternType == CHARUCOBOARD)
|
||||
{
|
||||
int offset = boardSize.width - 2;
|
||||
objectPoints[0][offset].x = objectPoints[0][0].x + grid_width;
|
||||
}
|
||||
|
||||
newObjPoints = objectPoints[0];
|
||||
|
||||
objectPoints.resize(imagePoints.size(),objectPoints[0]);
|
||||
|
||||
double rms;
|
||||
int iFixedPoint = -1;
|
||||
if (release_object)
|
||||
iFixedPoint = boardSize.width - 1;
|
||||
rms = calibrateCameraRO(objectPoints, imagePoints, imageSize, iFixedPoint,
|
||||
cameraMatrix, distCoeffs, rvecs, tvecs, newObjPoints,
|
||||
flags | CALIB_USE_LU);
|
||||
printf("RMS error reported by calibrateCamera: %g\n", rms);
|
||||
|
||||
bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs);
|
||||
|
||||
if (release_object) {
|
||||
cout << "New board corners: " << endl;
|
||||
cout << newObjPoints[0] << endl;
|
||||
cout << newObjPoints[boardSize.width - 1] << endl;
|
||||
cout << newObjPoints[boardSize.width * (boardSize.height - 1)] << endl;
|
||||
cout << newObjPoints.back() << endl;
|
||||
}
|
||||
|
||||
objectPoints.clear();
|
||||
objectPoints.resize(imagePoints.size(), newObjPoints);
|
||||
totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints,
|
||||
rvecs, tvecs, cameraMatrix, distCoeffs, reprojErrs);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
static void saveCameraParams( const string& filename,
|
||||
Size imageSize, Size boardSize,
|
||||
float squareSize, float aspectRatio, int flags,
|
||||
const Mat& cameraMatrix, const Mat& distCoeffs,
|
||||
const vector<Mat>& rvecs, const vector<Mat>& tvecs,
|
||||
const vector<float>& reprojErrs,
|
||||
const vector<vector<Point2f> >& imagePoints,
|
||||
const vector<Point3f>& newObjPoints,
|
||||
double totalAvgErr )
|
||||
{
|
||||
FileStorage fs( filename, FileStorage::WRITE );
|
||||
|
||||
time_t tt;
|
||||
time( &tt );
|
||||
struct tm *t2 = localtime( &tt );
|
||||
char buf[1024];
|
||||
strftime( buf, sizeof(buf)-1, "%c", t2 );
|
||||
|
||||
fs << "calibration_time" << buf;
|
||||
|
||||
if( !rvecs.empty() || !reprojErrs.empty() )
|
||||
fs << "nframes" << (int)std::max(rvecs.size(), reprojErrs.size());
|
||||
fs << "image_width" << imageSize.width;
|
||||
fs << "image_height" << imageSize.height;
|
||||
fs << "board_width" << boardSize.width;
|
||||
fs << "board_height" << boardSize.height;
|
||||
fs << "square_size" << squareSize;
|
||||
|
||||
if( flags & CALIB_FIX_ASPECT_RATIO )
|
||||
fs << "aspectRatio" << aspectRatio;
|
||||
|
||||
if( flags != 0 )
|
||||
{
|
||||
snprintf( buf, sizeof(buf), "flags: %s%s%s%s",
|
||||
flags & CALIB_USE_INTRINSIC_GUESS ? "+use_intrinsic_guess" : "",
|
||||
flags & CALIB_FIX_ASPECT_RATIO ? "+fix_aspectRatio" : "",
|
||||
flags & CALIB_FIX_PRINCIPAL_POINT ? "+fix_principal_point" : "",
|
||||
flags & CALIB_ZERO_TANGENT_DIST ? "+zero_tangent_dist" : "" );
|
||||
//cvWriteComment( *fs, buf, 0 );
|
||||
}
|
||||
|
||||
fs << "flags" << flags;
|
||||
|
||||
fs << "camera_matrix" << cameraMatrix;
|
||||
fs << "distortion_coefficients" << distCoeffs;
|
||||
|
||||
fs << "avg_reprojection_error" << totalAvgErr;
|
||||
if( !reprojErrs.empty() )
|
||||
fs << "per_view_reprojection_errors" << Mat(reprojErrs);
|
||||
|
||||
if( !rvecs.empty() && !tvecs.empty() )
|
||||
{
|
||||
CV_Assert(rvecs[0].type() == tvecs[0].type());
|
||||
Mat bigmat((int)rvecs.size(), 6, rvecs[0].type());
|
||||
for( int i = 0; i < (int)rvecs.size(); i++ )
|
||||
{
|
||||
Mat r = bigmat(Range(i, i+1), Range(0,3));
|
||||
Mat t = bigmat(Range(i, i+1), Range(3,6));
|
||||
|
||||
CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1);
|
||||
CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1);
|
||||
//*.t() is MatExpr (not Mat) so we can use assignment operator
|
||||
r = rvecs[i].t();
|
||||
t = tvecs[i].t();
|
||||
}
|
||||
//cvWriteComment( *fs, "a set of 6-tuples (rotation vector + translation vector) for each view", 0 );
|
||||
fs << "extrinsic_parameters" << bigmat;
|
||||
}
|
||||
|
||||
if( !imagePoints.empty() )
|
||||
{
|
||||
Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2);
|
||||
for( int i = 0; i < (int)imagePoints.size(); i++ )
|
||||
{
|
||||
Mat r = imagePtMat.row(i).reshape(2, imagePtMat.cols);
|
||||
Mat imgpti(imagePoints[i]);
|
||||
imgpti.copyTo(r);
|
||||
}
|
||||
fs << "image_points" << imagePtMat;
|
||||
}
|
||||
|
||||
if( !newObjPoints.empty() )
|
||||
{
|
||||
fs << "grid_points" << newObjPoints;
|
||||
}
|
||||
}
|
||||
|
||||
static bool readStringList( const string& filename, vector<string>& l )
|
||||
{
|
||||
l.resize(0);
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
size_t dir_pos = filename.rfind('/');
|
||||
if (dir_pos == string::npos)
|
||||
dir_pos = filename.rfind('\\');
|
||||
FileNode n = fs.getFirstTopLevelNode();
|
||||
if( n.type() != FileNode::SEQ )
|
||||
return false;
|
||||
FileNodeIterator it = n.begin(), it_end = n.end();
|
||||
for( ; it != it_end; ++it )
|
||||
{
|
||||
string fname = (string)*it;
|
||||
if (dir_pos != string::npos)
|
||||
{
|
||||
string fpath = samples::findFile(filename.substr(0, dir_pos + 1) + fname, false);
|
||||
if (fpath.empty())
|
||||
{
|
||||
fpath = samples::findFile(fname);
|
||||
}
|
||||
fname = fpath;
|
||||
}
|
||||
else
|
||||
{
|
||||
fname = samples::findFile(fname);
|
||||
}
|
||||
l.push_back(fname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool runAndSave(const string& outputFilename,
|
||||
const vector<vector<Point2f> >& imagePoints,
|
||||
Size imageSize, Size boardSize, Pattern patternType, float squareSize,
|
||||
float grid_width, bool release_object,
|
||||
float aspectRatio, int flags, Mat& cameraMatrix,
|
||||
Mat& distCoeffs, bool writeExtrinsics, bool writePoints, bool writeGrid )
|
||||
{
|
||||
vector<Mat> rvecs, tvecs;
|
||||
vector<float> reprojErrs;
|
||||
double totalAvgErr = 0;
|
||||
vector<Point3f> newObjPoints;
|
||||
|
||||
bool ok = runCalibration(imagePoints, imageSize, boardSize, patternType, squareSize,
|
||||
aspectRatio, grid_width, release_object, flags, cameraMatrix, distCoeffs,
|
||||
rvecs, tvecs, reprojErrs, newObjPoints, totalAvgErr);
|
||||
printf("%s. avg reprojection error = %.7f\n",
|
||||
ok ? "Calibration succeeded" : "Calibration failed",
|
||||
totalAvgErr);
|
||||
|
||||
if( ok )
|
||||
saveCameraParams( outputFilename, imageSize,
|
||||
boardSize, squareSize, aspectRatio,
|
||||
flags, cameraMatrix, distCoeffs,
|
||||
writeExtrinsics ? rvecs : vector<Mat>(),
|
||||
writeExtrinsics ? tvecs : vector<Mat>(),
|
||||
writeExtrinsics ? reprojErrs : vector<float>(),
|
||||
writePoints ? imagePoints : vector<vector<Point2f> >(),
|
||||
writeGrid ? newObjPoints : vector<Point3f>(),
|
||||
totalAvgErr );
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
Size boardSize, imageSize;
|
||||
float squareSize, markerSize, aspectRatio = 1;
|
||||
Mat cameraMatrix, distCoeffs;
|
||||
string outputFilename;
|
||||
string inputFilename = "";
|
||||
int arucoDict;
|
||||
string dictFilename;
|
||||
|
||||
int i, nframes;
|
||||
bool writeExtrinsics, writePoints;
|
||||
bool undistortImage = false;
|
||||
int flags = 0;
|
||||
VideoCapture capture;
|
||||
bool flipVertical;
|
||||
bool showUndistorted;
|
||||
bool videofile;
|
||||
int delay;
|
||||
clock_t prevTimestamp = 0;
|
||||
int mode = DETECTION;
|
||||
int cameraId = 0;
|
||||
vector<vector<Point2f>> imagePoints;
|
||||
vector<string> imageList;
|
||||
Pattern pattern = CHESSBOARD;
|
||||
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{help ||}{w||}{h||}{pt|chessboard|}{n|10|}{d|1000|}{s|1|}{ms|0.5|}{ad|DICT_4X4_50|}{adf|None|}{o|out_camera_data.yml|}"
|
||||
"{op||}{oe||}{zt||}{a||}{p||}{v||}{V||}{su||}"
|
||||
"{oo||}{ws|11|}{dt||}"
|
||||
"{fx||}{fy||}{cx||}{cy||}"
|
||||
"{imshow-scale|1|}{enable-k3|0|}"
|
||||
"{imread-apply-exif-rot||}"
|
||||
"{@input_data|0|}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
boardSize.width = parser.get<int>( "w" );
|
||||
boardSize.height = parser.get<int>( "h" );
|
||||
if ( parser.has("pt") )
|
||||
{
|
||||
string val = parser.get<string>("pt");
|
||||
if( val == "circles" )
|
||||
pattern = CIRCLES_GRID;
|
||||
else if( val == "acircles" )
|
||||
pattern = ASYMMETRIC_CIRCLES_GRID;
|
||||
else if( val == "chessboard" )
|
||||
pattern = CHESSBOARD;
|
||||
else if( val == "charuco" )
|
||||
pattern = CHARUCOBOARD;
|
||||
else
|
||||
return fprintf( stderr, "Invalid pattern type: must be chessboard or circles\n" ), -1;
|
||||
}
|
||||
|
||||
squareSize = parser.get<float>("s");
|
||||
markerSize = parser.get<float>("ms");
|
||||
|
||||
string arucoDictName = parser.get<string>("ad");
|
||||
if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; }
|
||||
else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; }
|
||||
else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; }
|
||||
else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; }
|
||||
else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; }
|
||||
else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; }
|
||||
else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; }
|
||||
else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; }
|
||||
else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; }
|
||||
else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; }
|
||||
else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; }
|
||||
else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; }
|
||||
else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; }
|
||||
else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; }
|
||||
else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; }
|
||||
else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; }
|
||||
else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; }
|
||||
else if (arucoDictName == "DICT_ARUCO_MIP_36h12") { arucoDict = cv::aruco::DICT_ARUCO_MIP_36h12; }
|
||||
else {
|
||||
cout << "Incorrect Aruco dictionary name " << arucoDictName << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
dictFilename = parser.get<std::string>("adf");
|
||||
nframes = parser.get<int>("n");
|
||||
delay = parser.get<int>("d");
|
||||
writePoints = parser.has("op");
|
||||
writeExtrinsics = parser.has("oe");
|
||||
bool writeGrid = parser.has("oo");
|
||||
if (parser.has("a")) {
|
||||
flags |= CALIB_FIX_ASPECT_RATIO;
|
||||
aspectRatio = parser.get<float>("a");
|
||||
}
|
||||
if ( parser.has("zt") )
|
||||
flags |= CALIB_ZERO_TANGENT_DIST;
|
||||
if ( parser.has("p") )
|
||||
flags |= CALIB_FIX_PRINCIPAL_POINT;
|
||||
flipVertical = parser.has("v");
|
||||
videofile = parser.has("V");
|
||||
if ( parser.has("o") )
|
||||
outputFilename = parser.get<string>("o");
|
||||
showUndistorted = parser.has("su");
|
||||
if ( isdigit(parser.get<string>("@input_data")[0]) )
|
||||
cameraId = parser.get<int>("@input_data");
|
||||
else
|
||||
inputFilename = parser.get<string>("@input_data");
|
||||
int winSize = parser.get<int>("ws");
|
||||
cameraMatrix = Mat::eye(3, 3, CV_64F);
|
||||
if (parser.has("fx") && parser.has("fy") && parser.has("cx") && parser.has("cy"))
|
||||
{
|
||||
cameraMatrix.at<double>(0,0) = parser.get<double>("fx");
|
||||
cameraMatrix.at<double>(0,2) = parser.get<double>("cx");
|
||||
cameraMatrix.at<double>(1,1) = parser.get<double>("fy");
|
||||
cameraMatrix.at<double>(1,2) = parser.get<double>("cy");
|
||||
flags |= CALIB_USE_INTRINSIC_GUESS;
|
||||
std::cout << "Use the following camera matrix as an initial guess:\n" << cameraMatrix << std::endl;
|
||||
}
|
||||
int viewScaleFactor = parser.get<int>("imshow-scale");
|
||||
bool useK3 = parser.get<bool>("enable-k3");
|
||||
std::cout << "Use K3 distortion coefficient? " << (useK3 ? "yes" : "no") << std::endl;
|
||||
if (!useK3)
|
||||
{
|
||||
flags |= CALIB_FIX_K3;
|
||||
}
|
||||
int imread_flag = IMREAD_COLOR;
|
||||
if (!parser.has("imread-apply-exif-rot"))
|
||||
{
|
||||
imread_flag |= IMREAD_IGNORE_ORIENTATION;
|
||||
}
|
||||
|
||||
float grid_width = squareSize *(pattern != CHARUCOBOARD ? (boardSize.width - 1): (boardSize.width - 2) );
|
||||
bool release_object = false;
|
||||
if (parser.has("dt")) {
|
||||
grid_width = parser.get<float>("dt");
|
||||
release_object = true;
|
||||
}
|
||||
if (!parser.check())
|
||||
{
|
||||
help(argv);
|
||||
parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
if ( squareSize <= 0 )
|
||||
return fprintf( stderr, "Invalid board square width\n" ), -1;
|
||||
if ( nframes <= 3 )
|
||||
return printf("Invalid number of images\n" ), -1;
|
||||
if ( aspectRatio <= 0 )
|
||||
return printf( "Invalid aspect ratio\n" ), -1;
|
||||
if ( delay <= 0 )
|
||||
return printf( "Invalid delay\n" ), -1;
|
||||
if ( boardSize.width <= 0 )
|
||||
return fprintf( stderr, "Invalid board width\n" ), -1;
|
||||
if ( boardSize.height <= 0 )
|
||||
return fprintf( stderr, "Invalid board height\n" ), -1;
|
||||
|
||||
cv::aruco::Dictionary dictionary;
|
||||
if (pattern == CHARUCOBOARD) {
|
||||
if (dictFilename == "None") {
|
||||
std::cout << "Using predefined dictionary with id: " << arucoDict << std::endl;
|
||||
dictionary = aruco::getPredefinedDictionary(arucoDict);
|
||||
}
|
||||
else {
|
||||
std::cout << "Using custom dictionary from file: " << dictFilename << std::endl;
|
||||
cv::FileStorage dict_file(dictFilename, cv::FileStorage::Mode::READ);
|
||||
cv::FileNode fn(dict_file.root());
|
||||
dictionary.readDictionary(fn);
|
||||
}
|
||||
}
|
||||
|
||||
cv::Ptr<cv::aruco::CharucoBoard> ch_board;
|
||||
std::vector<int> markerIds;
|
||||
cv::Ptr<cv::aruco::CharucoDetector> ch_detector;
|
||||
if (pattern == CHARUCOBOARD) {
|
||||
ch_board = cv::makePtr<cv::aruco::CharucoBoard>(boardSize, squareSize, markerSize, dictionary);
|
||||
ch_detector = cv::makePtr<cv::aruco::CharucoDetector>(cv::aruco::CharucoDetector(*ch_board));
|
||||
}
|
||||
|
||||
|
||||
if( !inputFilename.empty() )
|
||||
{
|
||||
if( !videofile && readStringList(samples::findFile(inputFilename), imageList) )
|
||||
mode = CAPTURING;
|
||||
else
|
||||
capture.open(samples::findFileOrKeep(inputFilename));
|
||||
}
|
||||
else
|
||||
capture.open(cameraId);
|
||||
|
||||
if( !capture.isOpened() && imageList.empty() )
|
||||
return fprintf( stderr, "Could not initialize video (%d) capture\n", cameraId ), -2;
|
||||
|
||||
if( !imageList.empty() )
|
||||
nframes = (int)imageList.size();
|
||||
|
||||
if( capture.isOpened() )
|
||||
printf( "%s", liveCaptureHelp );
|
||||
|
||||
namedWindow( "Image View", cv::WINDOW_AUTOSIZE );
|
||||
|
||||
for(i = 0;;i++)
|
||||
{
|
||||
Mat view, viewGray;
|
||||
bool blink = false;
|
||||
|
||||
if( capture.isOpened() )
|
||||
{
|
||||
Mat view0;
|
||||
capture >> view0;
|
||||
view0.copyTo(view);
|
||||
}
|
||||
else if( i < (int)imageList.size() )
|
||||
view = imread(imageList[i], imread_flag);
|
||||
|
||||
if(view.empty())
|
||||
{
|
||||
if( imagePoints.size() > 0 )
|
||||
runAndSave(outputFilename, imagePoints, imageSize,
|
||||
boardSize, pattern, squareSize, grid_width, release_object, aspectRatio,
|
||||
flags, cameraMatrix, distCoeffs,
|
||||
writeExtrinsics, writePoints, writeGrid);
|
||||
break;
|
||||
}
|
||||
|
||||
imageSize = view.size();
|
||||
|
||||
if( flipVertical )
|
||||
flip( view, view, 0 );
|
||||
|
||||
vector<Point2f> pointbuf;
|
||||
cvtColor(view, viewGray, COLOR_BGR2GRAY);
|
||||
|
||||
bool found;
|
||||
switch( pattern )
|
||||
{
|
||||
case CHESSBOARD:
|
||||
found = findChessboardCorners( view, boardSize, pointbuf,
|
||||
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_FAST_CHECK | CALIB_CB_NORMALIZE_IMAGE);
|
||||
break;
|
||||
case CIRCLES_GRID:
|
||||
found = findCirclesGrid( view, boardSize, pointbuf );
|
||||
break;
|
||||
case ASYMMETRIC_CIRCLES_GRID:
|
||||
found = findCirclesGrid( view, boardSize, pointbuf, CALIB_CB_ASYMMETRIC_GRID );
|
||||
break;
|
||||
case CHARUCOBOARD:
|
||||
{
|
||||
ch_detector->detectBoard(view, pointbuf, markerIds);
|
||||
found = pointbuf.size() == (size_t)(boardSize.width-1)*(boardSize.height-1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return fprintf( stderr, "Unknown pattern type\n" ), -1;
|
||||
}
|
||||
|
||||
// improve the found corners' coordinate accuracy
|
||||
if( pattern == CHESSBOARD && found) cornerSubPix( viewGray, pointbuf, Size(winSize,winSize),
|
||||
Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.0001 ));
|
||||
|
||||
if( mode == CAPTURING && found &&
|
||||
(!capture.isOpened() || clock() - prevTimestamp > delay*1e-3*CLOCKS_PER_SEC) )
|
||||
{
|
||||
imagePoints.push_back(pointbuf);
|
||||
prevTimestamp = clock();
|
||||
blink = capture.isOpened();
|
||||
}
|
||||
|
||||
if(found)
|
||||
{
|
||||
if(pattern != CHARUCOBOARD)
|
||||
drawChessboardCorners( view, boardSize, Mat(pointbuf), found );
|
||||
else
|
||||
drawChessboardCorners( view, Size(boardSize.width-1, boardSize.height-1), Mat(pointbuf), found );
|
||||
}
|
||||
|
||||
string msg = mode == CAPTURING ? "100/100" :
|
||||
mode == CALIBRATED ? "Calibrated" : "Press 'g' to start";
|
||||
int baseLine = 0;
|
||||
Size textSize = getTextSize(msg, 1, 1, 1, &baseLine);
|
||||
Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10);
|
||||
|
||||
if( mode == CAPTURING )
|
||||
{
|
||||
if(undistortImage)
|
||||
msg = cv::format( "%d/%d Undist", (int)imagePoints.size(), nframes );
|
||||
else
|
||||
msg = cv::format( "%d/%d", (int)imagePoints.size(), nframes );
|
||||
}
|
||||
|
||||
putText( view, msg, textOrigin, 1, 1,
|
||||
mode != CALIBRATED ? Scalar(0,0,255) : Scalar(0,255,0));
|
||||
|
||||
if( blink )
|
||||
bitwise_not(view, view);
|
||||
|
||||
if( mode == CALIBRATED && undistortImage )
|
||||
{
|
||||
Mat temp = view.clone();
|
||||
undistort(temp, view, cameraMatrix, distCoeffs);
|
||||
}
|
||||
if (viewScaleFactor > 1)
|
||||
{
|
||||
Mat viewScale;
|
||||
resize(view, viewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA);
|
||||
imshow("Image View", viewScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
imshow("Image View", view);
|
||||
}
|
||||
|
||||
char key = (char)waitKey(capture.isOpened() ? 50 : 500);
|
||||
|
||||
if( key == 27 )
|
||||
break;
|
||||
|
||||
if( key == 'u' && mode == CALIBRATED )
|
||||
undistortImage = !undistortImage;
|
||||
|
||||
if( capture.isOpened() && key == 'g' )
|
||||
{
|
||||
mode = CAPTURING;
|
||||
imagePoints.clear();
|
||||
}
|
||||
|
||||
if( mode == CAPTURING && imagePoints.size() >= (unsigned)nframes )
|
||||
{
|
||||
if( runAndSave(outputFilename, imagePoints, imageSize,
|
||||
boardSize, pattern, squareSize, grid_width, release_object, aspectRatio,
|
||||
flags, cameraMatrix, distCoeffs,
|
||||
writeExtrinsics, writePoints, writeGrid))
|
||||
mode = CALIBRATED;
|
||||
else
|
||||
mode = DETECTION;
|
||||
if( !capture.isOpened() )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( !capture.isOpened() && showUndistorted )
|
||||
{
|
||||
Mat view, rview, map1, map2;
|
||||
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
|
||||
getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0),
|
||||
imageSize, CV_16SC2, map1, map2);
|
||||
|
||||
for( i = 0; i < (int)imageList.size(); i++ )
|
||||
{
|
||||
view = imread(imageList[i], imread_flag);
|
||||
if(view.empty())
|
||||
continue;
|
||||
remap(view, rview, map1, map2, INTER_LINEAR);
|
||||
if (viewScaleFactor > 1)
|
||||
{
|
||||
Mat rviewScale;
|
||||
resize(rview, rviewScale, Size(), 1.0/viewScaleFactor, 1.0/viewScaleFactor, INTER_AREA);
|
||||
imshow("Image View", rviewScale);
|
||||
}
|
||||
else
|
||||
{
|
||||
imshow("Image View", rview);
|
||||
}
|
||||
char c = (char)waitKey();
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
//! [tutorial]
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/photo.hpp>
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#include <opencv2/dnn.hpp>
|
||||
#include <iostream>
|
||||
#include "../dnn/common.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
using namespace cv::dnn;
|
||||
using namespace cv::ccm;
|
||||
using namespace mcc;
|
||||
|
||||
const string about =
|
||||
"This sample detects Macbeth color checker using DNN or thresholding and applies color correction."
|
||||
"To run default:\n"
|
||||
"\t ./example_cpp_color_correction_model --input=path/to/your/input/image --query=path/to/your/query/image\n"
|
||||
"With DNN model:\n"
|
||||
"\t ./example_cpp_color_correction_model mcc --input=path/to/your/input/image --query=path/to/your/query/image\n\n"
|
||||
"Using pre-computed CCM:\n"
|
||||
"\t ./example_cpp_color_correction_model mcc --ccm_file=path/to/ccm_output.yaml --query=path/to/your/query/image\n\n"
|
||||
"Model path can also be specified using --model argument. And config path can be specified using --config. Download it using python download_models.py mcc from dnn samples directory\n\n";
|
||||
|
||||
const string param_keys =
|
||||
"{ help h | | Print help message. }"
|
||||
"{ @alias | | An alias name of model to extract preprocessing parameters from models.yml file. }"
|
||||
"{ zoo | ../dnn/models.yml | An optional path to file with preprocessing parameters }"
|
||||
"{ input i | mcc_ccm_test.jpg | Path to input image for computing CCM.}"
|
||||
"{ query q | baboon.jpg | Path to query image to apply color correction. If not provided, input image will be used. }"
|
||||
"{ type | 0 | chartType: 0-Standard, 1-DigitalSG, 2-Vinyl }"
|
||||
"{ num_charts | 1 | Maximum number of charts in the image }"
|
||||
"{ ccm_file | | Path to YAML file containing pre-computed CCM parameters}";
|
||||
|
||||
const string backend_keys = format(
|
||||
"{ backend | default | Choose one of computation backends: "
|
||||
"default: automatically (by default), "
|
||||
"openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), "
|
||||
"opencv: OpenCV implementation, "
|
||||
"vkcom: VKCOM, "
|
||||
"cuda: CUDA, "
|
||||
"webnn: WebNN }");
|
||||
|
||||
const string target_keys = format(
|
||||
"{ target | cpu | Choose one of target computation devices: "
|
||||
"cpu: CPU target (by default), "
|
||||
"opencl: OpenCL, "
|
||||
"opencl_fp16: OpenCL fp16 (half-float precision), "
|
||||
"vpu: VPU, "
|
||||
"vulkan: Vulkan, "
|
||||
"cuda: CUDA, "
|
||||
"cuda_fp16: CUDA fp16 (half-float preprocess) }");
|
||||
|
||||
string keys = param_keys + backend_keys + target_keys;
|
||||
|
||||
static bool processFrame(const Mat& frame, Ptr<CCheckerDetector> detector, Mat& src, int nc){
|
||||
if (!detector->process(frame, nc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
vector<Ptr<CChecker>> checkers = detector->getListColorChecker();
|
||||
src = checkers[0]->getChartsRGB(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about(about);
|
||||
|
||||
if (parser.has("help")) {
|
||||
cout << about << endl;
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
string modelName = parser.get<String>("@alias");
|
||||
string zooFile = parser.get<String>("zoo");
|
||||
const char* path = getenv("OPENCV_SAMPLES_DATA_PATH");
|
||||
|
||||
if ((path != NULL) || parser.has("@alias")) {
|
||||
zooFile = findFile(zooFile);
|
||||
}
|
||||
else{
|
||||
cout<<"[WARN] set the environment variables or pass the arguments --model, --config and models.yml file using --zoo for using dnn based detector. Continuing with default detector.\n\n";
|
||||
}
|
||||
keys += genPreprocArguments(modelName, zooFile);
|
||||
parser = CommandLineParser(argc, argv, keys);
|
||||
|
||||
int t = parser.get<int>("type");
|
||||
if (t < 0 || t > 2)
|
||||
{
|
||||
cout << "Error: --type must be 0, 1 or 2" << endl;
|
||||
parser.printMessage(); // prints full usage
|
||||
return -1;
|
||||
}
|
||||
|
||||
ColorChart chartType = ColorChart(t);
|
||||
|
||||
const string sha1 = parser.get<String>("sha1");
|
||||
const string modelPath = findModel(parser.get<string>("model"), sha1);
|
||||
const string config_sha1 = parser.get<String>("config_sha1");
|
||||
const string configPath = findModel(parser.get<string>("config"), config_sha1);
|
||||
const string backend = parser.get<String>("backend");
|
||||
const string target = parser.get<String>("target");
|
||||
|
||||
int nc = parser.get<int>("num_charts");
|
||||
|
||||
// Get input and target image paths
|
||||
const string inputFile = parser.get<String>("input");
|
||||
const string queryFile = parser.get<String>("query");
|
||||
const string ccmFile = parser.get<String>("ccm_file");
|
||||
|
||||
if (!ccmFile.empty()) {
|
||||
// When ccm_file is provided, only query is required
|
||||
if (queryFile.empty()) {
|
||||
cout << "Error: Query image path must be provided when using pre-computed CCM." << endl;
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// Original validation for when computing new CCM
|
||||
if (inputFile.empty()) {
|
||||
cout << "Error: Input image path must be provided." << endl;
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ColorCorrectionModel model;
|
||||
Mat queryImage;
|
||||
|
||||
if (!ccmFile.empty()) {
|
||||
// Load CCM from YAML file
|
||||
FileStorage fs(ccmFile, FileStorage::READ);
|
||||
if (!fs.isOpened()) {
|
||||
cout << "Error: Unable to open CCM file: " << ccmFile << endl;
|
||||
return -1;
|
||||
}
|
||||
model.read(fs["ColorCorrectionModel"]);
|
||||
fs.release();
|
||||
cout << "Loaded CCM from file: " << ccmFile << endl;
|
||||
|
||||
// Read query image when using pre-computed CCM
|
||||
queryImage = imread(findFile(queryFile));
|
||||
if (queryImage.empty()) {
|
||||
cout << "Error: Unable to read query image." << endl;
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// Read input image for computing new CCM
|
||||
Mat originalImage = imread(findFile(inputFile));
|
||||
if (originalImage.empty()) {
|
||||
cout << "Error: Unable to read input image." << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Process first image to compute CCM
|
||||
Mat image = originalImage.clone();
|
||||
Mat src;
|
||||
|
||||
Ptr<CCheckerDetector> detector;
|
||||
if (!modelPath.empty() && !configPath.empty()) {
|
||||
Net net = readNetFromTensorflow(modelPath, configPath);
|
||||
net.setPreferableBackend(getBackendID(backend));
|
||||
net.setPreferableTarget(getTargetID(target));
|
||||
detector = CCheckerDetector::create(net);
|
||||
cout << "Using DNN-based checker detector." << endl;
|
||||
} else {
|
||||
detector = CCheckerDetector::create();
|
||||
cout << "Using thresholding-based checker detector." << endl;
|
||||
}
|
||||
detector->setColorChartType(chartType);
|
||||
|
||||
if (!processFrame(image, detector, src, nc)) {
|
||||
cout << "No chart detected in the input image!" << endl;
|
||||
return -1;
|
||||
}
|
||||
// Convert to double and normalize
|
||||
src.convertTo(src, CV_64F, 1.0/255.0);
|
||||
|
||||
// Color correction model
|
||||
model = ColorCorrectionModel(src, COLORCHECKER_MACBETH);
|
||||
model.setCcmType(CCM_LINEAR);
|
||||
model.setDistance(DISTANCE_CIE2000);
|
||||
model.setLinearization(LINEARIZATION_GAMMA);
|
||||
model.setLinearizationGamma(2.2);
|
||||
|
||||
Mat ccm = model.compute();
|
||||
cout << "Computed CCM Matrix:\n" << ccm << endl;
|
||||
cout << "Loss: " << model.getLoss() << endl;
|
||||
|
||||
// Save model parameters to YAML file
|
||||
FileStorage fs("ccm_output.yaml", FileStorage::WRITE);
|
||||
model.write(fs);
|
||||
fs.release();
|
||||
cout << "Model parameters saved to ccm_output.yaml" << endl;
|
||||
|
||||
// Set query image for correction
|
||||
if (queryFile.empty()) {
|
||||
cout << "[WARN] No query image provided, applying color correction on input image" << endl;
|
||||
queryImage = originalImage.clone();
|
||||
} else {
|
||||
queryImage = imread(findFile(queryFile));
|
||||
if (queryImage.empty()) {
|
||||
cout << "Error: Unable to read query image." << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Mat calibratedImage;
|
||||
model.correctImage(queryImage, calibratedImage);
|
||||
|
||||
imshow("Original Image", queryImage);
|
||||
imshow("Corrected Image", calibratedImage);
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
//! [tutorial]
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
Mat img;
|
||||
int threshval = 100;
|
||||
|
||||
static void on_trackbar(int, void*)
|
||||
{
|
||||
Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
|
||||
Mat labelImage(img.size(), CV_32S);
|
||||
int nLabels = connectedComponents(bw, labelImage, 8);
|
||||
std::vector<Vec3b> colors(nLabels);
|
||||
colors[0] = Vec3b(0, 0, 0);//background
|
||||
for(int label = 1; label < nLabels; ++label){
|
||||
colors[label] = Vec3b( (rand()&255), (rand()&255), (rand()&255) );
|
||||
}
|
||||
Mat dst(img.size(), CV_8UC3);
|
||||
for(int r = 0; r < dst.rows; ++r){
|
||||
for(int c = 0; c < dst.cols; ++c){
|
||||
int label = labelImage.at<int>(r, c);
|
||||
Vec3b &pixel = dst.at<Vec3b>(r, c);
|
||||
pixel = colors[label];
|
||||
}
|
||||
}
|
||||
|
||||
imshow( "Connected Components", dst );
|
||||
}
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv, "{@image|stuff.jpg|image for converting to a grayscale}");
|
||||
parser.about("\nThis program demonstrates connected components and use of the trackbar\n");
|
||||
parser.printMessage();
|
||||
cout << "\nThe image is converted to grayscale and displayed, another image has a trackbar\n"
|
||||
"that controls thresholding and thereby the extracted contours which are drawn in color\n";
|
||||
|
||||
String inputImage = parser.get<string>(0);
|
||||
img = imread(samples::findFile(inputImage), IMREAD_GRAYSCALE);
|
||||
|
||||
if(img.empty())
|
||||
{
|
||||
cout << "Could not read input image file: " << inputImage << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
imshow( "Image", img );
|
||||
|
||||
namedWindow( "Connected Components", WINDOW_AUTOSIZE);
|
||||
createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar );
|
||||
on_trackbar(threshval, 0);
|
||||
|
||||
waitKey(0);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
*
|
||||
* cvout_sample just demonstrates the serial out capabilities of cv::Mat
|
||||
* That is, cv::Mat M(...); cout << M; Now works.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout
|
||||
<< "\n------------------------------------------------------------------\n"
|
||||
<< " This program shows the serial out capabilities of cv::Mat\n"
|
||||
<< "That is, cv::Mat M(...); cout << M; Now works.\n"
|
||||
<< "Output can be formatted to OpenCV, matlab, python, numpy, csv and \n"
|
||||
<< "C styles Usage:\n"
|
||||
<< argv[0]
|
||||
<< "\n------------------------------------------------------------------\n\n"
|
||||
<< endl;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
Mat I = Mat::eye(4, 4, CV_64F);
|
||||
I.at<double>(1,1) = CV_PI;
|
||||
cout << "I = \n" << I << ";" << endl << endl;
|
||||
|
||||
Mat r = Mat(10, 3, CV_8UC3);
|
||||
randu(r, Scalar::all(0), Scalar::all(255));
|
||||
|
||||
cout << "r (default) = \n" << r << ";" << endl << endl;
|
||||
cout << "r (matlab) = \n" << format(r, Formatter::FMT_MATLAB) << ";" << endl << endl;
|
||||
cout << "r (python) = \n" << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;
|
||||
cout << "r (numpy) = \n" << format(r, Formatter::FMT_NUMPY) << ";" << endl << endl;
|
||||
cout << "r (csv) = \n" << format(r, Formatter::FMT_CSV) << ";" << endl << endl;
|
||||
cout << "r (c) = \n" << format(r, Formatter::FMT_C) << ";" << endl << endl;
|
||||
|
||||
Point2f p(5, 1);
|
||||
cout << "p = " << p << ";" << endl;
|
||||
|
||||
Point3f p3f(2, 6, 7);
|
||||
cout << "p3f = " << p3f << ";" << endl;
|
||||
|
||||
vector<float> v;
|
||||
v.push_back(1);
|
||||
v.push_back(2);
|
||||
v.push_back(3);
|
||||
|
||||
cout << "shortvec = " << Mat(v) << endl;
|
||||
|
||||
vector<Point2f> points(20);
|
||||
for (size_t i = 0; i < points.size(); ++i)
|
||||
points[i] = Point2f((float)(i * 5), (float)(i % 7));
|
||||
|
||||
cout << "points = " << points << ";" << endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <opencv2/geometry/2d.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates iterative construction of\n"
|
||||
"delaunay triangulation and voronoi tessellation.\n"
|
||||
"It draws a random set of points in an image and then delaunay triangulates them.\n"
|
||||
"Usage: \n";
|
||||
cout << argv[0];
|
||||
cout << "\n\nThis program builds the triangulation interactively, you may stop this process by\n"
|
||||
"hitting any key.\n";
|
||||
}
|
||||
|
||||
static void draw_subdiv_point( Mat& img, Point2f fp, Scalar color )
|
||||
{
|
||||
circle( img, fp, 3, color, FILLED, LINE_8, 0 );
|
||||
}
|
||||
|
||||
static void draw_subdiv( Mat& img, Subdiv2D& subdiv, Scalar delaunay_color )
|
||||
{
|
||||
#if 1
|
||||
vector<Vec6f> triangleList;
|
||||
subdiv.getTriangleList(triangleList);
|
||||
vector<Point> pt(3);
|
||||
|
||||
for( size_t i = 0; i < triangleList.size(); i++ )
|
||||
{
|
||||
Vec6f t = triangleList[i];
|
||||
pt[0] = Point(cvRound(t[0]), cvRound(t[1]));
|
||||
pt[1] = Point(cvRound(t[2]), cvRound(t[3]));
|
||||
pt[2] = Point(cvRound(t[4]), cvRound(t[5]));
|
||||
line(img, pt[0], pt[1], delaunay_color, 1, LINE_AA, 0);
|
||||
line(img, pt[1], pt[2], delaunay_color, 1, LINE_AA, 0);
|
||||
line(img, pt[2], pt[0], delaunay_color, 1, LINE_AA, 0);
|
||||
}
|
||||
#else
|
||||
vector<Vec4f> edgeList;
|
||||
subdiv.getEdgeList(edgeList);
|
||||
for( size_t i = 0; i < edgeList.size(); i++ )
|
||||
{
|
||||
Vec4f e = edgeList[i];
|
||||
Point pt0 = Point(cvRound(e[0]), cvRound(e[1]));
|
||||
Point pt1 = Point(cvRound(e[2]), cvRound(e[3]));
|
||||
line(img, pt0, pt1, delaunay_color, 1, LINE_AA, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void locate_point( Mat& img, Subdiv2D& subdiv, Point2f fp, Scalar active_color )
|
||||
{
|
||||
int e0=0, vertex=0;
|
||||
|
||||
subdiv.locate(fp, e0, vertex);
|
||||
|
||||
if( e0 > 0 )
|
||||
{
|
||||
int e = e0;
|
||||
do
|
||||
{
|
||||
Point2f org, dst;
|
||||
if( subdiv.edgeOrg(e, &org) > 0 && subdiv.edgeDst(e, &dst) > 0 )
|
||||
line( img, org, dst, active_color, 3, LINE_AA, 0 );
|
||||
|
||||
e = subdiv.getEdge(e, Subdiv2D::NEXT_AROUND_LEFT);
|
||||
}
|
||||
while( e != e0 );
|
||||
}
|
||||
|
||||
draw_subdiv_point( img, fp, active_color );
|
||||
}
|
||||
|
||||
|
||||
static void paint_voronoi( Mat& img, Subdiv2D& subdiv )
|
||||
{
|
||||
vector<vector<Point2f> > facets;
|
||||
vector<Point2f> centers;
|
||||
subdiv.getVoronoiFacetList(vector<int>(), facets, centers);
|
||||
|
||||
vector<Point> ifacet;
|
||||
vector<vector<Point> > ifacets(1);
|
||||
|
||||
for( size_t i = 0; i < facets.size(); i++ )
|
||||
{
|
||||
ifacet.resize(facets[i].size());
|
||||
for( size_t j = 0; j < facets[i].size(); j++ )
|
||||
ifacet[j] = facets[i][j];
|
||||
|
||||
Scalar color;
|
||||
color[0] = rand() & 255;
|
||||
color[1] = rand() & 255;
|
||||
color[2] = rand() & 255;
|
||||
fillConvexPoly(img, ifacet, color, 8, 0);
|
||||
|
||||
ifacets[0] = ifacet;
|
||||
polylines(img, ifacets, true, Scalar(), 1, LINE_AA, 0);
|
||||
circle(img, centers[i], 3, Scalar(), FILLED, LINE_AA, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Scalar active_facet_color(0, 0, 255), delaunay_color(255,255,255);
|
||||
Rect rect(0, 0, 600, 600);
|
||||
|
||||
Subdiv2D subdiv(rect);
|
||||
Mat img(rect.size(), CV_8UC3);
|
||||
|
||||
img = Scalar::all(0);
|
||||
string win = "Delaunay Demo";
|
||||
imshow(win, img);
|
||||
|
||||
for( int i = 0; i < 200; i++ )
|
||||
{
|
||||
Point2f fp( (float)(rand()%(rect.width-10)+5),
|
||||
(float)(rand()%(rect.height-10)+5));
|
||||
|
||||
locate_point( img, subdiv, fp, active_facet_color );
|
||||
imshow( win, img );
|
||||
|
||||
if( waitKey( 100 ) >= 0 )
|
||||
break;
|
||||
|
||||
subdiv.insert(fp);
|
||||
|
||||
img = Scalar::all(0);
|
||||
draw_subdiv( img, subdiv, delaunay_color );
|
||||
imshow( win, img );
|
||||
|
||||
if( waitKey( 100 ) >= 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
img = Scalar::all(0);
|
||||
paint_voronoi( img, subdiv );
|
||||
imshow( win, img );
|
||||
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
int _brightness = 100;
|
||||
int _contrast = 100;
|
||||
|
||||
Mat image;
|
||||
|
||||
/* brightness/contrast callback function */
|
||||
static void updateBrightnessContrast( int /*arg*/, void* )
|
||||
{
|
||||
int histSize = 64;
|
||||
int brightness = _brightness - 100;
|
||||
int contrast = _contrast - 100;
|
||||
|
||||
/*
|
||||
* The algorithm is by Werner D. Streidt
|
||||
* (http://visca.com/ffactory/archives/5-99/msg00021.html)
|
||||
*/
|
||||
double a, b;
|
||||
if( contrast > 0 )
|
||||
{
|
||||
double delta = 127.*contrast/100;
|
||||
a = 255./(255. - delta*2);
|
||||
b = a*(brightness - delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
double delta = -128.*contrast/100;
|
||||
a = (256.-delta*2)/255.;
|
||||
b = a*brightness + delta;
|
||||
}
|
||||
|
||||
Mat dst, hist;
|
||||
image.convertTo(dst, CV_8U, a, b);
|
||||
imshow("image", dst);
|
||||
|
||||
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, 0);
|
||||
Mat histImage = Mat::ones(200, 320, CV_8U)*255;
|
||||
|
||||
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, CV_32F);
|
||||
|
||||
histImage = Scalar::all(255);
|
||||
int binW = cvRound((double)histImage.cols/histSize);
|
||||
|
||||
for( int i = 0; i < histSize; i++ )
|
||||
rectangle( histImage, Point(i*binW, histImage.rows),
|
||||
Point((i+1)*binW, histImage.rows - cvRound(hist.at<float>(i))),
|
||||
Scalar::all(0), -1, 8, 0 );
|
||||
imshow("histogram", histImage);
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h||}{@image|baboon.jpg|input image file}"
|
||||
};
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("\nThis program demonstrates the use of calcHist() -- histogram creation.\n");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
string inputImage = parser.get<string>(0);
|
||||
|
||||
// Load the source image. HighGUI use.
|
||||
image = imread(samples::findFile(inputImage), IMREAD_GRAYSCALE);
|
||||
if(image.empty())
|
||||
{
|
||||
std::cerr << "Cannot read image file: " << inputImage << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
namedWindow("image", 0);
|
||||
namedWindow("histogram", 0);
|
||||
|
||||
createTrackbar("brightness", "image", &_brightness, 200, updateBrightnessContrast);
|
||||
createTrackbar("contrast", "image", &_contrast, 200, updateBrightnessContrast);
|
||||
|
||||
updateBrightnessContrast(0, 0);
|
||||
waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,537 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
#include "opencv2/core/opengl.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <stdint.h>
|
||||
#ifdef HAVE_OPENGL
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#define NOMINMAX 1
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#if defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <OpenGL/gl.h>
|
||||
#include <OpenGL/glu.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glu.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates how to use MSER to detect extremal regions\n"
|
||||
"Usage:\n"
|
||||
<< argv[0] << " <image1(without parameter a synthetic image is used as default)>\n"
|
||||
"Press esc key when image window is active to change descriptor parameter\n"
|
||||
"Press 2, 8, 4, 6, +, -, or 5 keys in openGL windows to change view or use mouse\n";
|
||||
}
|
||||
|
||||
struct MSERParams
|
||||
{
|
||||
MSERParams(int _delta = 5, int _min_area = 60, int _max_area = 14400,
|
||||
double _max_variation = 0.25, double _min_diversity = .2,
|
||||
int _max_evolution = 200, double _area_threshold = 1.01,
|
||||
double _min_margin = 0.003, int _edge_blur_size = 5)
|
||||
{
|
||||
delta = _delta;
|
||||
minArea = _min_area;
|
||||
maxArea = _max_area;
|
||||
maxVariation = _max_variation;
|
||||
minDiversity = _min_diversity;
|
||||
maxEvolution = _max_evolution;
|
||||
areaThreshold = _area_threshold;
|
||||
minMargin = _min_margin;
|
||||
edgeBlurSize = _edge_blur_size;
|
||||
pass2Only = false;
|
||||
}
|
||||
|
||||
int delta;
|
||||
int minArea;
|
||||
int maxArea;
|
||||
double maxVariation;
|
||||
double minDiversity;
|
||||
bool pass2Only;
|
||||
|
||||
int maxEvolution;
|
||||
double areaThreshold;
|
||||
double minMargin;
|
||||
int edgeBlurSize;
|
||||
};
|
||||
|
||||
static String Legende(const MSERParams &pAct)
|
||||
{
|
||||
ostringstream ss;
|
||||
ss << "Area[" << pAct.minArea << "," << pAct.maxArea << "] ";
|
||||
ss << "del. [" << pAct.delta << "] ";
|
||||
ss << "var. [" << pAct.maxVariation << "] ";
|
||||
ss << "div. [" << (int)pAct.minDiversity << "] ";
|
||||
ss << "pas. [" << (int)pAct.pass2Only << "] ";
|
||||
ss << "RGb->evo. [" << pAct.maxEvolution << "] ";
|
||||
ss << "are. [" << (int)pAct.areaThreshold << "] ";
|
||||
ss << "mar. [" << (int)pAct.minMargin << "] ";
|
||||
ss << "siz. [" << pAct.edgeBlurSize << "]";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
const int win_width = 800;
|
||||
const int win_height = 640;
|
||||
#endif
|
||||
bool rotateEnable=true;
|
||||
bool keyPressed=false;
|
||||
|
||||
Vec4f rotAxis(1,0,1,0);
|
||||
Vec3f zoom(1,0,0);
|
||||
|
||||
float obsX = 0.f;
|
||||
float obsY = 0.f;
|
||||
float obsZ = -10.f;
|
||||
float tx = 0.f;
|
||||
float ty = 0.f;
|
||||
|
||||
float thetaObs = -1.570f;
|
||||
float phiObs = 1.570f;
|
||||
float rObs = 10.f;
|
||||
|
||||
int prevX = -1;
|
||||
int prevY = -1;
|
||||
int prevTheta = -1000;
|
||||
int prevPhi = -1000;
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
struct DrawData
|
||||
{
|
||||
ogl::Arrays arr;
|
||||
ogl::Texture2D tex;
|
||||
ogl::Buffer indices;
|
||||
};
|
||||
|
||||
|
||||
static void draw(void* userdata)
|
||||
{
|
||||
DrawData* data = static_cast<DrawData*>(userdata);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
gluLookAt(obsX, obsY, obsZ, 0, 0, .0, .0, 10.0, 0.0);
|
||||
glTranslatef(tx,ty,0);
|
||||
keyPressed = false;
|
||||
ogl::render(data->arr, data->indices, ogl::TRIANGLES);
|
||||
}
|
||||
|
||||
static void onMouse(int event, int x, int y, int flags, void*)
|
||||
{
|
||||
if (event == EVENT_RBUTTONDOWN)
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
if (event == EVENT_RBUTTONUP)
|
||||
{
|
||||
prevX = -1;
|
||||
prevY = -1;
|
||||
}
|
||||
if (prevX != -1)
|
||||
{
|
||||
tx += float((x - prevX) / 100.0);
|
||||
ty -= float((y - prevY) / 100.0);
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
if (event == EVENT_LBUTTONDOWN)
|
||||
{
|
||||
prevTheta = x;
|
||||
prevPhi = y;
|
||||
}
|
||||
if (event == EVENT_LBUTTONUP)
|
||||
{
|
||||
prevTheta = -1000;
|
||||
prevPhi = -1000;
|
||||
}
|
||||
if (prevTheta != -1000)
|
||||
{
|
||||
if (x - prevTheta<0)
|
||||
{
|
||||
thetaObs += 0.02f;
|
||||
}
|
||||
else if (x - prevTheta>0)
|
||||
{
|
||||
thetaObs -= 0.02f;
|
||||
}
|
||||
if (y - prevPhi<0)
|
||||
{
|
||||
phiObs -= 0.02f;
|
||||
}
|
||||
else if (y - prevPhi>0)
|
||||
{
|
||||
phiObs += 0.02f;
|
||||
}
|
||||
prevTheta = x;
|
||||
prevPhi = y;
|
||||
}
|
||||
if (event==EVENT_MOUSEWHEEL)
|
||||
{
|
||||
if (getMouseWheelDelta(flags)>0)
|
||||
rObs += 0.1f;
|
||||
else
|
||||
rObs -= 0.1f;
|
||||
}
|
||||
float pi = static_cast<float>(CV_PI);
|
||||
if (thetaObs>pi)
|
||||
{
|
||||
thetaObs = -2 * pi + thetaObs;
|
||||
}
|
||||
if (thetaObs<-pi)
|
||||
{
|
||||
thetaObs = 2 * pi + thetaObs;
|
||||
}
|
||||
if (phiObs>pi / 2)
|
||||
{
|
||||
phiObs = pi / 2 - 0.0001f;
|
||||
}
|
||||
if (phiObs<-pi / 2)
|
||||
{
|
||||
phiObs = -pi / 2 + 0.00001f;
|
||||
}
|
||||
if (rObs<0)
|
||||
{
|
||||
rObs = 0;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
static void DrawOpenGLMSER(Mat img, Mat result)
|
||||
{
|
||||
Mat imgGray;
|
||||
if (img.type() != CV_8UC1)
|
||||
cvtColor(img, imgGray, COLOR_BGR2GRAY);
|
||||
else
|
||||
imgGray = img;
|
||||
|
||||
namedWindow("OpenGL", WINDOW_OPENGL);
|
||||
setMouseCallback("OpenGL", onMouse, NULL);
|
||||
|
||||
Mat_<Vec3f> vertex(1, img.cols*img.rows);
|
||||
Mat_<Vec2f> texCoords(1, img.cols*img.rows);
|
||||
for (int i = 0, nbPix = 0; i<img.rows; i++)
|
||||
{
|
||||
for (int j = 0; j<img.cols; j++, nbPix++)
|
||||
{
|
||||
float x = (j) / (float)img.cols;
|
||||
float y = (i) / (float)img.rows;
|
||||
vertex.at< Vec3f >(0, nbPix) = Vec3f(float(2 * (x - 0.5)), float(2 * (0.5 - y)), float(imgGray.at<uchar>(i, j) / 512.0));
|
||||
texCoords.at< Vec2f>(0, nbPix) = Vec2f(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
Mat_<int> indices(1, (img.rows - 1)*(6 * img.cols));
|
||||
for (int i = 1, nbPix = 0; i<img.rows; i++)
|
||||
{
|
||||
for (int j = 1; j<img.cols; j++)
|
||||
{
|
||||
int c = i*img.cols + j;
|
||||
indices.at<int>(0, nbPix++) = c;
|
||||
indices.at<int>(0, nbPix++) = c - 1;
|
||||
indices.at<int>(0, nbPix++) = c - img.cols - 1;
|
||||
indices.at<int>(0, nbPix++) = c - img.cols - 1;
|
||||
indices.at<int>(0, nbPix++) = c - img.cols;
|
||||
indices.at<int>(0, nbPix++) = c;
|
||||
}
|
||||
}
|
||||
|
||||
DrawData *data = new DrawData;
|
||||
|
||||
data->arr.setVertexArray(vertex);
|
||||
data->arr.setTexCoordArray(texCoords);
|
||||
data->indices.copyFrom(indices);
|
||||
data->tex.copyFrom(result);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
gluPerspective(45.0, (double)win_width / win_height, 0.0, 1000.0);
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
glLoadIdentity();
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
data->tex.bind();
|
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
setOpenGlDrawCallback("OpenGL", draw, data);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
updateWindow("OpenGL");
|
||||
char key = (char)waitKey(40);
|
||||
if (key == 27)
|
||||
break;
|
||||
if (key == 0x20)
|
||||
rotateEnable = !rotateEnable;
|
||||
float pi = static_cast<float>(CV_PI);
|
||||
|
||||
switch (key) {
|
||||
case '5':
|
||||
obsX = 0, obsY = 0, obsZ = -10;
|
||||
thetaObs = -pi/2, phiObs = pi/2, rObs = 10;
|
||||
tx=0; ty=0;
|
||||
break;
|
||||
case '4':
|
||||
thetaObs += 0.1f;
|
||||
break;
|
||||
case '6':
|
||||
thetaObs -= 0.1f;
|
||||
break;
|
||||
case '2':
|
||||
phiObs -= 0.1f;
|
||||
break;
|
||||
case '8':
|
||||
phiObs += 0.1f;
|
||||
break;
|
||||
case '+':
|
||||
rObs -= 0.1f;
|
||||
break;
|
||||
case '-':
|
||||
rObs += 0.1f;
|
||||
break;
|
||||
}
|
||||
|
||||
if (thetaObs>pi)
|
||||
{
|
||||
thetaObs = -2 * pi + thetaObs;
|
||||
}
|
||||
if (thetaObs<-pi)
|
||||
thetaObs = 2 * pi + thetaObs;
|
||||
if (phiObs>pi / 2)
|
||||
phiObs = pi / 2 - 0.0001f;
|
||||
if (phiObs<-pi / 2)
|
||||
phiObs = -pi / 2 + 0.00001f;
|
||||
if (rObs<0)
|
||||
rObs = 0;
|
||||
obsX = rObs*cos(thetaObs)*cos(phiObs);
|
||||
obsY = rObs*sin(thetaObs)*cos(phiObs);
|
||||
obsZ = rObs*sin(phiObs);
|
||||
}
|
||||
setOpenGlDrawCallback("OpenGL", 0, 0);
|
||||
destroyAllWindows();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add nested rectangles of different widths and colors to an image
|
||||
static void addNestedRectangles(Mat &img, Point p0, int* width, int *color, int n) {
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
rectangle(img, Rect(p0, Size(width[i], width[i])), Scalar(color[i]), 1);
|
||||
p0 += Point((width[i] - width[i + 1]) / 2, (width[i] - width[i + 1]) / 2);
|
||||
floodFill(img, p0, Scalar(color[i]));
|
||||
}
|
||||
}
|
||||
|
||||
// Add nested circles of different widths and colors to an image
|
||||
static void addNestedCircles(Mat &img, Point p0, int *width, int *color, int n) {
|
||||
for (int i = 0; i<n; i++)
|
||||
{
|
||||
circle(img, p0, width[i] / 2, Scalar(color[i]), 1);
|
||||
floodFill(img, p0, Scalar(color[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static Mat MakeSyntheticImage()
|
||||
{
|
||||
const int fond = 0;
|
||||
|
||||
Mat img(800, 800, CV_8UC1);
|
||||
img = Scalar(fond);
|
||||
|
||||
int width[] = { 390, 380, 300, 290, 280, 270, 260, 250, 210, 190, 150, 100, 80, 70 };
|
||||
|
||||
int color1[] = { 80, 180, 160, 140, 120, 100, 90, 110, 170, 150, 140, 100, 220 };
|
||||
int color2[] = { 81, 181, 161, 141, 121, 101, 91, 111, 171, 151, 141, 101, 221 };
|
||||
int color3[] = { 175, 75, 95, 115, 135, 155, 165, 145, 85, 105, 115, 155, 35 };
|
||||
int color4[] = { 173, 73, 93, 113, 133, 153, 163, 143, 83, 103, 113, 153, 33 };
|
||||
|
||||
addNestedRectangles(img, Point(10, 10), width, color1, 13);
|
||||
addNestedCircles(img, Point(200, 600), width, color2, 13);
|
||||
|
||||
addNestedRectangles(img, Point(410, 10), width, color3, 13);
|
||||
addNestedCircles(img, Point(600, 600), width, color4, 13);
|
||||
|
||||
int histSize = 256;
|
||||
float range[] = { 0, 256 };
|
||||
const float* histRange[] = { range };
|
||||
Mat hist;
|
||||
|
||||
// we compute the histogram
|
||||
calcHist(&img, 1, 0, Mat(), hist, 1, &histSize, histRange, true, false);
|
||||
|
||||
cout << "****************Maximal region************************\n";
|
||||
for (int i = 0; i < hist.rows; i++)
|
||||
{
|
||||
if (hist.at<float>(i, 0)!=0)
|
||||
{
|
||||
cout << "h" << setw(3) << left << i << "\t=\t" << hist.at<float>(i, 0) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
Mat imgOrig, img;
|
||||
Size blurSize(5, 5);
|
||||
cv::CommandLineParser parser(argc, argv, "{ help h | | }{ @input | | }");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
string input = parser.get<string>("@input");
|
||||
if (!input.empty())
|
||||
{
|
||||
imgOrig = imread(samples::findFile(input), IMREAD_GRAYSCALE);
|
||||
blur(imgOrig, img, blurSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
imgOrig = MakeSyntheticImage();
|
||||
img = imgOrig;
|
||||
}
|
||||
|
||||
// Descriptor array MSER
|
||||
vector<String> typeDesc;
|
||||
// Param array for MSER
|
||||
vector<MSERParams> pMSER;
|
||||
|
||||
// Color palette
|
||||
vector<Vec3b> palette;
|
||||
for (int i = 0; i<=numeric_limits<uint16_t>::max(); i++)
|
||||
palette.push_back(Vec3b((uchar)rand(), (uchar)rand(), (uchar)rand()));
|
||||
|
||||
help(argv);
|
||||
|
||||
MSERParams params;
|
||||
|
||||
params.delta = 10;
|
||||
params.minArea = 100;
|
||||
params.maxArea = 5000;
|
||||
params.maxVariation = 2;
|
||||
params.minDiversity = 0;
|
||||
params.pass2Only = true;
|
||||
|
||||
typeDesc.push_back("MSER");
|
||||
pMSER.push_back(params);
|
||||
|
||||
params.pass2Only = false;
|
||||
typeDesc.push_back("MSER");
|
||||
pMSER.push_back(params);
|
||||
|
||||
params.delta = 100;
|
||||
typeDesc.push_back("MSER");
|
||||
pMSER.push_back(params);
|
||||
|
||||
vector<MSERParams>::iterator itMSER = pMSER.begin();
|
||||
Ptr<Feature2D> b;
|
||||
String label;
|
||||
// Descriptor loop
|
||||
vector<String>::iterator itDesc;
|
||||
Mat result(img.rows, img.cols, CV_8UC3);
|
||||
for (itDesc = typeDesc.begin(); itDesc != typeDesc.end(); ++itDesc)
|
||||
{
|
||||
vector<KeyPoint> keyImg1;
|
||||
if (*itDesc == "MSER")
|
||||
{
|
||||
if (img.type() == CV_8UC3)
|
||||
{
|
||||
b = MSER::create(itMSER->delta, itMSER->minArea, itMSER->maxArea, itMSER->maxVariation, itMSER->minDiversity, itMSER->maxEvolution,
|
||||
itMSER->areaThreshold, itMSER->minMargin, itMSER->edgeBlurSize);
|
||||
label = Legende(*itMSER);
|
||||
++itMSER;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = MSER::create(itMSER->delta, itMSER->minArea, itMSER->maxArea, itMSER->maxVariation, itMSER->minDiversity);
|
||||
b.dynamicCast<MSER>()->setPass2Only(itMSER->pass2Only);
|
||||
label = Legende(*itMSER);
|
||||
++itMSER;
|
||||
}
|
||||
}
|
||||
|
||||
if (img.type()==CV_8UC3)
|
||||
{
|
||||
img.copyTo(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
vector<Mat> plan;
|
||||
plan.push_back(img);
|
||||
plan.push_back(img);
|
||||
plan.push_back(img);
|
||||
merge(plan,result);
|
||||
}
|
||||
try
|
||||
{
|
||||
// We can detect regions using detectRegions method
|
||||
vector<KeyPoint> keyImg;
|
||||
vector<Rect> zone;
|
||||
vector<vector <Point> > region;
|
||||
Mat desc;
|
||||
|
||||
if (b.dynamicCast<MSER>().get())
|
||||
{
|
||||
Ptr<MSER> sbd = b.dynamicCast<MSER>();
|
||||
sbd->detectRegions(img, region, zone);
|
||||
//result = Scalar(0, 0, 0);
|
||||
int nbPixelInMSER=0;
|
||||
for (vector<vector <Point> >::iterator itr = region.begin(); itr != region.end(); ++itr)
|
||||
{
|
||||
for (vector <Point>::iterator itp = itr->begin(); itp != itr->end(); ++itp)
|
||||
{
|
||||
// all pixels belonging to region become blue
|
||||
result.at<Vec3b>(itp->y, itp->x) = Vec3b(128, 0, 0);
|
||||
nbPixelInMSER++;
|
||||
}
|
||||
}
|
||||
cout << "Number of MSER region: " << region.size() << "; Number of pixels in all MSER region: " << nbPixelInMSER << "\n";
|
||||
}
|
||||
|
||||
const string winName = *itDesc + label;
|
||||
namedWindow(winName, WINDOW_AUTOSIZE);
|
||||
imshow(winName, result);
|
||||
imshow("Original", img);
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
cout << "Feature: " << *itDesc << "\n";
|
||||
cout << e.msg << endl;
|
||||
}
|
||||
#ifdef HAVE_OPENGL
|
||||
DrawOpenGLMSER(img, result);
|
||||
#endif
|
||||
waitKey();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// This example provides a digital recognition based on LeNet-5 and connected component analysis.
|
||||
// It makes it possible for OpenCV beginner to run dnn models in real time using only CPU.
|
||||
// It can read pictures from the camera in real time to make predictions, and display the recognized digits as overlays on top of the original digits.
|
||||
//
|
||||
// In order to achieve a better display effect, please write the number on white paper and occupy the entire camera.
|
||||
//
|
||||
// You can follow the following guide to train LeNet-5 by yourself using the MNIST dataset.
|
||||
// https://github.com/intel/caffe/blob/a3d5b022fe026e9092fc7abc7654b1162ab9940d/examples/mnist/readme.md
|
||||
//
|
||||
// You can also download already trained model directly.
|
||||
// https://github.com/zihaomu/opencv_digit_text_recognition_demo/tree/master/src
|
||||
|
||||
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/dnn.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace cv;
|
||||
using namespace cv::dnn;
|
||||
|
||||
const char *keys =
|
||||
"{ help h | | Print help message. }"
|
||||
"{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera.}"
|
||||
"{ device | 0 | camera device number. }"
|
||||
"{ modelBin | | Path to a binary .caffemodel file contains trained network.}"
|
||||
"{ modelTxt | | Path to a .prototxt file contains the model definition of trained network.}"
|
||||
"{ width | 640 | Set the width of the camera }"
|
||||
"{ height | 480 | Set the height of the camera }"
|
||||
"{ thr | 0.7 | Confidence threshold. }";
|
||||
|
||||
// Find best class for the blob (i.e. class with maximal probability)
|
||||
static void getMaxClass(const Mat &probBlob, int &classId, double &classProb);
|
||||
|
||||
void predictor(Net net, const Mat &roi, int &class_id, double &probability);
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// Parse command line arguments.
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
|
||||
if (argc == 1 || parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vWidth = parser.get<int>("width");
|
||||
int vHeight = parser.get<int>("height");
|
||||
float confThreshold = parser.get<float>("thr");
|
||||
std::string modelTxt = parser.get<String>("modelTxt");
|
||||
std::string modelBin = parser.get<String>("modelBin");
|
||||
|
||||
Net net;
|
||||
try
|
||||
{
|
||||
net = readNet(modelTxt, modelBin);
|
||||
}
|
||||
catch (cv::Exception &ee)
|
||||
{
|
||||
std::cerr << "Exception: " << ee.what() << std::endl;
|
||||
std::cout << "Can't load the network by using the flowing files:" << std::endl;
|
||||
std::cout << "modelTxt: " << modelTxt << std::endl;
|
||||
std::cout << "modelBin: " << modelBin << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string resultWinName = "Please write the number on white paper and occupy the entire camera.";
|
||||
const std::string preWinName = "Preprocessing";
|
||||
|
||||
namedWindow(preWinName, WINDOW_AUTOSIZE);
|
||||
namedWindow(resultWinName, WINDOW_AUTOSIZE);
|
||||
|
||||
Mat labels, stats, centroids;
|
||||
Point position;
|
||||
|
||||
Rect getRectangle;
|
||||
bool ifDrawingBox = false;
|
||||
|
||||
int classId = 0;
|
||||
double probability = 0;
|
||||
|
||||
Rect basicRect = Rect(0, 0, vWidth, vHeight);
|
||||
Mat rawImage;
|
||||
|
||||
double fps = 0;
|
||||
|
||||
// Open a video file or an image file or a camera stream.
|
||||
VideoCapture cap;
|
||||
if (parser.has("input"))
|
||||
cap.open(parser.get<String>("input"));
|
||||
else
|
||||
cap.open(parser.get<int>("device"));
|
||||
|
||||
TickMeter tm;
|
||||
|
||||
while (waitKey(1) < 0)
|
||||
{
|
||||
cap >> rawImage;
|
||||
if (rawImage.empty())
|
||||
{
|
||||
waitKey();
|
||||
break;
|
||||
}
|
||||
|
||||
tm.reset();
|
||||
tm.start();
|
||||
|
||||
Mat image = rawImage.clone();
|
||||
// Image preprocessing
|
||||
cvtColor(image, image, COLOR_BGR2GRAY);
|
||||
GaussianBlur(image, image, Size(3, 3), 2, 2);
|
||||
adaptiveThreshold(image, image, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 25, 10);
|
||||
bitwise_not(image, image);
|
||||
|
||||
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1,-1));
|
||||
dilate(image, image, element, Point(-1,-1), 1);
|
||||
// Find connected component
|
||||
int nccomps = cv::connectedComponentsWithStats(image, labels, stats, centroids);
|
||||
|
||||
for (int i = 1; i < nccomps; i++)
|
||||
{
|
||||
ifDrawingBox = false;
|
||||
|
||||
// Extend the bounding box of connected component for easier recognition
|
||||
if (stats.at<int>(i - 1, CC_STAT_AREA) > 80 && stats.at<int>(i - 1, CC_STAT_AREA) < 3000)
|
||||
{
|
||||
ifDrawingBox = true;
|
||||
int left = stats.at<int>(i - 1, CC_STAT_HEIGHT) / 4;
|
||||
getRectangle = Rect(stats.at<int>(i - 1, CC_STAT_LEFT) - left, stats.at<int>(i - 1, CC_STAT_TOP) - left, stats.at<int>(i - 1, CC_STAT_WIDTH) + 2 * left, stats.at<int>(i - 1, CC_STAT_HEIGHT) + 2 * left);
|
||||
getRectangle &= basicRect;
|
||||
}
|
||||
|
||||
if (ifDrawingBox && !getRectangle.empty())
|
||||
{
|
||||
Mat roi = image(getRectangle);
|
||||
predictor(net, roi, classId, probability);
|
||||
|
||||
if (probability < confThreshold)
|
||||
continue;
|
||||
|
||||
rectangle(rawImage, getRectangle, Scalar(128, 255, 128), 2);
|
||||
|
||||
position = Point(getRectangle.br().x - 7, getRectangle.br().y + 25);
|
||||
putText(rawImage, std::to_string(classId), position, 3, 1.0, Scalar(128, 128, 255), 2);
|
||||
}
|
||||
}
|
||||
|
||||
tm.stop();
|
||||
fps = 1 / tm.getTimeSec();
|
||||
std::string fpsString = format("Inference FPS: %.2f.", fps);
|
||||
putText(rawImage, fpsString, Point(5, 20), FONT_HERSHEY_SIMPLEX, 0.6, Scalar(128, 255, 128));
|
||||
|
||||
imshow(resultWinName, rawImage);
|
||||
imshow(preWinName, image);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void getMaxClass(const Mat &probBlob, int &classId, double &classProb)
|
||||
{
|
||||
Mat probMat = probBlob.reshape(1, 1);
|
||||
Point classNumber;
|
||||
minMaxLoc(probMat, NULL, &classProb, NULL, &classNumber);
|
||||
classId = classNumber.x;
|
||||
}
|
||||
|
||||
void predictor(Net net, const Mat &roi, int &classId, double &probability)
|
||||
{
|
||||
Mat pred;
|
||||
// Convert Mat to batch of images
|
||||
Mat inputBlob = dnn::blobFromImage(roi, 1.0, Size(28, 28));
|
||||
// Set the network input
|
||||
net.setInput(inputBlob);
|
||||
// Compute output
|
||||
pred = net.forward();
|
||||
getMaxClass(pred, classId, probability);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void drawRandomLines(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomRectangles(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomEllipses(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomPolylines(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomFilledPolygons(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomCircles(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawRandomText(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
void drawAll(Mat& image, RNG& rng, int width, int height, int lineType);
|
||||
|
||||
// Display help message to the user
|
||||
static void help() {
|
||||
cout << "\nThis program demonstrates OpenCV drawing and text output functions by drawing random shapes and texts\n"
|
||||
"You can change the drawing mode by pressing keys while the program is running:\n"
|
||||
" 'l' : lines\n"
|
||||
" 'r' : rectangles\n"
|
||||
" 'e' : ellipses\n"
|
||||
" 'p' : polylines\n"
|
||||
" 'f' : filled polygons\n"
|
||||
" 'c' : circles\n"
|
||||
" 't' : text\n"
|
||||
" 'a' : all shapes\n"
|
||||
"Press 'ESC' to exit the program.\n";
|
||||
}
|
||||
|
||||
static Scalar randomColor(RNG& rng) {
|
||||
int icolor = (unsigned)rng;
|
||||
return Scalar(icolor&255, (icolor>>8)&255, (icolor>>16)&255);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 2) {
|
||||
string arg = argv[1];
|
||||
if (arg == "-h" || arg == "--help") {
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
help();
|
||||
|
||||
// Initialize random number generator, image dimensions, and type
|
||||
RNG rng(0xFFFFFFFF);
|
||||
int width = 1000, height = 700;
|
||||
int lineType = LINE_AA;
|
||||
Mat image = Mat::zeros(height, width, CV_8UC3);
|
||||
|
||||
// Draw all shapes by default
|
||||
drawAll(image, rng, width, height, lineType);
|
||||
imshow("Drawing Demo", image);
|
||||
int key = waitKey(0);
|
||||
|
||||
while (key != 27) { // 27 is the ASCII code for 'ESC'
|
||||
switch (key) {
|
||||
case 'l':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomLines(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'r':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomRectangles(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'e':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomEllipses(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'p':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomPolylines(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'f':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomFilledPolygons(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'c':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomCircles(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 't':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawRandomText(image, rng, width, height, lineType);
|
||||
break;
|
||||
case 'a':
|
||||
image = Mat::zeros(height, width, CV_8UC3);
|
||||
drawAll(image, rng, width, height, lineType);
|
||||
break;
|
||||
}
|
||||
imshow("Drawing Demo", image);
|
||||
key = waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function implementations to draw each type of shape
|
||||
// Each function follows a similar pattern
|
||||
void drawRandomLines(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
Point pt1, pt2;
|
||||
// Draw 100 random lines
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
pt1.x = rng.uniform(0, width);
|
||||
pt1.y = rng.uniform(0, height);
|
||||
pt2.x = rng.uniform(0, width);
|
||||
pt2.y = rng.uniform(0, height);
|
||||
// Draw a line between the random points with a random color and thickness
|
||||
line(image, pt1, pt2, randomColor(rng), rng.uniform(1, 10), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomRectangles(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
Point pt1, pt2;
|
||||
// Generate random corner points
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
pt1.x = rng.uniform(0, width);
|
||||
pt1.y = rng.uniform(0, height);
|
||||
pt2.x = rng.uniform(0, width);
|
||||
pt2.y = rng.uniform(0, height);
|
||||
// Draw a rectangle with random colors and thickness (or filled if thickness is -1). MAX changes any negative number by rng.uniform to -1
|
||||
rectangle(image, pt1, pt2, randomColor(rng), MAX(rng.uniform(-1, 10), -1), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomEllipses(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
Point center;
|
||||
Size axes;
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
center.x = rng.uniform(0, width);
|
||||
center.y = rng.uniform(0, height);
|
||||
axes.width = rng.uniform(0, 200);
|
||||
axes.height = rng.uniform(0, 200);
|
||||
double angle = rng.uniform(0.0, 360.0);
|
||||
// Draw an ellipse with a random color and thickness
|
||||
ellipse(image, center, axes, angle, 0, 360, randomColor(rng), rng.uniform(-1, 9), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomPolylines(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Point points[1][5];
|
||||
points[0][0] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][1] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][2] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][3] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][4] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
|
||||
const Point* ppt[1] = {points[0]};
|
||||
int npt[] = {5};
|
||||
|
||||
polylines(image, ppt, npt, 1, true, randomColor(rng), rng.uniform(1, 10), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomFilledPolygons(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
Point points[1][5];
|
||||
// Generate 5 random points for each polyline
|
||||
points[0][0] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][1] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][2] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][3] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
points[0][4] = Point(rng.uniform(0, width), rng.uniform(0, height));
|
||||
|
||||
const Point* ppt[1] = {points[0]};
|
||||
int npt[] = {5};
|
||||
// Draw the polyline with a random color and thickness
|
||||
fillPoly(image, ppt, npt, 1, randomColor(rng), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomCircles(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
Point center;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
// Generate a random center and radius
|
||||
center.x = rng.uniform(0, width);
|
||||
center.y = rng.uniform(0, height);
|
||||
circle(image, center, rng.uniform(0, 300), randomColor(rng), rng.uniform(-1, 9), lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawRandomText(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
Point org;
|
||||
for (int i = 0; i < 50; ++i) {
|
||||
// Generate a random position for the text
|
||||
org.x = rng.uniform(0, width);
|
||||
org.y = rng.uniform(0, height);
|
||||
// Randomize font face, scale, and thickness
|
||||
int fontFace = rng.uniform(0, 3);
|
||||
double fontScale = rng.uniform(0.5, 2.0);
|
||||
int thickness = rng.uniform(1, 3);
|
||||
putText(image, "This is OpenCV drawing demo sample", org, fontFace, fontScale, randomColor(rng), thickness, lineType);
|
||||
}
|
||||
}
|
||||
|
||||
void drawAll(Mat& image, RNG& rng, int width, int height, int lineType) {
|
||||
drawRandomLines(image, rng, width, height, lineType);
|
||||
drawRandomRectangles(image, rng, width, height, lineType);
|
||||
drawRandomEllipses(image, rng, width, height, lineType);
|
||||
drawRandomPolylines(image, rng, width, height, lineType);
|
||||
drawRandomFilledPolygons(image, rng, width, height, lineType);
|
||||
drawRandomCircles(image, rng, width, height, lineType);
|
||||
drawRandomText(image, rng, width, height, lineType);
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace cv;
|
||||
static double getError2EpipLines (const Mat &F, const Mat &pts1, const Mat &pts2, const Mat &mask) {
|
||||
Mat points1, points2;
|
||||
vconcat(pts1, Mat::ones(1, pts1.cols, pts1.type()), points1);
|
||||
vconcat(pts2, Mat::ones(1, pts2.cols, pts2.type()), points2);
|
||||
|
||||
double mean_error = 0;
|
||||
for (int pt = 0; pt < (int) mask.total(); pt++)
|
||||
if (mask.at<uchar>(pt)) {
|
||||
const Mat l2 = F * points1.col(pt);
|
||||
const Mat l1 = F.t() * points2.col(pt);
|
||||
mean_error += (fabs(points1.col(pt).dot(l1)) / sqrt(pow(l1.at<double>(0), 2) + pow(l1.at<double>(1), 2)) +
|
||||
fabs(points2.col(pt).dot(l2) / sqrt(pow(l2.at<double>(0), 2) + pow(l2.at<double>(1), 2)))) / 2;
|
||||
}
|
||||
return mean_error / mask.total();
|
||||
}
|
||||
static int sgn(double val) { return (0 < val) - (val < 0); }
|
||||
|
||||
/*
|
||||
* @points3d - vector of Point3 or Mat of size Nx3
|
||||
* @planes - vector of found planes
|
||||
* @labels - vector of size point3d. Every point which has non-zero label is classified to this plane.
|
||||
*/
|
||||
static void getPlanes (InputArray points3d_, std::vector<int> &labels, std::vector<Vec4d> &planes, int desired_num_planes, double thr_, double conf_, int max_iters_) {
|
||||
Mat points3d = points3d_.getMat();
|
||||
points3d.convertTo(points3d, CV_64F); // convert points to have double precision
|
||||
if (points3d_.isVector())
|
||||
points3d = Mat((int)points3d.total(), 3, CV_64F, points3d.data);
|
||||
else {
|
||||
if (points3d.type() != CV_64F)
|
||||
points3d = points3d.reshape(1, (int)points3d.total()); // convert point to have 1 channel
|
||||
if (points3d.rows < points3d.cols)
|
||||
transpose(points3d, points3d); // transpose so points will be in rows
|
||||
CV_CheckEQ(points3d.cols, 3, "Invalid dimension of point");
|
||||
}
|
||||
|
||||
/*
|
||||
* 3D plane fitting with RANSAC
|
||||
* @best_model contains coefficients [a b c d] s.t. ax + by + cz = d
|
||||
*
|
||||
*/
|
||||
auto plane_ransac = [] (const Mat &pts, double thr, double conf, int max_iters, Vec4d &best_model, std::vector<bool> &inliers) {
|
||||
const int pts_size = pts.rows, max_lo_inliers = 15, max_lo_iters = 10;
|
||||
int best_inls = 0;
|
||||
if (pts_size < 3) return false;
|
||||
RNG rng;
|
||||
const auto * const points = (double *) pts.data;
|
||||
std::vector<int> min_sample(3);
|
||||
inliers = std::vector<bool>(pts_size);
|
||||
const double log_conf = log(1-conf);
|
||||
Vec4d model, lo_model;
|
||||
std::vector<int> random_pool (pts_size);
|
||||
for (int p = 0; p < pts_size; p++)
|
||||
random_pool[p] = p;
|
||||
|
||||
// estimate plane coefficients using covariance matrix
|
||||
auto estimate = [&] (const std::vector<int> &sample, Vec4d &model_) {
|
||||
// https://www.ilikebigbits.com/2017_09_25_plane_from_points_2.html
|
||||
const int n = static_cast<int>(sample.size());
|
||||
if (n < 3) return false;
|
||||
double sum_x = 0, sum_y = 0, sum_z = 0;
|
||||
for (int s : sample) {
|
||||
sum_x += points[3*s ];
|
||||
sum_y += points[3*s+1];
|
||||
sum_z += points[3*s+2];
|
||||
}
|
||||
const double c_x = sum_x / n, c_y = sum_y / n, c_z = sum_z / n;
|
||||
double xx = 0, yy = 0, zz = 0, xy = 0, xz = 0, yz = 0;
|
||||
for (int s : sample) {
|
||||
const double x_ = points[3*s] - c_x, y_ = points[3*s+1] - c_y, z_ = points[3*s+2] - c_z;
|
||||
xx += x_*x_; yy += y_*y_; zz += z_*z_; xy += x_*y_; yz += y_*z_; xz += x_*z_;
|
||||
}
|
||||
xx /= n; yy /= n; zz /= n; xy /= n; yz /= n; xz /= n;
|
||||
Vec3d weighted_normal(0,0,0);
|
||||
const double det_x = yy*zz - yz*yz, det_y = xx*zz - xz*xz, det_z = xx*yy - xy*xy;
|
||||
Vec3d axis_x (det_x, xz*xz-xy*zz, xy*yz-xz*yy);
|
||||
Vec3d axis_y (xz*yz-xy*zz, det_y, xy*xz-yz*xx);
|
||||
Vec3d axis_z (xy*yz-xz*yy, xy*xz-yz*xx, det_z);
|
||||
weighted_normal += axis_x * det_x * det_x;
|
||||
weighted_normal += sgn(weighted_normal.dot(axis_y)) * axis_y * det_y * det_y;
|
||||
weighted_normal += sgn(weighted_normal.dot(axis_z)) * axis_z * det_z * det_z;
|
||||
weighted_normal /= norm(weighted_normal);
|
||||
if (std::isinf(weighted_normal(0)) ||
|
||||
std::isinf(weighted_normal(1)) ||
|
||||
std::isinf(weighted_normal(2))) return false;
|
||||
// find plane model from normal and centroid
|
||||
model_ = Vec4d(weighted_normal(0), weighted_normal(1), weighted_normal(2),
|
||||
weighted_normal.dot(Vec3d(c_x, c_y, c_z)));
|
||||
return true;
|
||||
};
|
||||
|
||||
// calculate number of inliers
|
||||
auto getInliers = [&] (const Vec4d &model_) {
|
||||
const double a = model_(0), b = model_(1), c = model_(2), d = model_(3);
|
||||
int num_inliers = 0;
|
||||
std::fill(inliers.begin(), inliers.end(), false);
|
||||
for (int p = 0; p < pts_size; p++) {
|
||||
inliers[p] = fabs(a * points[3*p] + b * points[3*p+1] + c * points[3*p+2] - d) < thr;
|
||||
if (inliers[p]) num_inliers++;
|
||||
if (num_inliers + pts_size - p < best_inls) break;
|
||||
}
|
||||
return num_inliers;
|
||||
};
|
||||
// main RANSAC loop
|
||||
for (int iters = 0; iters < max_iters; iters++) {
|
||||
// find minimal sample: 3 points
|
||||
min_sample[0] = rng.uniform(0, pts_size);
|
||||
min_sample[1] = rng.uniform(0, pts_size);
|
||||
min_sample[2] = rng.uniform(0, pts_size);
|
||||
if (! estimate(min_sample, model))
|
||||
continue;
|
||||
int num_inliers = getInliers(model);
|
||||
if (num_inliers > best_inls) {
|
||||
// store so-far-the-best
|
||||
std::vector<bool> best_inliers = inliers;
|
||||
// do Local Optimization
|
||||
for (int lo_iter = 0; lo_iter < max_lo_iters; lo_iter++) {
|
||||
std::vector<int> inliers_idx; inliers_idx.reserve(max_lo_inliers);
|
||||
randShuffle(random_pool);
|
||||
for (int p : random_pool) {
|
||||
if (best_inliers[p]) {
|
||||
inliers_idx.emplace_back(p);
|
||||
if ((int)inliers_idx.size() >= max_lo_inliers)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! estimate(inliers_idx, lo_model))
|
||||
continue;
|
||||
int lo_inls = getInliers(lo_model);
|
||||
if (best_inls < lo_inls) {
|
||||
best_model = lo_model;
|
||||
best_inls = lo_inls;
|
||||
best_inliers = inliers;
|
||||
}
|
||||
}
|
||||
if (best_inls < num_inliers) {
|
||||
best_model = model;
|
||||
best_inls = num_inliers;
|
||||
}
|
||||
// update max iters
|
||||
// because points are quite noisy we need more iterations
|
||||
const double max_hyp = 3 * log_conf / log(1 - pow(double(best_inls) / pts_size, 3));
|
||||
if (! std::isinf(max_hyp) && max_hyp < max_iters)
|
||||
max_iters = static_cast<int>(max_hyp);
|
||||
}
|
||||
}
|
||||
getInliers(best_model);
|
||||
return best_inls != 0;
|
||||
};
|
||||
|
||||
labels = std::vector<int>(points3d.rows, 0);
|
||||
Mat pts3d_plane_fit = points3d.clone();
|
||||
// keep array of indices of points corresponding to original points3d
|
||||
std::vector<int> to_orig_pts_arr(pts3d_plane_fit.rows);
|
||||
for (int i = 0; i < (int) to_orig_pts_arr.size(); i++)
|
||||
to_orig_pts_arr[i] = i;
|
||||
for (int num_planes = 1; num_planes <= desired_num_planes; num_planes++) {
|
||||
Vec4d model;
|
||||
std::vector<bool> inl;
|
||||
if (!plane_ransac(pts3d_plane_fit, thr_, conf_, max_iters_, model, inl))
|
||||
break;
|
||||
planes.emplace_back(model);
|
||||
|
||||
const int pts3d_size = pts3d_plane_fit.rows;
|
||||
pts3d_plane_fit = Mat();
|
||||
pts3d_plane_fit.reserve(points3d.rows);
|
||||
|
||||
int cnt = 0;
|
||||
for (int p = 0; p < pts3d_size; p++) {
|
||||
if (! inl[p]) {
|
||||
// if point is not inlier to found plane - add it to next run
|
||||
to_orig_pts_arr[cnt] = to_orig_pts_arr[p];
|
||||
pts3d_plane_fit.push_back(points3d.row(to_orig_pts_arr[cnt]));
|
||||
cnt++;
|
||||
} else labels[to_orig_pts_arr[p]] = num_planes; // otherwise label this point
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int args, char** argv) {
|
||||
std::string data_file, image_dir;
|
||||
if (args < 3) {
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Path to data file and directory to image files are missing!\nData file must have"
|
||||
" format:\n--------------\n image_name_1\nimage_name_2\nk11 k12 k13\n0 k22 k23\n"
|
||||
"0 0 1\n--------------\nIf image_name_{1,2} are not in the same directory as "
|
||||
"the data file then add argument with directory to image files.\nFor example: "
|
||||
"./essential_mat_reconstr essential_mat_data.txt ./");
|
||||
} else {
|
||||
data_file = argv[1];
|
||||
image_dir = argv[2];
|
||||
}
|
||||
std::ifstream file(data_file, std::ios_base::in);
|
||||
CV_CheckEQ((int)file.is_open(), 1, "Data file is not found!");
|
||||
std::string filename1, filename2;
|
||||
std::getline(file, filename1);
|
||||
std::getline(file, filename2);
|
||||
Mat image1 = imread(image_dir+filename1);
|
||||
Mat image2 = imread(image_dir+filename2);
|
||||
CV_CheckEQ((int)image1.empty(), 0, "Image 1 is not found!");
|
||||
CV_CheckEQ((int)image2.empty(), 0, "Image 2 is not found!");
|
||||
|
||||
// read calibration
|
||||
Matx33d K;
|
||||
for (int i = 0; i < 3; i++)
|
||||
for (int j = 0; j < 3; j++)
|
||||
file >> K(i,j);
|
||||
file.close();
|
||||
|
||||
Mat descriptors1, descriptors2;
|
||||
std::vector<KeyPoint> keypoints1, keypoints2;
|
||||
|
||||
// detect points with SIFT
|
||||
Ptr<SIFT> detector = SIFT::create();
|
||||
detector->detect(image1, keypoints1);
|
||||
detector->detect(image2, keypoints2);
|
||||
detector->compute(image1, keypoints1, descriptors1);
|
||||
detector->compute(image2, keypoints2, descriptors2);
|
||||
|
||||
FlannBasedMatcher matcher(makePtr<flann::KDTreeIndexParams>(5), makePtr<flann::SearchParams>(32));
|
||||
|
||||
// get k=2 best match that we can apply ratio test explained by D.Lowe
|
||||
std::vector<std::vector<DMatch>> matches_vector;
|
||||
matcher.knnMatch(descriptors1, descriptors2, matches_vector, 2);
|
||||
|
||||
// filter keypoints with Lowe ratio test
|
||||
std::vector<Point2d> pts1, pts2;
|
||||
pts1.reserve(matches_vector.size()); pts2.reserve(matches_vector.size());
|
||||
for (const auto &m : matches_vector) {
|
||||
// compare best and second match using Lowe ratio test
|
||||
if (m[0].distance / m[1].distance < 0.75) {
|
||||
pts1.emplace_back(keypoints1[m[0].queryIdx].pt);
|
||||
pts2.emplace_back(keypoints2[m[0].trainIdx].pt);
|
||||
}
|
||||
}
|
||||
|
||||
Mat inliers;
|
||||
const int pts_size = (int) pts1.size();
|
||||
const auto begin_time = std::chrono::steady_clock::now();
|
||||
// fine essential matrix
|
||||
const Mat E = findEssentialMat(pts1, pts2, Mat(K), RANSAC, 0.99, 1.0, 1000/*maxIters*/, inliers);
|
||||
std::cout << "RANSAC essential matrix time " << std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - begin_time).count() <<
|
||||
"mcs.\nNumber of inliers " << countNonZero(inliers) << "\n";
|
||||
|
||||
Mat points1 = Mat((int)pts1.size(), 2, CV_64F, pts1.data());
|
||||
Mat points2 = Mat((int)pts2.size(), 2, CV_64F, pts2.data());
|
||||
points1 = points1.t(); points2 = points2.t();
|
||||
|
||||
std::cout << "Mean error to epipolar lines " <<
|
||||
getError2EpipLines(K.inv().t() * E * K.inv(), points1, points2, inliers) << "\n";
|
||||
|
||||
// decompose essential into rotation and translation
|
||||
Mat R1, R2, t;
|
||||
decomposeEssentialMat(E, R1, R2, t);
|
||||
|
||||
// Create two relative pose
|
||||
// P1 = K [ I | 0 ]
|
||||
// P2 = K [R{1,2} | {+-}t]
|
||||
Mat P1;
|
||||
hconcat(K, Vec3d::zeros(), P1);
|
||||
std::vector<Mat> P2s(4);
|
||||
hconcat(K * R1, K * t, P2s[0]);
|
||||
hconcat(K * R1, -K * t, P2s[1]);
|
||||
hconcat(K * R2, K * t, P2s[2]);
|
||||
hconcat(K * R2, -K * t, P2s[3]);
|
||||
|
||||
// find objects point by enumerating over 4 different projection matrices of second camera
|
||||
// vector to keep object points
|
||||
std::vector<std::vector<Vec3d>> obj_pts_per_cam(4);
|
||||
// vector to keep indices of image points corresponding to object points
|
||||
std::vector<std::vector<int>> img_idxs_per_cam(4);
|
||||
int cam_idx = 0, best_cam_idx = 0, max_obj_pts = 0;
|
||||
for (const auto &P2 : P2s) {
|
||||
obj_pts_per_cam[cam_idx].reserve(pts_size);
|
||||
img_idxs_per_cam[cam_idx].reserve(pts_size);
|
||||
for (int i = 0; i < pts_size; i++) {
|
||||
// process only inliers
|
||||
if (! inliers.at<uchar>(i))
|
||||
continue;
|
||||
|
||||
Vec4d obj_pt;
|
||||
// find object point using triangulation
|
||||
triangulatePoints(P1, P2, points1.col(i), points2.col(i), obj_pt);
|
||||
obj_pt /= obj_pt(3); // normalize 4d point
|
||||
if (obj_pt(2) > 0) { // check if projected point has positive depth
|
||||
obj_pts_per_cam[cam_idx].emplace_back(Vec3d(obj_pt(0), obj_pt(1), obj_pt(2)));
|
||||
img_idxs_per_cam[cam_idx].emplace_back(i);
|
||||
}
|
||||
}
|
||||
if (max_obj_pts < (int) obj_pts_per_cam[cam_idx].size()) {
|
||||
max_obj_pts = (int) obj_pts_per_cam[cam_idx].size();
|
||||
best_cam_idx = cam_idx;
|
||||
}
|
||||
cam_idx++;
|
||||
}
|
||||
|
||||
std::cout << "Number of object points " << max_obj_pts << "\n";
|
||||
|
||||
const int circle_sz = 7;
|
||||
// draw image points that are inliers on two images
|
||||
std::vector<int> labels;
|
||||
std::vector<Vec4d> planes;
|
||||
getPlanes (obj_pts_per_cam[best_cam_idx], labels, planes, 4, 0.002, 0.99, 10000);
|
||||
const int num_found_planes = (int) planes.size();
|
||||
RNG rng;
|
||||
std::vector<Scalar> plane_colors (num_found_planes);
|
||||
for (int pl = 0; pl < num_found_planes; pl++)
|
||||
plane_colors[pl] = Scalar (rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256));
|
||||
for (int obj_pt = 0; obj_pt < max_obj_pts; obj_pt++) {
|
||||
const int pt = img_idxs_per_cam[best_cam_idx][obj_pt];
|
||||
if (labels[obj_pt] > 0) { // plot plane points
|
||||
circle (image1, pts1[pt], circle_sz, plane_colors[labels[obj_pt]-1], -1);
|
||||
circle (image2, pts2[pt], circle_sz, plane_colors[labels[obj_pt]-1], -1);
|
||||
} else { // plot inliers
|
||||
circle (image1, pts1[pt], circle_sz, Scalar(0,0,0), -1);
|
||||
circle (image2, pts2[pt], circle_sz, Scalar(0,0,0), -1);
|
||||
}
|
||||
}
|
||||
|
||||
// concatenate two images
|
||||
hconcat(image1, image2, image1);
|
||||
const int new_img_size = 1200 * 800; // for example
|
||||
// resize with the same aspect ratio
|
||||
resize(image1, image1, Size((int)sqrt ((double) image1.cols * new_img_size / image1.rows),
|
||||
(int)sqrt ((double) image1.rows * new_img_size / image1.cols)));
|
||||
imshow("image 1-2", image1);
|
||||
imwrite("planes.png", image1);
|
||||
waitKey(0);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
# cmake needs this line
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# Define project name
|
||||
project(opencv_example_project)
|
||||
|
||||
# Find OpenCV, you may need to set OpenCV_DIR variable
|
||||
# to the absolute path to the directory containing OpenCVConfig.cmake file
|
||||
# via the command line or GUI
|
||||
find_package(OpenCV REQUIRED)
|
||||
|
||||
# If the package has been found, several variables will
|
||||
# be set, you can find the full list with descriptions
|
||||
# in the OpenCVConfig.cmake file.
|
||||
# Print some message showing some of them
|
||||
message(STATUS "OpenCV library status:")
|
||||
message(STATUS " config: ${OpenCV_DIR}")
|
||||
message(STATUS " version: ${OpenCV_VERSION}")
|
||||
message(STATUS " libraries: ${OpenCV_LIBS}")
|
||||
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
|
||||
|
||||
# Declare the executable target built from your sources
|
||||
add_executable(opencv_example example.cpp)
|
||||
|
||||
# Link your application with OpenCV libraries
|
||||
target_link_libraries(opencv_example PRIVATE ${OpenCV_LIBS})
|
||||
@@ -0,0 +1,12 @@
|
||||
CXX ?= g++
|
||||
|
||||
CXXFLAGS += -c -Wall $(shell pkg-config --cflags opencv)
|
||||
LDFLAGS += $(shell pkg-config --libs --static opencv)
|
||||
|
||||
all: opencv_example
|
||||
|
||||
opencv_example: example.o; $(CXX) $< -o $@ $(LDFLAGS)
|
||||
|
||||
%.o: %.cpp; $(CXX) $< -o $@ $(CXXFLAGS)
|
||||
|
||||
clean: ; rm -f example.o opencv_example
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void drawText(Mat & image);
|
||||
|
||||
int main()
|
||||
{
|
||||
cout << "Built with OpenCV " << CV_VERSION << endl;
|
||||
Mat image;
|
||||
VideoCapture capture;
|
||||
capture.open(0);
|
||||
if(capture.isOpened())
|
||||
{
|
||||
cout << "Capture is opened" << endl;
|
||||
for(;;)
|
||||
{
|
||||
capture >> image;
|
||||
if(image.empty())
|
||||
break;
|
||||
drawText(image);
|
||||
imshow("Sample", image);
|
||||
if(waitKey(10) >= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "No capture" << endl;
|
||||
image = Mat::zeros(480, 640, CV_8UC1);
|
||||
drawText(image);
|
||||
imshow("Sample", image);
|
||||
waitKey(0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void drawText(Mat & image)
|
||||
{
|
||||
putText(image, "Hello OpenCV",
|
||||
Point(20, 50),
|
||||
FONT_HERSHEY_COMPLEX, 1, // font face and scale
|
||||
Scalar(255, 255, 255), // white
|
||||
1, LINE_AA); // line thickness and type
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// ALIKED + LightGlueMatcher usage example
|
||||
// Demonstrates feature detection, extraction, and matching using ALIKED and LightGlue.
|
||||
|
||||
#include <opencv2/features.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
// ---- Parse arguments ----
|
||||
String alikedModel, lightglueModel, imgPath1, imgPath2;
|
||||
|
||||
if (argc >= 5)
|
||||
{
|
||||
imgPath1 = argv[1];
|
||||
imgPath2 = argv[2];
|
||||
alikedModel = argv[3];
|
||||
lightglueModel = argv[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Usage: " << argv[0] << " <image1> <image2> <aliked_model> <lightglue_model>" << endl;
|
||||
cout << endl;
|
||||
cout << "Example:" << endl;
|
||||
cout << " " << argv[0] << " img1.jpg img2.jpg aliked-n16rot-top1k-640.onnx aliked_lightglue.onnx" << endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---- Load images ----
|
||||
Mat img1 = imread(imgPath1);
|
||||
Mat img2 = imread(imgPath2);
|
||||
if (img1.empty() || img2.empty())
|
||||
{
|
||||
cerr << "Error: cannot load images." << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 1. Create ALIKED feature extractor
|
||||
// ================================================================
|
||||
// Method A: From ONNX model file
|
||||
Ptr<ALIKED> aliked = ALIKED::create(alikedModel);
|
||||
|
||||
// Method B: Customize parameters
|
||||
// ALIKED::Params params;
|
||||
// params.inputSize = Size(640, 640); // Network input resolution
|
||||
// params.normalizeDescriptors = true; // L2-normalize descriptors
|
||||
// Ptr<ALIKED> aliked = ALIKED::create(alikedModel, params);
|
||||
|
||||
// Method C: From in-memory model data
|
||||
// vector<uchar> modelData = readFile(alikedModel);
|
||||
// Ptr<ALIKED> aliked = ALIKED::create(modelData);
|
||||
|
||||
cout << "Descriptor size: " << aliked->descriptorSize() << endl; // 128
|
||||
cout << "Descriptor type: " << aliked->descriptorType() << endl; // CV_32F
|
||||
cout << "Default norm: " << aliked->defaultNorm() << endl; // NORM_L2
|
||||
|
||||
// ================================================================
|
||||
// 2. Detect keypoints and compute descriptors
|
||||
// ================================================================
|
||||
vector<KeyPoint> kpts1, kpts2;
|
||||
Mat descs1, descs2;
|
||||
|
||||
// Method A: detect + compute in one call (recommended)
|
||||
aliked->detectAndCompute(img1, Mat(), kpts1, descs1);
|
||||
aliked->detectAndCompute(img2, Mat(), kpts2, descs2);
|
||||
|
||||
// Method B: detect only (no descriptors)
|
||||
// vector<KeyPoint> kpts;
|
||||
// aliked->detect(img, kpts);
|
||||
|
||||
// Method C: compute only (from existing keypoints)
|
||||
// Mat descs;
|
||||
// aliked->compute(img, kpts, descs);
|
||||
|
||||
cout << "Image 1: " << kpts1.size() << " keypoints, descriptors " << descs1.rows << "x" << descs1.cols << endl;
|
||||
cout << "Image 2: " << kpts2.size() << " keypoints, descriptors " << descs2.rows << "x" << descs2.cols << endl;
|
||||
|
||||
// ================================================================
|
||||
// 3. Create LightGlueMatcher
|
||||
// ================================================================
|
||||
// Method A: From ONNX model file
|
||||
Ptr<LightGlueMatcher> lg = LightGlueMatcher::create(lightglueModel);
|
||||
|
||||
// Method B: Customize parameters
|
||||
// LightGlueMatcher::Params lgParams;
|
||||
// lgParams.scoreThreshold = 0.1f; // Filter low-confidence matches
|
||||
// lgParams.disableWinograd = false; // Keep Winograd convolution
|
||||
// Ptr<LightGlueMatcher> lg = LightGlueMatcher::create(lightglueModel, lgParams);
|
||||
|
||||
// Method C: From in-memory model data
|
||||
// vector<uchar> lgData = readFile(lightglueModel);
|
||||
// Ptr<LightGlueMatcher> lg = LightGlueMatcher::create(lgData);
|
||||
|
||||
// ================================================================
|
||||
// 4. Set keypoint context for LightGlue
|
||||
// ================================================================
|
||||
// LightGlue needs keypoint coordinates + image sizes for spatial reasoning.
|
||||
// Build Nx2 float matrices with pixel coordinates.
|
||||
|
||||
Mat kpts1Mat((int)kpts1.size(), 2, CV_32F);
|
||||
Mat kpts2Mat((int)kpts2.size(), 2, CV_32F);
|
||||
for (size_t i = 0; i < kpts1.size(); i++)
|
||||
{
|
||||
kpts1Mat.at<float>((int)i, 0) = kpts1[i].pt.x;
|
||||
kpts1Mat.at<float>((int)i, 1) = kpts1[i].pt.y;
|
||||
}
|
||||
for (size_t i = 0; i < kpts2.size(); i++)
|
||||
{
|
||||
kpts2Mat.at<float>((int)i, 0) = kpts2[i].pt.x;
|
||||
kpts2Mat.at<float>((int)i, 1) = kpts2[i].pt.y;
|
||||
}
|
||||
|
||||
// setPairInfo must be called before match()/knnMatch()
|
||||
lg->setPairInfo(kpts1Mat, kpts2Mat, img1.size(), img2.size());
|
||||
|
||||
// ================================================================
|
||||
// 5. Match descriptors
|
||||
// ================================================================
|
||||
|
||||
// Method A: 1-to-1 matching (returns best match per query keypoint)
|
||||
vector<DMatch> matches;
|
||||
lg->match(descs1, descs2, matches);
|
||||
|
||||
cout << "1-to-1 matches: " << matches.size() << endl;
|
||||
|
||||
// Method B: kNN matching (k=1 only for LightGlue)
|
||||
// vector<vector<DMatch>> knnMatches;
|
||||
// lg->knnMatch(descs1, descs2, knnMatches, 1);
|
||||
// // knnMatches[i] contains matches for query keypoint i
|
||||
|
||||
// ================================================================
|
||||
// 6. Filter matches by confidence (optional)
|
||||
// ================================================================
|
||||
// DMatch distance = 1.0 - confidence_score
|
||||
// Lower distance = better match
|
||||
|
||||
vector<DMatch> goodMatches;
|
||||
float distanceThreshold = 0.9f; // confidence > 0.1
|
||||
for (const auto& m : matches)
|
||||
{
|
||||
if (m.distance < distanceThreshold)
|
||||
goodMatches.push_back(m);
|
||||
}
|
||||
cout << "Good matches (distance < " << distanceThreshold << "): " << goodMatches.size() << endl;
|
||||
|
||||
// ================================================================
|
||||
// 7. Visualize results
|
||||
// ================================================================
|
||||
Mat canvas;
|
||||
cv::drawMatches(img1, kpts1, img2, kpts2, goodMatches, canvas,
|
||||
Scalar::all(-1), Scalar::all(-1), vector<char>(),
|
||||
DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
|
||||
|
||||
imshow("ALIKED + LightGlue Matches", canvas);
|
||||
cout << "Press any key to exit..." << endl;
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout <<
|
||||
"\nThis program demonstrates dense optical flow algorithm by Gunnar Farneback\n"
|
||||
"Mainly the function: calcOpticalFlowFarneback()\n"
|
||||
"Call:\n"
|
||||
<< argv[0]
|
||||
<< "This reads from video camera 0\n" << endl;
|
||||
}
|
||||
static void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step,
|
||||
double, const Scalar& color)
|
||||
{
|
||||
for(int y = 0; y < cflowmap.rows; y += step)
|
||||
for(int x = 0; x < cflowmap.cols; x += step)
|
||||
{
|
||||
const Point2f& fxy = flow.at<Point2f>(y, x);
|
||||
line(cflowmap, Point(x,y), Point(cvRound(x+fxy.x), cvRound(y+fxy.y)),
|
||||
color);
|
||||
circle(cflowmap, Point(x,y), 2, color, -1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
VideoCapture cap(0);
|
||||
help(argv);
|
||||
if( !cap.isOpened() )
|
||||
return -1;
|
||||
|
||||
Mat flow, cflow, frame;
|
||||
UMat gray, prevgray, uflow;
|
||||
namedWindow("flow", 1);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
cap >> frame;
|
||||
cvtColor(frame, gray, COLOR_BGR2GRAY);
|
||||
|
||||
if( !prevgray.empty() )
|
||||
{
|
||||
calcOpticalFlowFarneback(prevgray, gray, uflow, 0.5, 3, 15, 3, 5, 1.2, 0);
|
||||
cvtColor(prevgray, cflow, COLOR_GRAY2BGR);
|
||||
uflow.copyTo(flow);
|
||||
drawOptFlowMap(flow, cflow, 16, 1.5, Scalar(0, 255, 0));
|
||||
imshow("flow", cflow);
|
||||
}
|
||||
if(waitKey(30)>=0)
|
||||
break;
|
||||
std::swap(prevgray, gray);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
// flann_search_dataset.cpp
|
||||
// Naive program to search a query picture in a dataset illustrating usage of FLANN
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/utils/filesystem.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include "opencv2/flann.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#define _ORB_
|
||||
|
||||
const char* keys =
|
||||
"{ help h | | Print help message. }"
|
||||
"{ dataset | | Path to the images folder used as dataset. }"
|
||||
"{ image | | Path to the image to search for in the dataset. }"
|
||||
"{ save | | Path and filename where to save the flann structure to. }"
|
||||
"{ load | | Path and filename where to load the flann structure from. }";
|
||||
|
||||
struct img_info {
|
||||
int img_index;
|
||||
unsigned int nbr_of_matches;
|
||||
|
||||
img_info(int _img_index, unsigned int _nbr_of_matches)
|
||||
: img_index(_img_index)
|
||||
, nbr_of_matches(_nbr_of_matches)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
//-- Test the program options
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cv::String img_path = parser.get<String>("image");
|
||||
Mat img = imread( samples::findFile( img_path ), IMREAD_GRAYSCALE );
|
||||
if (img.empty() )
|
||||
{
|
||||
cout << "Could not open the image "<< img_path << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cv::String db_path = parser.get<String>("dataset");
|
||||
if (!utils::fs::isDirectory(db_path))
|
||||
{
|
||||
cout << "Dataset folder "<< db_path.c_str() <<" doesn't exist!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cv::String load_db_path = parser.get<String>("load");
|
||||
if ((load_db_path != String()) && (!utils::fs::exists(load_db_path)))
|
||||
{
|
||||
cout << "File " << load_db_path.c_str()
|
||||
<< " where to load the flann structure from doesn't exist!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const cv::String save_db_path = parser.get<String>("save");
|
||||
|
||||
//-- Step 1: Detect the keypoints using a detector, compute the descriptors
|
||||
// in the folder containing the images of the dataset
|
||||
#ifdef _SIFT_
|
||||
int minHessian = 400;
|
||||
Ptr<Feature2D> detector = SIFT::create( minHessian );
|
||||
#elif defined(_ORB_)
|
||||
Ptr<Feature2D> detector = ORB::create();
|
||||
#else
|
||||
cout << "Missing or unknown defined descriptor. "
|
||||
"Only SIFT and ORB are currently interfaced here" << endl;
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
std::vector<KeyPoint> db_keypoints;
|
||||
Mat db_descriptors;
|
||||
std::vector<unsigned int> db_images_indice_range; //store the range of indices per image
|
||||
std::vector<int> db_indice_2_image_lut; //match descriptor indice to its image
|
||||
|
||||
db_images_indice_range.push_back(0);
|
||||
std::vector<cv::String> files;
|
||||
utils::fs::glob(db_path, cv::String(), files);
|
||||
for (std::vector<cv::String>::iterator itr = files.begin(); itr != files.end(); ++itr)
|
||||
{
|
||||
Mat tmp_img = imread( *itr, IMREAD_GRAYSCALE );
|
||||
if (!tmp_img.empty())
|
||||
{
|
||||
std::vector<KeyPoint> kpts;
|
||||
Mat descriptors;
|
||||
detector->detectAndCompute( tmp_img, noArray(), kpts, descriptors );
|
||||
|
||||
db_keypoints.insert( db_keypoints.end(), kpts.begin(), kpts.end() );
|
||||
db_descriptors.push_back( descriptors );
|
||||
db_images_indice_range.push_back( db_images_indice_range.back()
|
||||
+ static_cast<unsigned int>(kpts.size()) );
|
||||
}
|
||||
}
|
||||
|
||||
//-- Set the LUT
|
||||
db_indice_2_image_lut.resize( db_images_indice_range.back() );
|
||||
const int nbr_of_imgs = static_cast<int>( db_images_indice_range.size()-1 );
|
||||
for (int i = 0; i < nbr_of_imgs; ++i)
|
||||
{
|
||||
const unsigned int first_indice = db_images_indice_range[i];
|
||||
const unsigned int last_indice = db_images_indice_range[i+1];
|
||||
std::fill( db_indice_2_image_lut.begin() + first_indice,
|
||||
db_indice_2_image_lut.begin() + last_indice,
|
||||
i );
|
||||
}
|
||||
|
||||
//-- Step 2: build the structure storing the descriptors
|
||||
#if defined(_SIFT_)
|
||||
cv::Ptr<flann::GenericIndex<cvflann::L2<float> > > index;
|
||||
if (load_db_path != String())
|
||||
index = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,
|
||||
cvflann::SavedIndexParams(load_db_path));
|
||||
else
|
||||
index = cv::makePtr<flann::GenericIndex<cvflann::L2<float> > >(db_descriptors,
|
||||
cvflann::KDTreeIndexParams(4));
|
||||
|
||||
#elif defined(_ORB_)
|
||||
cv::Ptr<flann::GenericIndex<cvflann::Hamming<unsigned char> > > index;
|
||||
if (load_db_path != String())
|
||||
index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >
|
||||
(db_descriptors, cvflann::SavedIndexParams(load_db_path));
|
||||
else
|
||||
index = cv::makePtr<flann::GenericIndex<cvflann::Hamming<unsigned char> > >
|
||||
(db_descriptors, cvflann::LshIndexParams());
|
||||
#else
|
||||
cout<< "Descriptor not listed. Set the proper FLANN distance for this descriptor" <<endl;
|
||||
return -1;
|
||||
#endif
|
||||
if (save_db_path != String())
|
||||
index->save(save_db_path);
|
||||
|
||||
|
||||
// Return if no query image was set
|
||||
if (img_path == String())
|
||||
return 0;
|
||||
|
||||
//-- Detect the keypoints and compute the descriptors for the query image
|
||||
std::vector<KeyPoint> img_keypoints;
|
||||
Mat img_descriptors;
|
||||
detector->detectAndCompute( img, noArray(), img_keypoints, img_descriptors );
|
||||
|
||||
|
||||
//-- Step 3: retrieve the descriptors in the dataset matching the ones of the query image
|
||||
// /!\ knnSearch doesn't follow OpenCV standards by not initialising empty Mat properties
|
||||
const int knn = 2;
|
||||
Mat indices(img_descriptors.rows, knn, CV_32S);
|
||||
#if defined(_SIFT_)
|
||||
#define DIST_TYPE float
|
||||
Mat dists(img_descriptors.rows, knn, CV_32F);
|
||||
#elif defined(_ORB_)
|
||||
#define DIST_TYPE int
|
||||
Mat dists(img_descriptors.rows, knn, CV_32S);
|
||||
#endif
|
||||
index->knnSearch( img_descriptors, indices, dists, knn, cvflann::SearchParams(32) );
|
||||
|
||||
//-- Filter matches using the Lowe's ratio test
|
||||
const float ratio_thresh = 0.7f;
|
||||
std::vector<DMatch> good_matches; //contains
|
||||
std::vector<unsigned int> matches_per_img_histogram( nbr_of_imgs, 0 );
|
||||
for (int i = 0; i < dists.rows; ++i)
|
||||
{
|
||||
if (dists.at<DIST_TYPE>(i,0) < ratio_thresh * dists.at<DIST_TYPE>(i,1))
|
||||
{
|
||||
const int indice_in_db = indices.at<int>(i,0);
|
||||
DMatch dmatch(i, indice_in_db, db_indice_2_image_lut[indice_in_db],
|
||||
static_cast<float>(dists.at<DIST_TYPE>(i,0)));
|
||||
good_matches.push_back( dmatch );
|
||||
matches_per_img_histogram[ db_indice_2_image_lut[indice_in_db] ]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-- Step 4: find the dataset image with the highest proportion of matches
|
||||
std::multimap<float, img_info> images_infos;
|
||||
for (int i = 0; i < nbr_of_imgs; ++i)
|
||||
{
|
||||
const unsigned int nbr_of_matches = matches_per_img_histogram[i];
|
||||
if (nbr_of_matches < 4) //we need at leat 4 points for a homography
|
||||
continue;
|
||||
|
||||
const unsigned int nbr_of_kpts = db_images_indice_range[i+1] - db_images_indice_range[i];
|
||||
const float inverse_proportion_of_retrieved_kpts =
|
||||
static_cast<float>(nbr_of_kpts) / static_cast<float>(nbr_of_matches);
|
||||
|
||||
img_info info(i, nbr_of_matches);
|
||||
images_infos.insert( std::pair<float,img_info>(inverse_proportion_of_retrieved_kpts,
|
||||
info) );
|
||||
}
|
||||
|
||||
if (images_infos.begin() == images_infos.end())
|
||||
{
|
||||
cout<<"No good match could be found."<<endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-- if there are several images with a similar proportion of matches,
|
||||
// select the one with the highest number of matches weighted by the
|
||||
// squared ratio of proportions
|
||||
const float best_matches_proportion = images_infos.begin()->first;
|
||||
float new_matches_proportion = best_matches_proportion;
|
||||
img_info best_img = images_infos.begin()->second;
|
||||
|
||||
std::multimap<float, img_info>::iterator it = images_infos.begin();
|
||||
++it;
|
||||
while ((it!=images_infos.end()) && (it->first < 1.1*best_matches_proportion))
|
||||
{
|
||||
const float ratio = new_matches_proportion / it->first;
|
||||
if( it->second.nbr_of_matches * (ratio * ratio) > best_img.nbr_of_matches)
|
||||
{
|
||||
new_matches_proportion = it->first;
|
||||
best_img = it->second;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
//-- Step 5: filter goodmatches that belong to the best image match of the dataset
|
||||
std::vector<DMatch> filtered_good_matches;
|
||||
for (std::vector<DMatch>::iterator itr(good_matches.begin()); itr != good_matches.end(); ++itr)
|
||||
{
|
||||
if (itr->imgIdx == best_img.img_index)
|
||||
filtered_good_matches.push_back(*itr);
|
||||
}
|
||||
|
||||
//-- Retrieve the best image match from the dataset
|
||||
Mat db_img = imread( files[best_img.img_index], IMREAD_GRAYSCALE );
|
||||
|
||||
//-- Draw matches
|
||||
Mat img_matches;
|
||||
drawMatches( img, img_keypoints, db_img, db_keypoints, filtered_good_matches, img_matches, Scalar::all(-1),
|
||||
Scalar::all(-1), std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
|
||||
|
||||
//-- Show detected matches
|
||||
imshow("Good Matches", img_matches );
|
||||
waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrated the floodFill() function\n"
|
||||
"Call:\n"
|
||||
<< argv[0]
|
||||
<< " [image_name -- Default: fruits.jpg]\n" << endl;
|
||||
|
||||
cout << "Hot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tc - switch color/grayscale mode\n"
|
||||
"\tm - switch mask mode\n"
|
||||
"\tr - restore the original image\n"
|
||||
"\ts - use null-range floodfill\n"
|
||||
"\tf - use gradient floodfill with fixed(absolute) range\n"
|
||||
"\tg - use gradient floodfill with floating(relative) range\n"
|
||||
"\t4 - use 4-connectivity mode\n"
|
||||
"\t8 - use 8-connectivity mode\n" << endl;
|
||||
}
|
||||
|
||||
Mat image0, image, gray, mask;
|
||||
int ffillMode = 1;
|
||||
int loDiff = 20, upDiff = 20;
|
||||
int connectivity = 4;
|
||||
int isColor = true;
|
||||
bool useMask = false;
|
||||
int newMaskVal = 255;
|
||||
|
||||
static void onMouse( int event, int x, int y, int, void* )
|
||||
{
|
||||
if( event != EVENT_LBUTTONDOWN )
|
||||
return;
|
||||
|
||||
Point seed = Point(x,y);
|
||||
int lo = ffillMode == 0 ? 0 : loDiff;
|
||||
int up = ffillMode == 0 ? 0 : upDiff;
|
||||
int flags = connectivity + (newMaskVal << 8) +
|
||||
(ffillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);
|
||||
int b = (unsigned)theRNG() & 255;
|
||||
int g = (unsigned)theRNG() & 255;
|
||||
int r = (unsigned)theRNG() & 255;
|
||||
Rect ccomp;
|
||||
|
||||
Scalar newVal = isColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114);
|
||||
Mat dst = isColor ? image : gray;
|
||||
int area;
|
||||
|
||||
if( useMask )
|
||||
{
|
||||
threshold(mask, mask, 1, 128, THRESH_BINARY);
|
||||
area = floodFill(dst, mask, seed, newVal, &ccomp, Scalar(lo, lo, lo),
|
||||
Scalar(up, up, up), flags);
|
||||
imshow( "mask", mask );
|
||||
}
|
||||
else
|
||||
{
|
||||
area = floodFill(dst, seed, newVal, &ccomp, Scalar(lo, lo, lo),
|
||||
Scalar(up, up, up), flags);
|
||||
}
|
||||
|
||||
imshow("image", dst);
|
||||
cout << area << " pixels were repainted\n";
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser (argc, argv,
|
||||
"{help h | | show help message}{@image|fruits.jpg| input image}"
|
||||
);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
string filename = parser.get<string>("@image");
|
||||
image0 = imread(samples::findFile(filename), 1);
|
||||
|
||||
if( image0.empty() )
|
||||
{
|
||||
cout << "Image empty\n";
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
help(argv);
|
||||
image0.copyTo(image);
|
||||
cvtColor(image0, gray, COLOR_BGR2GRAY);
|
||||
mask.create(image0.rows+2, image0.cols+2, CV_8UC1);
|
||||
|
||||
namedWindow( "image", 0 );
|
||||
createTrackbar( "lo_diff", "image", &loDiff, 255, 0 );
|
||||
createTrackbar( "up_diff", "image", &upDiff, 255, 0 );
|
||||
|
||||
setMouseCallback( "image", onMouse, 0 );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
imshow("image", isColor ? image : gray);
|
||||
|
||||
char c = (char)waitKey(0);
|
||||
if( c == 27 )
|
||||
{
|
||||
cout << "Exiting ...\n";
|
||||
break;
|
||||
}
|
||||
switch( c )
|
||||
{
|
||||
case 'c':
|
||||
if( isColor )
|
||||
{
|
||||
cout << "Grayscale mode is set\n";
|
||||
cvtColor(image0, gray, COLOR_BGR2GRAY);
|
||||
mask = Scalar::all(0);
|
||||
isColor = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Color mode is set\n";
|
||||
image0.copyTo(image);
|
||||
mask = Scalar::all(0);
|
||||
isColor = true;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if( useMask )
|
||||
{
|
||||
destroyWindow( "mask" );
|
||||
useMask = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
namedWindow( "mask", 0 );
|
||||
mask = Scalar::all(0);
|
||||
imshow("mask", mask);
|
||||
useMask = true;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
cout << "Original image is restored\n";
|
||||
image0.copyTo(image);
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
mask = Scalar::all(0);
|
||||
break;
|
||||
case 's':
|
||||
cout << "Simple floodfill mode is set\n";
|
||||
ffillMode = 0;
|
||||
break;
|
||||
case 'f':
|
||||
cout << "Fixed Range floodfill mode is set\n";
|
||||
ffillMode = 1;
|
||||
break;
|
||||
case 'g':
|
||||
cout << "Gradient (floating range) floodfill mode is set\n";
|
||||
ffillMode = 2;
|
||||
break;
|
||||
case '4':
|
||||
cout << "4-connectivity mode is set\n";
|
||||
connectivity = 4;
|
||||
break;
|
||||
case '8':
|
||||
cout << "8-connectivity mode is set\n";
|
||||
connectivity = 8;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
@file gauge.cpp
|
||||
@author Alessandro de Oliveira Faria (A.K.A. CABELO)
|
||||
@brief This sample application processes an image frame of an analog gauge
|
||||
and extracts its reading using functions from the OpenCV* computer vision
|
||||
library. The workflow is divided into two stages: calibration and
|
||||
measurement. Questions and suggestions email to:
|
||||
Alessandro de Oliveira Faria cabelo[at]opensuse[dot]org or OpenCV Team.
|
||||
@date Nov 26, 2025
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
struct GaugeCalibration {
|
||||
double min_angle;
|
||||
double max_angle;
|
||||
double min_value;
|
||||
double max_value;
|
||||
string units;
|
||||
int x;
|
||||
int y;
|
||||
int r;
|
||||
};
|
||||
|
||||
static Vec3f avg_circles(const vector<Vec3f> &circles) {
|
||||
double avg_x = 0.0;
|
||||
double avg_y = 0.0;
|
||||
double avg_r = 0.0;
|
||||
|
||||
int b = static_cast<int>(circles.size());
|
||||
for (int i = 0; i < b; ++i) {
|
||||
avg_x += circles[i][0];
|
||||
avg_y += circles[i][1];
|
||||
avg_r += circles[i][2];
|
||||
}
|
||||
|
||||
avg_x /= b;
|
||||
avg_y /= b;
|
||||
avg_r /= b;
|
||||
|
||||
return Vec3f(static_cast<float>(avg_x),
|
||||
static_cast<float>(avg_y),
|
||||
static_cast<float>(avg_r));
|
||||
}
|
||||
|
||||
static double dist_2_pts(double x1, double y1, double x2, double y2) {
|
||||
double dx = x2 - x1;
|
||||
double dy = y2 - y1;
|
||||
return std::sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
static GaugeCalibration calibrate_gauge(string filename) {
|
||||
GaugeCalibration cal;
|
||||
Mat img = imread(filename);
|
||||
if (img.empty()) {
|
||||
cerr << "Error loading image: " << filename << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int height = img.rows;
|
||||
|
||||
Mat gray;
|
||||
cvtColor(img, gray, COLOR_BGR2GRAY);
|
||||
|
||||
// Detects circles (HoughCircles)
|
||||
vector<Vec3f> circles;
|
||||
HoughCircles(gray, circles, HOUGH_GRADIENT, 1,
|
||||
20,
|
||||
100, 50,
|
||||
static_cast<int>(height * 0.35),
|
||||
static_cast<int>(height * 0.48));
|
||||
|
||||
if (circles.empty()) {
|
||||
cerr << "No circles found." << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Vec3f avg = avg_circles(circles);
|
||||
int x = static_cast<int>(avg[0]);
|
||||
int y = static_cast<int>(avg[1]);
|
||||
int r = static_cast<int>(avg[2]);
|
||||
|
||||
cal.x = x;
|
||||
cal.y = y;
|
||||
cal.r = r;
|
||||
|
||||
// Draw the circle and the center.
|
||||
circle(img, Point(x, y), r, Scalar(0, 0, 255), 3, LINE_AA);
|
||||
circle(img, Point(x, y), 2, Scalar(0, 255, 0), 3, LINE_AA);
|
||||
|
||||
// Generation of calibration lines.
|
||||
double separation = 10.0; // in degrees
|
||||
int interval = static_cast<int>(360.0 / separation);
|
||||
|
||||
vector<Point2d> p1(interval);
|
||||
vector<Point2d> p2(interval);
|
||||
vector<Point2d> p_text(interval);
|
||||
|
||||
for (int i = 0; i < interval; ++i) {
|
||||
double angle_rad = separation * i * CV_PI / 180.0;
|
||||
p1[i].x = x + 0.9 * r * std::cos(angle_rad);
|
||||
p1[i].y = y + 0.9 * r * std::sin(angle_rad);
|
||||
}
|
||||
|
||||
int text_offset_x = 10;
|
||||
int text_offset_y = 5;
|
||||
|
||||
for (int i = 0; i < interval; ++i) {
|
||||
double angle_rad = separation * i * CV_PI / 180.0;
|
||||
p2[i].x = x + r * std::cos(angle_rad);
|
||||
p2[i].y = y + r * std::sin(angle_rad);
|
||||
|
||||
double text_angle_rad = separation * (i + 9) * CV_PI / 180.0; // i+9 = rotate 90°
|
||||
p_text[i].x = x - text_offset_x + 1.2 * r * std::cos(text_angle_rad);
|
||||
p_text[i].y = y + text_offset_y + 1.2 * r * std::sin(text_angle_rad);
|
||||
}
|
||||
|
||||
// Draws lines and text.
|
||||
for (int i = 0; i < interval; ++i) {
|
||||
line(img,
|
||||
Point(static_cast<int>(p1[i].x), static_cast<int>(p1[i].y)),
|
||||
Point(static_cast<int>(p2[i].x), static_cast<int>(p2[i].y)),
|
||||
Scalar(0, 255, 0), 2);
|
||||
|
||||
putText(img,
|
||||
to_string(static_cast<int>(i * separation)),
|
||||
Point(static_cast<int>(p_text[i].x), static_cast<int>(p_text[i].y)),
|
||||
FONT_HERSHEY_SIMPLEX, 0.3, Scalar(0, 0, 0), 1, LINE_AA);
|
||||
}
|
||||
|
||||
// Save calibration image
|
||||
imwrite("gauge-calibration.jpg", img);
|
||||
|
||||
cout << "Min angle (lowest possible angle of dial) - in degrees: ";
|
||||
cin >> cal.min_angle;
|
||||
|
||||
cout << "Max angle (highest possible angle) - in degrees: ";
|
||||
cin >> cal.max_angle;
|
||||
|
||||
cout << "Min value: ";
|
||||
cin >> cal.min_value;
|
||||
|
||||
cout << "Max value: ";
|
||||
cin >> cal.max_value;
|
||||
|
||||
cout << "Enter units: ";
|
||||
cin >> cal.units;
|
||||
|
||||
return cal;
|
||||
}
|
||||
|
||||
static double get_current_value(Mat img,
|
||||
const GaugeCalibration &cal) {
|
||||
Mat gray2;
|
||||
cvtColor(img, gray2, COLOR_BGR2GRAY);
|
||||
|
||||
int thresh = 175;
|
||||
int maxValue = 255;
|
||||
|
||||
Mat dst2;
|
||||
threshold(gray2, dst2, thresh, maxValue, THRESH_BINARY_INV);
|
||||
|
||||
// For debugging: threshold image
|
||||
//imwrite("gauge-tempdst2.jpg", dst2);
|
||||
|
||||
// Detect lines
|
||||
vector<Vec4i> lines;
|
||||
int minLineLength = 10;
|
||||
int maxLineGap = 0;
|
||||
HoughLinesP(dst2, lines, 3, CV_PI / 180, 100, minLineLength, maxLineGap);
|
||||
|
||||
if (lines.empty()) {
|
||||
cerr << "No rows found." << endl;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Filter lines by distance from center
|
||||
vector<Vec4i> final_line_list;
|
||||
|
||||
double diff1LowerBound = 0.15;
|
||||
double diff1UpperBound = 0.25;
|
||||
double diff2LowerBound = 0.5;
|
||||
double diff2UpperBound = 1.0;
|
||||
|
||||
int x = cal.x;
|
||||
int y = cal.y;
|
||||
int r = cal.r;
|
||||
|
||||
for (size_t i = 0; i < lines.size(); ++i) {
|
||||
int x1 = lines[i][0];
|
||||
int y1 = lines[i][1];
|
||||
int x2 = lines[i][2];
|
||||
int y2 = lines[i][3];
|
||||
|
||||
double diff1 = dist_2_pts(x, y, x1, y1);
|
||||
double diff2 = dist_2_pts(x, y, x2, y2);
|
||||
|
||||
// ensures that diff1 is the smallest (closest to the center)
|
||||
if (diff1 > diff2) {
|
||||
std::swap(diff1, diff2);
|
||||
}
|
||||
|
||||
if ((diff1 < diff1UpperBound * r) && (diff1 > diff1LowerBound * r) &&
|
||||
(diff2 < diff2UpperBound * r) && (diff2 > diff2LowerBound * r)) {
|
||||
final_line_list.push_back(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (final_line_list.empty()) {
|
||||
cerr << "No lines within the expected radius." << endl;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Use the first filtered line.
|
||||
int x1 = final_line_list[0][0];
|
||||
int y1 = final_line_list[0][1];
|
||||
int x2 = final_line_list[0][2];
|
||||
int y2 = final_line_list[0][3];
|
||||
|
||||
// Draw the line on the original image for debugging.
|
||||
line(img, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2);
|
||||
//imwrite("gauge-lines-2.jpg", img);
|
||||
|
||||
// Decide which point is furthest from the center.
|
||||
double dist_pt_0 = dist_2_pts(x, y, x1, y1);
|
||||
double dist_pt_1 = dist_2_pts(x, y, x2, y2);
|
||||
|
||||
double x_angle, y_angle;
|
||||
if (dist_pt_0 > dist_pt_1) {
|
||||
x_angle = x1 - x;
|
||||
y_angle = y - y1;
|
||||
} else {
|
||||
x_angle = x2 - x;
|
||||
y_angle = y - y2;
|
||||
}
|
||||
|
||||
double res = 0;
|
||||
// atan(y/x) in radians
|
||||
if(x_angle != 0)
|
||||
{
|
||||
res = std::atan(y_angle / x_angle);
|
||||
res = res * 180.0 / CV_PI; // rad2deg
|
||||
}
|
||||
double final_angle = 0.0;
|
||||
|
||||
if (x_angle > 0 && y_angle > 0) { // Quadrante I
|
||||
final_angle = 270.0 - res;
|
||||
}
|
||||
if (x_angle < 0 && y_angle > 0) { // Quadrante II
|
||||
final_angle = 90.0 - res;
|
||||
}
|
||||
if (x_angle < 0 && y_angle < 0) { // Quadrante III
|
||||
final_angle = 90.0 - res;
|
||||
}
|
||||
if (x_angle > 0 && y_angle < 0) { // Quadrante IV
|
||||
final_angle = 270.0 - res;
|
||||
}
|
||||
|
||||
double old_min = cal.min_angle;
|
||||
double old_max = cal.max_angle;
|
||||
double new_min = cal.min_value;
|
||||
double new_max = cal.max_value;
|
||||
|
||||
double old_value = final_angle;
|
||||
double old_range = (old_max - old_min);
|
||||
double new_range = (new_max - new_min);
|
||||
|
||||
double new_value = (((old_value - old_min) * new_range) / old_range) + new_min;
|
||||
|
||||
return new_value;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
CommandLineParser parser(argc, argv, "{@input |gauge-1.jpg|Input image to reads the value using functions from the OpenCV. }");
|
||||
parser.about("Analog-gauge-reader example:\n");
|
||||
parser.printMessage();
|
||||
string filename = parser.get<String>("@input");
|
||||
|
||||
Mat img = imread(filename);
|
||||
if (img.empty()) {
|
||||
cerr << "Error loading image. " << filename << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Calibration
|
||||
GaugeCalibration cal = calibrate_gauge(filename);
|
||||
|
||||
double val = get_current_value(img, cal );
|
||||
cout << "Current reading: " << val << " " << cal.units << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*******************************************************************************
|
||||
*
|
||||
* This program demonstrates various shape fitting techniques using OpenCV.
|
||||
* It reads an image, applies binary thresholding, and then detects contours.
|
||||
*
|
||||
* For each contour, it fits and draws several geometric shapes including
|
||||
* convex hulls, minimum enclosing circles, rectangles, triangles, and ellipses
|
||||
* using different fitting methods:
|
||||
* 1: OpenCV's original method fitEllipse which implements Fitzgibbon 1995 method.
|
||||
* 2: The Approximate Mean Square (AMS) method fitEllipseAMS proposed by Taubin 1991
|
||||
* 3: The Direct least square (Direct) method fitEllipseDirect proposed by Fitzgibbon1999
|
||||
*
|
||||
* The results are displayed with unique colors
|
||||
* for each shape and fitting method for clear differentiation.
|
||||
*
|
||||
*
|
||||
*********************************************************************************/
|
||||
|
||||
#include "opencv2/geometry/2d.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
const string hot_keys =
|
||||
"\n\nHot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tq - quit the program\n"
|
||||
"\tc - make the circle\n"
|
||||
"\tr - make the rectangle\n"
|
||||
"\th - make the convexhull\n"
|
||||
"\tt - make the triangle\n"
|
||||
"\te - make the ellipse\n"
|
||||
"\ta - make all shapes\n"
|
||||
"\t0 - use OpenCV's method for ellipse fitting\n"
|
||||
"\t1 - use Approximate Mean Square (AMS) method for ellipse fitting \n"
|
||||
"\t2 - use Direct least square (Direct) method for ellipse fitting\n";
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates various shape fitting techniques on a set of points using functions: \n"
|
||||
<< "minAreaRect(), minEnclosingTriangle(), minEnclosingCircle(), convexHull(), and ellipse().\n\n"
|
||||
<< "Usage: " << argv[0] << " [--image_name=<image_path> Default: ellipses.jpg]\n\n";
|
||||
cout << hot_keys << endl;
|
||||
}
|
||||
|
||||
void processImage(int, void*);
|
||||
void drawShapes(Mat &img, const vector<Point> &points, int ellipseMethod, string shape);
|
||||
void drawConvexHull(Mat &img, const vector<Point> &points);
|
||||
void drawMinAreaRect(Mat &img, const vector<Point> &points);
|
||||
void drawFittedEllipses(Mat &img, const vector<Point> &points, int ellipseMethod);
|
||||
void drawMinEnclosingCircle(Mat &img, const vector<Point> &points);
|
||||
void drawMinEnclosingTriangle(Mat &img, const vector<Point> &points);
|
||||
|
||||
// Shape fitting options
|
||||
Mat image;
|
||||
enum EllipseFittingMethod {
|
||||
OpenCV_Method,
|
||||
AMS_Method,
|
||||
Direct_Method
|
||||
};
|
||||
|
||||
struct UserData {
|
||||
int sliderPos = 70;
|
||||
string shape = "--all";
|
||||
int ellipseMethod = OpenCV_Method;
|
||||
};
|
||||
|
||||
const char* keys =
|
||||
"{help h | | Show help message }"
|
||||
"{@image |ellipses.jpg| Path to input image file }";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
help(argv);
|
||||
|
||||
if (parser.has("help"))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UserData userData; // variable to pass all the necessary values to trackbar callback
|
||||
|
||||
string filename = parser.get<string>("@image");
|
||||
image = imread(samples::findFile(filename), IMREAD_COLOR); // Read the image from the specified path
|
||||
|
||||
if (image.empty()) {
|
||||
cout << "Could not open or find the image" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
namedWindow("Shapes", WINDOW_AUTOSIZE); // Create a window to display the results
|
||||
createTrackbar("Threshold", "Shapes", NULL, 255, processImage, &userData); // Create a threshold trackbar
|
||||
setTrackbarPos("Threshold", "Shapes", userData.sliderPos);
|
||||
|
||||
for(;;) {
|
||||
char key = (char)waitKey(0); // Listen for a key press
|
||||
if (key == 'q' || key == 27) break; // Exit the loop if 'q' or ESC is pressed
|
||||
|
||||
switch (key) {
|
||||
case 'h': userData.shape = "--convexhull"; break;
|
||||
case 'a': userData.shape = "--all"; break;
|
||||
case 't': userData.shape = "--triangle"; break;
|
||||
case 'c': userData.shape = "--circle"; break;
|
||||
case 'e': userData.shape = "--ellipse"; break;
|
||||
case 'r': userData.shape = "--rectangle"; break;
|
||||
case '0': userData.ellipseMethod = OpenCV_Method; break;
|
||||
case '1': userData.ellipseMethod = AMS_Method; break;
|
||||
case '2': userData.ellipseMethod = Direct_Method; break;
|
||||
default: break; // Do nothing for other keys
|
||||
}
|
||||
|
||||
processImage(userData.sliderPos, &userData); // Process the image with the current settings
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function to draw the minimum enclosing circle around given points
|
||||
void drawMinEnclosingCircle(Mat &img, const vector<Point> &points) {
|
||||
Point2f center;
|
||||
float radius = 0;
|
||||
minEnclosingCircle(points, center, radius); // Find the enclosing circle
|
||||
// Draw the circle
|
||||
circle(img, center, cvRound(radius), Scalar(0, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
|
||||
// Function to draw the minimum enclosing triangle around given points
|
||||
void drawMinEnclosingTriangle(Mat &img, const vector<Point> &points) {
|
||||
vector<Point> triangle;
|
||||
minEnclosingTriangle(points, triangle); // Find the enclosing triangle
|
||||
|
||||
if (triangle.size() != 3) {
|
||||
return;
|
||||
}
|
||||
// Use polylines to draw the triangle. The 'true' argument closes the triangle.
|
||||
polylines(img, triangle, true, Scalar(255, 0, 0), 2, LINE_AA);
|
||||
|
||||
}
|
||||
|
||||
// Function to draw the minimum area rectangle around given points
|
||||
void drawMinAreaRect(Mat &img, const vector<Point> &points) {
|
||||
RotatedRect box = minAreaRect(points); // Find the minimum area rectangle
|
||||
Point2f vtx[4];
|
||||
box.points(vtx);
|
||||
// Convert Point2f to Point because polylines expects a vector of Point
|
||||
vector<Point> rectPoints;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
rectPoints.push_back(Point(cvRound(vtx[i].x), cvRound(vtx[i].y)));
|
||||
}
|
||||
|
||||
// Use polylines to draw the rectangle. The 'true' argument closes the loop, drawing a rectangle.
|
||||
polylines(img, rectPoints, true, Scalar(0, 255, 0), 2, LINE_AA);
|
||||
}
|
||||
|
||||
// Function to draw the convex hull of given points
|
||||
void drawConvexHull(Mat &img, const vector<Point> &points) {
|
||||
vector<Point> hull;
|
||||
convexHull(points, hull, false); // Find the convex hull
|
||||
// Draw the convex hull
|
||||
polylines(img, hull, true, Scalar(255, 255, 0), 2, LINE_AA);
|
||||
}
|
||||
|
||||
inline static bool isGoodBox(const RotatedRect& box) {
|
||||
//size.height >= size.width awalys,only if the pts are on a line or at the same point,size.width=0
|
||||
return (box.size.height <= box.size.width * 30) && (box.size.width > 0);
|
||||
}
|
||||
|
||||
// Function to draw fitted ellipses using different methods
|
||||
void drawFittedEllipses(Mat &img, const vector<Point> &points, int ellipseMethod) {
|
||||
switch (ellipseMethod) {
|
||||
case OpenCV_Method: // Standard ellipse fitting
|
||||
{
|
||||
RotatedRect fittedEllipse = fitEllipse(points);
|
||||
if (isGoodBox(fittedEllipse)) {
|
||||
ellipse(img, fittedEllipse, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
putText(img, "OpenCV", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
break;
|
||||
case AMS_Method: // AMS ellipse fitting
|
||||
{
|
||||
RotatedRect fittedEllipseAMS = fitEllipseAMS(points);
|
||||
if (isGoodBox(fittedEllipseAMS)) {
|
||||
ellipse(img, fittedEllipseAMS, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
putText(img, "AMS", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
break;
|
||||
case Direct_Method: // Direct ellipse fitting
|
||||
{
|
||||
RotatedRect fittedEllipseDirect = fitEllipseDirect(points);
|
||||
if (isGoodBox(fittedEllipseDirect)) {
|
||||
ellipse(img, fittedEllipseDirect, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
putText(img, "Direct", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
break;
|
||||
default: // Default case falls back to OpenCV method
|
||||
{
|
||||
RotatedRect fittedEllipse = fitEllipse(points);
|
||||
if (isGoodBox(fittedEllipse)) {
|
||||
ellipse(img, fittedEllipse, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
}
|
||||
putText(img, "OpenCV (default)", Point(img.cols - 80, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 0, 255), 2, LINE_AA);
|
||||
cout << "Warning: Invalid ellipseMethod value. Falling back to default OpenCV method." << endl;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to draw shapes
|
||||
void drawShapes(Mat &img, const vector<Point> &points, int ellipseMethod, string shape) {
|
||||
if (shape == "--circle") {
|
||||
drawMinEnclosingCircle(img, points);
|
||||
} else if (shape == "--triangle") {
|
||||
drawMinEnclosingTriangle(img, points);
|
||||
} else if (shape == "--rectangle") {
|
||||
drawMinAreaRect(img, points);
|
||||
} else if (shape == "--convexhull") {
|
||||
drawConvexHull(img, points);
|
||||
} else if (shape == "--ellipse"){
|
||||
drawFittedEllipses(img, points, ellipseMethod);
|
||||
}
|
||||
else if (shape == "--all") {
|
||||
drawMinEnclosingCircle(img, points);
|
||||
drawMinEnclosingTriangle(img, points);
|
||||
drawMinAreaRect(img, points);
|
||||
drawConvexHull(img, points);
|
||||
drawFittedEllipses(img, points, ellipseMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// Main function to process the image based on the current trackbar position
|
||||
void processImage(int position, void* userData){
|
||||
|
||||
UserData* data = static_cast<UserData*>(userData);
|
||||
|
||||
data->sliderPos = position;
|
||||
|
||||
Mat processedImg = image.clone(); // Clone the original image for processing
|
||||
Mat gray;
|
||||
cvtColor(processedImg, gray, COLOR_BGR2GRAY); // Convert to grayscale
|
||||
threshold(gray, gray, data->sliderPos, 255, THRESH_BINARY); // Apply binary threshold
|
||||
|
||||
Mat filteredImg;
|
||||
medianBlur(gray, filteredImg, 3);
|
||||
|
||||
vector<vector<Point>> contours;
|
||||
findContours(filteredImg, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // Find contours
|
||||
|
||||
if (contours.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
imshow("Mask", filteredImg); // Show the mask
|
||||
for (size_t i = 0; i < contours.size(); ++i) {
|
||||
if (contours[i].size() < 5) { // Check if the contour has enough points
|
||||
continue;
|
||||
}
|
||||
drawShapes(processedImg, contours[i], data->ellipseMethod, data->shape);
|
||||
}
|
||||
|
||||
imshow("Shapes", processedImg); // Display the processed image with fitted shapes
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates GrabCut segmentation -- select an object in a region\n"
|
||||
"and then grabcut will attempt to segment it out.\n"
|
||||
"Call:\n"
|
||||
<< argv[0] << " <image_name>\n"
|
||||
"\nSelect a rectangular area around the object you want to segment\n" <<
|
||||
"\nHot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tr - restore the original image\n"
|
||||
"\tn - next iteration\n"
|
||||
"\n"
|
||||
"\tleft mouse button - set rectangle\n"
|
||||
"\n"
|
||||
"\tCTRL+left mouse button - set GC_BGD pixels\n"
|
||||
"\tSHIFT+left mouse button - set GC_FGD pixels\n"
|
||||
"\n"
|
||||
"\tCTRL+right mouse button - set GC_PR_BGD pixels\n"
|
||||
"\tSHIFT+right mouse button - set GC_PR_FGD pixels\n" << endl;
|
||||
}
|
||||
|
||||
const Scalar RED = Scalar(0,0,255);
|
||||
const Scalar PINK = Scalar(230,130,255);
|
||||
const Scalar BLUE = Scalar(255,0,0);
|
||||
const Scalar LIGHTBLUE = Scalar(255,255,160);
|
||||
const Scalar GREEN = Scalar(0,255,0);
|
||||
|
||||
const int BGD_KEY = EVENT_FLAG_CTRLKEY;
|
||||
const int FGD_KEY = EVENT_FLAG_SHIFTKEY;
|
||||
|
||||
static void getBinMask( const Mat& comMask, Mat& binMask )
|
||||
{
|
||||
if( comMask.empty() || comMask.type()!=CV_8UC1 )
|
||||
CV_Error( Error::StsBadArg, "comMask is empty or has incorrect type (not CV_8UC1)" );
|
||||
if( binMask.empty() || binMask.rows!=comMask.rows || binMask.cols!=comMask.cols )
|
||||
binMask.create( comMask.size(), CV_8UC1 );
|
||||
binMask = comMask & 1;
|
||||
}
|
||||
|
||||
class GCApplication
|
||||
{
|
||||
public:
|
||||
enum{ NOT_SET = 0, IN_PROCESS = 1, SET = 2 };
|
||||
static const int radius = 2;
|
||||
static const int thickness = -1;
|
||||
|
||||
void reset();
|
||||
void setImageAndWinName( const Mat& _image, const string& _winName );
|
||||
void showImage() const;
|
||||
void mouseClick( int event, int x, int y, int flags, void* param );
|
||||
int nextIter();
|
||||
int getIterCount() const { return iterCount; }
|
||||
private:
|
||||
void setRectInMask();
|
||||
void setLblsInMask( int flags, Point p, bool isPr );
|
||||
|
||||
const string* winName;
|
||||
const Mat* image;
|
||||
Mat mask;
|
||||
Mat bgdModel, fgdModel;
|
||||
|
||||
uchar rectState, lblsState, prLblsState;
|
||||
bool isInitialized;
|
||||
|
||||
Rect rect;
|
||||
vector<Point> fgdPxls, bgdPxls, prFgdPxls, prBgdPxls;
|
||||
int iterCount;
|
||||
};
|
||||
|
||||
void GCApplication::reset()
|
||||
{
|
||||
if( !mask.empty() )
|
||||
mask.setTo(Scalar::all(GC_BGD));
|
||||
bgdPxls.clear(); fgdPxls.clear();
|
||||
prBgdPxls.clear(); prFgdPxls.clear();
|
||||
|
||||
isInitialized = false;
|
||||
rectState = NOT_SET;
|
||||
lblsState = NOT_SET;
|
||||
prLblsState = NOT_SET;
|
||||
iterCount = 0;
|
||||
}
|
||||
|
||||
void GCApplication::setImageAndWinName( const Mat& _image, const string& _winName )
|
||||
{
|
||||
if( _image.empty() || _winName.empty() )
|
||||
return;
|
||||
image = &_image;
|
||||
winName = &_winName;
|
||||
mask.create( image->size(), CV_8UC1);
|
||||
reset();
|
||||
}
|
||||
|
||||
void GCApplication::showImage() const
|
||||
{
|
||||
if( image->empty() || winName->empty() )
|
||||
return;
|
||||
|
||||
Mat res;
|
||||
Mat binMask;
|
||||
image->copyTo( res );
|
||||
if( isInitialized ){
|
||||
getBinMask( mask, binMask);
|
||||
|
||||
Mat black (binMask.rows, binMask.cols, CV_8UC3, cv::Scalar(0,0,0));
|
||||
black.setTo(Scalar::all(255), binMask);
|
||||
|
||||
addWeighted(black, 0.5, res, 0.5, 0.0, res);
|
||||
}
|
||||
|
||||
vector<Point>::const_iterator it;
|
||||
for( it = bgdPxls.begin(); it != bgdPxls.end(); ++it )
|
||||
circle( res, *it, radius, BLUE, thickness );
|
||||
for( it = fgdPxls.begin(); it != fgdPxls.end(); ++it )
|
||||
circle( res, *it, radius, RED, thickness );
|
||||
for( it = prBgdPxls.begin(); it != prBgdPxls.end(); ++it )
|
||||
circle( res, *it, radius, LIGHTBLUE, thickness );
|
||||
for( it = prFgdPxls.begin(); it != prFgdPxls.end(); ++it )
|
||||
circle( res, *it, radius, PINK, thickness );
|
||||
|
||||
if( rectState == IN_PROCESS || rectState == SET )
|
||||
rectangle( res, Point( rect.x, rect.y ), Point(rect.x + rect.width, rect.y + rect.height ), GREEN, 2);
|
||||
|
||||
imshow( *winName, res );
|
||||
}
|
||||
|
||||
void GCApplication::setRectInMask()
|
||||
{
|
||||
CV_Assert( !mask.empty() );
|
||||
mask.setTo( GC_BGD );
|
||||
rect.x = max(0, rect.x);
|
||||
rect.y = max(0, rect.y);
|
||||
rect.width = min(rect.width, image->cols-rect.x);
|
||||
rect.height = min(rect.height, image->rows-rect.y);
|
||||
(mask(rect)).setTo( Scalar(GC_PR_FGD) );
|
||||
}
|
||||
|
||||
void GCApplication::setLblsInMask( int flags, Point p, bool isPr )
|
||||
{
|
||||
vector<Point> *bpxls, *fpxls;
|
||||
uchar bvalue, fvalue;
|
||||
if( !isPr )
|
||||
{
|
||||
bpxls = &bgdPxls;
|
||||
fpxls = &fgdPxls;
|
||||
bvalue = GC_BGD;
|
||||
fvalue = GC_FGD;
|
||||
}
|
||||
else
|
||||
{
|
||||
bpxls = &prBgdPxls;
|
||||
fpxls = &prFgdPxls;
|
||||
bvalue = GC_PR_BGD;
|
||||
fvalue = GC_PR_FGD;
|
||||
}
|
||||
if( flags & BGD_KEY )
|
||||
{
|
||||
bpxls->push_back(p);
|
||||
circle( mask, p, radius, bvalue, thickness );
|
||||
}
|
||||
if( flags & FGD_KEY )
|
||||
{
|
||||
fpxls->push_back(p);
|
||||
circle( mask, p, radius, fvalue, thickness );
|
||||
}
|
||||
}
|
||||
|
||||
void GCApplication::mouseClick( int event, int x, int y, int flags, void* )
|
||||
{
|
||||
// TODO add bad args check
|
||||
switch( event )
|
||||
{
|
||||
case EVENT_LBUTTONDOWN: // set rect or GC_BGD(GC_FGD) labels
|
||||
{
|
||||
bool isb = (flags & BGD_KEY) != 0,
|
||||
isf = (flags & FGD_KEY) != 0;
|
||||
if( rectState == NOT_SET && !isb && !isf )
|
||||
{
|
||||
rectState = IN_PROCESS;
|
||||
rect = Rect( x, y, 1, 1 );
|
||||
}
|
||||
if ( (isb || isf) && rectState == SET )
|
||||
lblsState = IN_PROCESS;
|
||||
}
|
||||
break;
|
||||
case EVENT_RBUTTONDOWN: // set GC_PR_BGD(GC_PR_FGD) labels
|
||||
{
|
||||
bool isb = (flags & BGD_KEY) != 0,
|
||||
isf = (flags & FGD_KEY) != 0;
|
||||
if ( (isb || isf) && rectState == SET )
|
||||
prLblsState = IN_PROCESS;
|
||||
}
|
||||
break;
|
||||
case EVENT_LBUTTONUP:
|
||||
if( rectState == IN_PROCESS )
|
||||
{
|
||||
if(rect.x == x || rect.y == y){
|
||||
rectState = NOT_SET;
|
||||
}
|
||||
else{
|
||||
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
|
||||
rectState = SET;
|
||||
setRectInMask();
|
||||
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
|
||||
}
|
||||
showImage();
|
||||
}
|
||||
if( lblsState == IN_PROCESS )
|
||||
{
|
||||
setLblsInMask(flags, Point(x,y), false);
|
||||
lblsState = SET;
|
||||
nextIter();
|
||||
showImage();
|
||||
}
|
||||
else{
|
||||
if(rectState == SET){
|
||||
nextIter();
|
||||
showImage();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVENT_RBUTTONUP:
|
||||
if( prLblsState == IN_PROCESS )
|
||||
{
|
||||
setLblsInMask(flags, Point(x,y), true);
|
||||
prLblsState = SET;
|
||||
}
|
||||
if(rectState == SET){
|
||||
nextIter();
|
||||
showImage();
|
||||
}
|
||||
break;
|
||||
case EVENT_MOUSEMOVE:
|
||||
if( rectState == IN_PROCESS )
|
||||
{
|
||||
rect = Rect( Point(rect.x, rect.y), Point(x,y) );
|
||||
CV_Assert( bgdPxls.empty() && fgdPxls.empty() && prBgdPxls.empty() && prFgdPxls.empty() );
|
||||
showImage();
|
||||
}
|
||||
else if( lblsState == IN_PROCESS )
|
||||
{
|
||||
setLblsInMask(flags, Point(x,y), false);
|
||||
showImage();
|
||||
}
|
||||
else if( prLblsState == IN_PROCESS )
|
||||
{
|
||||
setLblsInMask(flags, Point(x,y), true);
|
||||
showImage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int GCApplication::nextIter()
|
||||
{
|
||||
if( isInitialized )
|
||||
grabCut( *image, mask, rect, bgdModel, fgdModel, 1 );
|
||||
else
|
||||
{
|
||||
if( rectState != SET )
|
||||
return iterCount;
|
||||
|
||||
if( lblsState == SET || prLblsState == SET )
|
||||
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_MASK );
|
||||
else
|
||||
grabCut( *image, mask, rect, bgdModel, fgdModel, 1, GC_INIT_WITH_RECT );
|
||||
|
||||
isInitialized = true;
|
||||
}
|
||||
iterCount++;
|
||||
|
||||
bgdPxls.clear(); fgdPxls.clear();
|
||||
prBgdPxls.clear(); prFgdPxls.clear();
|
||||
|
||||
return iterCount;
|
||||
}
|
||||
|
||||
GCApplication gcapp;
|
||||
|
||||
static void on_mouse( int event, int x, int y, int flags, void* param )
|
||||
{
|
||||
gcapp.mouseClick( event, x, y, flags, param );
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{@input| messi5.jpg |}");
|
||||
help(argv);
|
||||
|
||||
string filename = parser.get<string>("@input");
|
||||
if( filename.empty() )
|
||||
{
|
||||
cout << "\nDurn, empty filename" << endl;
|
||||
return 1;
|
||||
}
|
||||
Mat image = imread(samples::findFile(filename), IMREAD_COLOR);
|
||||
if( image.empty() )
|
||||
{
|
||||
cout << "\n Durn, couldn't read image filename " << filename << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const string winName = "image";
|
||||
namedWindow( winName, WINDOW_AUTOSIZE );
|
||||
setMouseCallback( winName, on_mouse, 0 );
|
||||
|
||||
gcapp.setImageAndWinName( image, winName );
|
||||
gcapp.showImage();
|
||||
|
||||
for(;;)
|
||||
{
|
||||
char c = (char)waitKey(0);
|
||||
switch( c )
|
||||
{
|
||||
case '\x1b':
|
||||
cout << "Exiting ..." << endl;
|
||||
goto exit_main;
|
||||
case 'r':
|
||||
cout << endl;
|
||||
gcapp.reset();
|
||||
gcapp.showImage();
|
||||
break;
|
||||
case 'n':
|
||||
int iterCount = gcapp.getIterCount();
|
||||
cout << "<" << iterCount << "... ";
|
||||
int newIterCount = gcapp.nextIter();
|
||||
if( newIterCount > iterCount )
|
||||
{
|
||||
gcapp.showImage();
|
||||
cout << iterCount << ">" << endl;
|
||||
}
|
||||
else
|
||||
cout << "rect must be determined>" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
exit_main:
|
||||
destroyWindow( winName );
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* This sample demonstrates the use of the function
|
||||
* findTransformECC that implements the image alignment ECC algorithm
|
||||
*
|
||||
*
|
||||
* The demo loads an image (defaults to fruits.jpg) and it artificially creates
|
||||
* a template image based on the given motion type. When two images are given,
|
||||
* the first image is the input image and the second one defines the template image.
|
||||
* In the latter case, you can also parse the warp's initialization.
|
||||
*
|
||||
* Input and output warp files consist of the raw warp (transform) elements
|
||||
*
|
||||
* Authors: G. Evangelidis, INRIA, Grenoble, France
|
||||
* M. Asbach, Fraunhofer IAIS, St. Augustin, Germany
|
||||
*/
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/video.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/core/utility.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(const char** argv);
|
||||
static int readWarp(string iFilename, Mat& warp, int motionType);
|
||||
static int saveWarp(string fileName, const Mat& warp, int motionType);
|
||||
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W);
|
||||
|
||||
#define HOMO_VECTOR(H, x, y)\
|
||||
H.at<float>(0,0) = (float)(x);\
|
||||
H.at<float>(1,0) = (float)(y);\
|
||||
H.at<float>(2,0) = 1.;
|
||||
|
||||
#define GET_HOMO_VALUES(X, x, y)\
|
||||
(x) = static_cast<float> (X.at<float>(0,0)/X.at<float>(2,0));\
|
||||
(y) = static_cast<float> (X.at<float>(1,0)/X.at<float>(2,0));
|
||||
|
||||
|
||||
const std::string keys =
|
||||
"{@inputImage | fruits.jpg | input image filename }"
|
||||
"{@templateImage | | template image filename (optional)}"
|
||||
"{@inputWarp | | input warp (matrix) filename (optional)}"
|
||||
"{n numOfIter | 50 | ECC's iterations }"
|
||||
"{e epsilon | 0.0001 | ECC's convergence epsilon }"
|
||||
"{o outputWarp | outWarp.ecc | output warp (matrix) filename }"
|
||||
"{m motionType | affine | type of motion (translation, euclidean, affine, homography) }"
|
||||
"{v verbose | 1 | display initial and final images }"
|
||||
"{w warpedImfile | warpedECC.png | warped input image }"
|
||||
"{h help | | print help message }"
|
||||
;
|
||||
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
|
||||
cout << "\nThis file demonstrates the use of the ECC image alignment algorithm. When one image"
|
||||
" is given, the template image is artificially formed by a random warp. When both images"
|
||||
" are given, the initialization of the warp by command line parsing is possible. "
|
||||
"If inputWarp is missing, the identity transformation initializes the algorithm. \n" << endl;
|
||||
|
||||
cout << "\nUsage example (one image): \n"
|
||||
<< argv[0]
|
||||
<< " fruits.jpg -o=outWarp.ecc "
|
||||
"-m=euclidean -e=1e-6 -N=70 -v=1 \n" << endl;
|
||||
|
||||
cout << "\nUsage example (two images with initialization): \n"
|
||||
<< argv[0]
|
||||
<< " yourInput.png yourTemplate.png "
|
||||
"yourInitialWarp.ecc -o=outWarp.ecc -m=homography -e=1e-6 -N=70 -v=1 -w=yourFinalImage.png \n" << endl;
|
||||
|
||||
}
|
||||
|
||||
static int readWarp(string iFilename, Mat& warp, int motionType){
|
||||
|
||||
// it reads from file a specific number of raw values:
|
||||
// 9 values for homography, 6 otherwise
|
||||
CV_Assert(warp.type()==CV_32FC1);
|
||||
int numOfElements;
|
||||
if (motionType==MOTION_HOMOGRAPHY)
|
||||
numOfElements=9;
|
||||
else
|
||||
numOfElements=6;
|
||||
|
||||
int i;
|
||||
int ret_value;
|
||||
|
||||
ifstream myfile(iFilename.c_str());
|
||||
if (myfile.is_open()){
|
||||
float* matPtr = warp.ptr<float>(0);
|
||||
for(i=0; i<numOfElements; i++){
|
||||
myfile >> matPtr[i];
|
||||
}
|
||||
ret_value = 1;
|
||||
}
|
||||
else {
|
||||
cout << "Unable to open file " << iFilename.c_str() << endl;
|
||||
ret_value = 0;
|
||||
}
|
||||
return ret_value;
|
||||
}
|
||||
|
||||
static int saveWarp(string fileName, const Mat& warp, int motionType)
|
||||
{
|
||||
// it saves the raw matrix elements in a file
|
||||
CV_Assert(warp.type()==CV_32FC1);
|
||||
|
||||
const float* matPtr = warp.ptr<float>(0);
|
||||
int ret_value;
|
||||
|
||||
ofstream outfile(fileName.c_str());
|
||||
if( !outfile ) {
|
||||
cerr << "error in saving "
|
||||
<< "Couldn't open file '" << fileName.c_str() << "'!" << endl;
|
||||
ret_value = 0;
|
||||
}
|
||||
else {//save the warp's elements
|
||||
outfile << matPtr[0] << " " << matPtr[1] << " " << matPtr[2] << endl;
|
||||
outfile << matPtr[3] << " " << matPtr[4] << " " << matPtr[5] << endl;
|
||||
if (motionType==MOTION_HOMOGRAPHY){
|
||||
outfile << matPtr[6] << " " << matPtr[7] << " " << matPtr[8] << endl;
|
||||
}
|
||||
ret_value = 1;
|
||||
}
|
||||
return ret_value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void draw_warped_roi(Mat& image, const int width, const int height, Mat& W)
|
||||
{
|
||||
Point2f top_left, top_right, bottom_left, bottom_right;
|
||||
|
||||
Mat H = Mat (3, 1, CV_32F);
|
||||
Mat U = Mat (3, 1, CV_32F);
|
||||
|
||||
Mat warp_mat = Mat::eye (3, 3, CV_32F);
|
||||
|
||||
for (int y = 0; y < W.rows; y++)
|
||||
for (int x = 0; x < W.cols; x++)
|
||||
warp_mat.at<float>(y,x) = W.at<float>(y,x);
|
||||
|
||||
//warp the corners of rectangle
|
||||
|
||||
// top-left
|
||||
HOMO_VECTOR(H, 1, 1);
|
||||
gemm(warp_mat, H, 1, 0, 0, U);
|
||||
GET_HOMO_VALUES(U, top_left.x, top_left.y);
|
||||
|
||||
// top-right
|
||||
HOMO_VECTOR(H, width, 1);
|
||||
gemm(warp_mat, H, 1, 0, 0, U);
|
||||
GET_HOMO_VALUES(U, top_right.x, top_right.y);
|
||||
|
||||
// bottom-left
|
||||
HOMO_VECTOR(H, 1, height);
|
||||
gemm(warp_mat, H, 1, 0, 0, U);
|
||||
GET_HOMO_VALUES(U, bottom_left.x, bottom_left.y);
|
||||
|
||||
// bottom-right
|
||||
HOMO_VECTOR(H, width, height);
|
||||
gemm(warp_mat, H, 1, 0, 0, U);
|
||||
GET_HOMO_VALUES(U, bottom_right.x, bottom_right.y);
|
||||
|
||||
// draw the warped perimeter
|
||||
line(image, top_left, top_right, Scalar(255));
|
||||
line(image, top_right, bottom_right, Scalar(255));
|
||||
line(image, bottom_right, bottom_left, Scalar(255));
|
||||
line(image, bottom_left, top_left, Scalar(255));
|
||||
}
|
||||
|
||||
int main (const int argc, const char * argv[])
|
||||
{
|
||||
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("ECC demo");
|
||||
|
||||
parser.printMessage();
|
||||
help(argv);
|
||||
|
||||
string imgFile = parser.get<string>(0);
|
||||
string tempImgFile = parser.get<string>(1);
|
||||
string inWarpFile = parser.get<string>(2);
|
||||
|
||||
int number_of_iterations = parser.get<int>("n");
|
||||
double termination_eps = parser.get<double>("e");
|
||||
string warpType = parser.get<string>("m");
|
||||
int verbose = parser.get<int>("v");
|
||||
string finalWarp = parser.get<string>("o");
|
||||
string warpedImFile = parser.get<string>("w");
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
if (!(warpType == "translation" || warpType == "euclidean"
|
||||
|| warpType == "affine" || warpType == "homography"))
|
||||
{
|
||||
cerr << "Invalid motion transformation" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mode_temp;
|
||||
if (warpType == "translation")
|
||||
mode_temp = MOTION_TRANSLATION;
|
||||
else if (warpType == "euclidean")
|
||||
mode_temp = MOTION_EUCLIDEAN;
|
||||
else if (warpType == "affine")
|
||||
mode_temp = MOTION_AFFINE;
|
||||
else
|
||||
mode_temp = MOTION_HOMOGRAPHY;
|
||||
|
||||
Mat inputImage = imread(samples::findFile(imgFile), IMREAD_GRAYSCALE);
|
||||
if (inputImage.empty())
|
||||
{
|
||||
cerr << "Unable to load the inputImage" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat target_image;
|
||||
Mat template_image;
|
||||
|
||||
if (tempImgFile!="") {
|
||||
inputImage.copyTo(target_image);
|
||||
template_image = imread(samples::findFile(tempImgFile), IMREAD_GRAYSCALE);
|
||||
if (template_image.empty()){
|
||||
cerr << "Unable to load the template image" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
else{ //apply random warp to input image
|
||||
resize(inputImage, target_image, Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
|
||||
Mat warpGround;
|
||||
RNG rng(getTickCount());
|
||||
double angle;
|
||||
switch (mode_temp) {
|
||||
case MOTION_TRANSLATION:
|
||||
warpGround = Mat_<float>({2,3}, {
|
||||
1, 0, (rng.uniform(10.f, 20.f)),
|
||||
0, 1, (rng.uniform(10.f, 20.f))
|
||||
});
|
||||
warpAffine(target_image, template_image, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_EUCLIDEAN:
|
||||
angle = CV_PI/30 + CV_PI*rng.uniform((double)-2.f, (double)2.f)/180;
|
||||
|
||||
warpGround = Mat_<float>({2,3}, {
|
||||
(float)cos(angle), (float)-sin(angle), (rng.uniform(10.f, 20.f)),
|
||||
(float)sin(angle), (float)cos(angle), (rng.uniform(10.f, 20.f))
|
||||
});
|
||||
warpAffine(target_image, template_image, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_AFFINE:
|
||||
|
||||
warpGround = Mat_<float>({2,3}, {
|
||||
(1-rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)), (rng.uniform(10.f, 20.f))
|
||||
});
|
||||
warpAffine(target_image, template_image, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
case MOTION_HOMOGRAPHY:
|
||||
warpGround = Mat_<float>({3,3}, {
|
||||
(1-rng.uniform(-0.05f, 0.05f)), (rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)),
|
||||
(rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f
|
||||
});
|
||||
warpPerspective(target_image, template_image, warpGround,
|
||||
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int warp_mode = mode_temp;
|
||||
|
||||
// initialize or load the warp matrix
|
||||
Mat warp_matrix;
|
||||
if (warpType == "homography")
|
||||
warp_matrix = Mat::eye(3, 3, CV_32F);
|
||||
else
|
||||
warp_matrix = Mat::eye(2, 3, CV_32F);
|
||||
|
||||
if (inWarpFile!=""){
|
||||
int readflag = readWarp(inWarpFile, warp_matrix, warp_mode);
|
||||
if ((!readflag) || warp_matrix.empty())
|
||||
{
|
||||
cerr << "-> Check warp initialization file" << endl << flush;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
printf("\n ->Performance Warning: Identity warp ideally assumes images of "
|
||||
"similar size. If the deformation is strong, the identity warp may not "
|
||||
"be a good initialization. \n");
|
||||
|
||||
}
|
||||
|
||||
if (number_of_iterations > 200)
|
||||
cout << "-> Warning: too many iterations " << endl;
|
||||
|
||||
if (warp_mode != MOTION_HOMOGRAPHY)
|
||||
warp_matrix.rows = 2;
|
||||
|
||||
// start timing
|
||||
const double tic_init = (double) getTickCount ();
|
||||
double cc = findTransformECC (template_image, target_image, warp_matrix, warp_mode,
|
||||
TermCriteria (TermCriteria::COUNT+TermCriteria::EPS,
|
||||
number_of_iterations, termination_eps));
|
||||
|
||||
if (cc == -1)
|
||||
{
|
||||
cerr << "The execution was interrupted. The correlation value is going to be minimized." << endl;
|
||||
cerr << "Check the warp initialization and/or the size of images." << endl << flush;
|
||||
}
|
||||
|
||||
// end timing
|
||||
const double toc_final = (double) getTickCount ();
|
||||
const double total_time = (toc_final-tic_init)/(getTickFrequency());
|
||||
if (verbose){
|
||||
cout << "Alignment time (" << warpType << " transformation): "
|
||||
<< total_time << " sec" << endl << flush;
|
||||
// cout << "Final correlation: " << cc << endl << flush;
|
||||
|
||||
}
|
||||
|
||||
// save the final warp matrix
|
||||
saveWarp(finalWarp, warp_matrix, warp_mode);
|
||||
|
||||
if (verbose){
|
||||
cout << "\nThe final warp has been saved in the file: " << finalWarp << endl << flush;
|
||||
}
|
||||
|
||||
// save the final warped image
|
||||
Mat warped_image = Mat(template_image.rows, template_image.cols, CV_32FC1);
|
||||
if (warp_mode != MOTION_HOMOGRAPHY)
|
||||
warpAffine (target_image, warped_image, warp_matrix, warped_image.size(),
|
||||
INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
else
|
||||
warpPerspective (target_image, warped_image, warp_matrix, warped_image.size(),
|
||||
INTER_LINEAR + WARP_INVERSE_MAP);
|
||||
|
||||
//save the warped image
|
||||
imwrite(warpedImFile, warped_image);
|
||||
|
||||
// display resulting images
|
||||
if (verbose)
|
||||
{
|
||||
|
||||
cout << "The warped image has been saved in the file: " << warpedImFile << endl << flush;
|
||||
|
||||
namedWindow ("image", WINDOW_AUTOSIZE);
|
||||
namedWindow ("template", WINDOW_AUTOSIZE);
|
||||
namedWindow ("warped image", WINDOW_AUTOSIZE);
|
||||
namedWindow ("error (black: no error)", WINDOW_AUTOSIZE);
|
||||
|
||||
moveWindow ("image", 20, 300);
|
||||
moveWindow ("template", 300, 300);
|
||||
moveWindow ("warped image", 600, 300);
|
||||
moveWindow ("error (black: no error)", 900, 300);
|
||||
|
||||
// draw boundaries of corresponding regions
|
||||
Mat identity_matrix = Mat::eye(3,3,CV_32F);
|
||||
|
||||
draw_warped_roi (target_image, template_image.cols-2, template_image.rows-2, warp_matrix);
|
||||
draw_warped_roi (template_image, template_image.cols-2, template_image.rows-2, identity_matrix);
|
||||
|
||||
Mat errorImage;
|
||||
subtract(template_image, warped_image, errorImage);
|
||||
double max_of_error;
|
||||
minMaxLoc(errorImage, NULL, &max_of_error);
|
||||
|
||||
// show images
|
||||
cout << "Press any key to exit the demo (you might need to click on the images before)." << endl << flush;
|
||||
|
||||
imshow ("image", target_image);
|
||||
waitKey (200);
|
||||
imshow ("template", template_image);
|
||||
waitKey (200);
|
||||
imshow ("warped image", warped_image);
|
||||
waitKey(200);
|
||||
imshow ("error (black: no error)", abs(errorImage)*255/max_of_error);
|
||||
waitKey(0);
|
||||
|
||||
}
|
||||
|
||||
// done
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*this creates a yaml or xml list of files from the command line args
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** av)
|
||||
{
|
||||
cout << "\nThis creates a yaml or xml list of files from the command line args\n"
|
||||
"usage:\n./" << av[0] << " imagelist.yaml *.png\n"
|
||||
<< "Try using different extensions.(e.g. yaml yml xml xml.gz etc...)\n"
|
||||
<< "This will serialize this list of images or whatever with opencv's FileStorage framework" << endl;
|
||||
}
|
||||
|
||||
int main(int ac, char** av)
|
||||
{
|
||||
cv::CommandLineParser parser(ac, av, "{help h||}{@output||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(av);
|
||||
return 0;
|
||||
}
|
||||
string outputname = parser.get<string>("@output");
|
||||
|
||||
if (outputname.empty())
|
||||
{
|
||||
help(av);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Mat m = imread(outputname); //check if the output is an image - prevent overwrites!
|
||||
if(!m.empty()){
|
||||
std::cerr << "fail! Please specify an output file, don't want to overwrite you images!" << endl;
|
||||
help(av);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileStorage fs(outputname, FileStorage::WRITE);
|
||||
fs << "images" << "[";
|
||||
for(int i = 2; i < ac; i++){
|
||||
fs << string(av[i]);
|
||||
}
|
||||
fs << "]";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <opencv2/core/core.hpp>
|
||||
#include <opencv2/highgui/highgui.hpp>
|
||||
#include <opencv2/imgproc/imgproc.hpp>
|
||||
|
||||
using namespace cv;
|
||||
struct Pix
|
||||
{
|
||||
Point next_point;
|
||||
double cost;
|
||||
|
||||
bool operator > (const Pix &b) const
|
||||
{
|
||||
return cost > b.cost;
|
||||
}
|
||||
};
|
||||
|
||||
struct Parameters
|
||||
{
|
||||
Mat img, img_pre_render, img_render;
|
||||
Point end;
|
||||
std::vector<std::vector<Point> > contours;
|
||||
std::vector<Point> tmp_contour;
|
||||
Mat zero_crossing, gradient_magnitude, Ix, Iy, hit_map_x, hit_map_y;
|
||||
};
|
||||
|
||||
|
||||
static float local_cost(const Point& p, const Point& q, const Mat& gradient_magnitude, const Mat& Iy, const Mat& Ix, const Mat& zero_crossing)
|
||||
{
|
||||
float fG = gradient_magnitude.at<float>(q.y, q.x);
|
||||
float dp;
|
||||
float dq;
|
||||
const float WEIGHT_LAP_ZERO_CROSS = 0.43f;
|
||||
const float WEIGHT_GRADIENT_MAGNITUDE = 0.14f;
|
||||
const float WEIGHT_GRADIENT_DIRECTION = 0.43f;
|
||||
bool isDiag = (p.x != q.x) && (p.y != q.y);
|
||||
|
||||
if ((Iy.at<float>(p) * (q.x - p.x) - Ix.at<float>(p) * (q.y - p.y)) >= 0)
|
||||
{
|
||||
dp = Iy.at<float>(p) * (q.x - p.x) - Ix.at<float>(p) * (q.y - p.y);
|
||||
dq = Iy.at<float>(q) * (q.x - p.x) - Ix.at<float>(q) * (q.y - p.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
dp = Iy.at<float>(p) * (p.x - q.x) + (-Ix.at<float>(p)) * (p.y - q.y);
|
||||
dq = Iy.at<float>(q) * (p.x - q.x) + (-Ix.at<float>(q)) * (p.y - q.y);
|
||||
}
|
||||
if (isDiag)
|
||||
{
|
||||
dp /= sqrtf(2);
|
||||
dq /= sqrtf(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
fG /= sqrtf(2);
|
||||
}
|
||||
return WEIGHT_LAP_ZERO_CROSS * zero_crossing.at<uchar>(q) +
|
||||
WEIGHT_GRADIENT_DIRECTION * (acosf(dp) + acosf(dq)) / static_cast<float>(CV_PI) +
|
||||
WEIGHT_GRADIENT_MAGNITUDE * fG;
|
||||
}
|
||||
|
||||
static void find_min_path(const Point& start, Parameters* param)
|
||||
{
|
||||
Pix begin;
|
||||
Mat &img = param->img;
|
||||
Mat cost_map(img.size(), CV_32F, Scalar(FLT_MAX));
|
||||
Mat expand(img.size(), CV_8UC1, Scalar(0));
|
||||
Mat processed(img.size(), CV_8UC1, Scalar(0));
|
||||
Mat removed(img.size(), CV_8UC1, Scalar(0));
|
||||
std::priority_queue < Pix, std::vector<Pix>, std::greater<Pix> > L;
|
||||
|
||||
cost_map.at<float>(start) = 0;
|
||||
processed.at<uchar>(start) = 1;
|
||||
begin.cost = 0;
|
||||
begin.next_point = start;
|
||||
|
||||
L.push(begin);
|
||||
while (!L.empty())
|
||||
{
|
||||
Pix P = L.top();
|
||||
L.pop();
|
||||
Point p = P.next_point;
|
||||
processed.at<uchar>(p) = 0;
|
||||
if (removed.at<uchar>(p) == 0)
|
||||
{
|
||||
expand.at<uchar>(p) = 1;
|
||||
for (int i = -1; i <= 1; i++)
|
||||
{
|
||||
for(int j = -1; j <= 1; j++)
|
||||
{
|
||||
int tx = p.x + i;
|
||||
int ty = p.y + j;
|
||||
if (tx < 0 || tx >= img.cols || ty < 0 || ty >= img.rows)
|
||||
continue;
|
||||
if (expand.at<uchar>(ty, tx) == 0)
|
||||
{
|
||||
Point q = Point(tx, ty);
|
||||
float cost = cost_map.at<float>(p) + local_cost(p, q, param->gradient_magnitude, param->Iy, param->Ix, param->zero_crossing);
|
||||
if (processed.at<uchar>(q) == 1 && cost < cost_map.at<float>(q))
|
||||
{
|
||||
removed.at<uchar>(q) = 1;
|
||||
}
|
||||
if (processed.at<uchar>(q) == 0)
|
||||
{
|
||||
cost_map.at<float>(q) = cost;
|
||||
param->hit_map_x.at<int>(q)= p.x;
|
||||
param->hit_map_y.at<int>(q) = p.y;
|
||||
processed.at<uchar>(q) = 1;
|
||||
Pix val;
|
||||
val.cost = cost_map.at<float>(q);
|
||||
val.next_point = q;
|
||||
L.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void onMouse(int event, int x, int y, int , void* userdata)
|
||||
{
|
||||
Parameters* param = reinterpret_cast<Parameters*>(userdata);
|
||||
Point &end = param->end;
|
||||
std::vector<std::vector<Point> > &contours = param->contours;
|
||||
std::vector<Point> &tmp_contour = param->tmp_contour;
|
||||
Mat &img_render = param->img_render;
|
||||
Mat &img_pre_render = param->img_pre_render;
|
||||
|
||||
if (event == EVENT_LBUTTONDOWN)
|
||||
{
|
||||
end = Point(x, y);
|
||||
if (!contours.back().empty())
|
||||
{
|
||||
for (int i = static_cast<int>(tmp_contour.size()) - 1; i >= 0; i--)
|
||||
{
|
||||
contours.back().push_back(tmp_contour[i]);
|
||||
}
|
||||
tmp_contour.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
contours.back().push_back(end);
|
||||
}
|
||||
find_min_path(end, param);
|
||||
|
||||
img_render.copyTo(img_pre_render);
|
||||
imshow("lasso", img_render);
|
||||
}
|
||||
else if (event == EVENT_RBUTTONDOWN)
|
||||
{
|
||||
img_pre_render.copyTo(img_render);
|
||||
drawContours(img_pre_render, contours, static_cast<int>(contours.size()) - 1, Scalar(0,255,0), FILLED);
|
||||
addWeighted(img_pre_render, 0.3, img_render, 0.7, 0, img_render);
|
||||
contours.resize(contours.size() + 1);
|
||||
imshow("lasso", img_render);
|
||||
}
|
||||
else if (event == EVENT_MOUSEMOVE && !contours.back().empty())
|
||||
{
|
||||
tmp_contour.clear();
|
||||
img_pre_render.copyTo(img_render);
|
||||
Point val_point = Point(x, y);
|
||||
while (val_point != end)
|
||||
{
|
||||
tmp_contour.push_back(val_point);
|
||||
Point cur = Point(param->hit_map_x.at<int>(val_point), param->hit_map_y.at<int>(val_point));
|
||||
line(img_render, val_point, cur, Scalar(255, 0, 0), 2);
|
||||
val_point = cur;
|
||||
}
|
||||
imshow("lasso", img_render);
|
||||
}
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h | |}"
|
||||
"{@image | fruits.jpg| Path to image to process}"
|
||||
};
|
||||
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
Parameters param;
|
||||
const int EDGE_THRESHOLD_LOW = 50;
|
||||
const int EDGE_THRESHOLD_HIGH = 100;
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("\nThis program demonstrates implementation of 'Intelligent Scissors' algorithm designed\n"
|
||||
"by Eric N. Mortensen and William A. Barrett, and described in article\n"
|
||||
"'Intelligent Scissors for Image Composition':\n"
|
||||
"http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.138.3811&rep=rep1&type=pdf\n"
|
||||
"To start drawing a new contour select a pixel, click LEFT mouse button.\n"
|
||||
"To fix a path click LEFT mouse button again.\n"
|
||||
"To finish drawing a contour click RIGHT mouse button.\n");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 1;
|
||||
}
|
||||
std::vector<std::vector<Point> > c(1);
|
||||
param.contours = c;
|
||||
std::string filename = parser.get<std::string>(0);
|
||||
|
||||
Mat grayscale, img_canny;
|
||||
param.img = imread(samples::findFile(filename));
|
||||
param.hit_map_x.create(param.img.rows, param.img.cols, CV_32SC1);
|
||||
param.hit_map_y.create(param.img.rows, param.img.cols, CV_32SC1);
|
||||
|
||||
cvtColor(param.img, grayscale, COLOR_BGR2GRAY);
|
||||
Canny(grayscale, img_canny, EDGE_THRESHOLD_LOW, EDGE_THRESHOLD_HIGH);
|
||||
|
||||
threshold(img_canny, param.zero_crossing, 254, 1, THRESH_BINARY_INV);
|
||||
Sobel(grayscale, param.Ix, CV_32FC1, 1, 0, 1);
|
||||
Sobel(grayscale, param.Iy, CV_32FC1, 0, 1, 1);
|
||||
param.Ix.convertTo(param.Ix, CV_32F, 1.0/255);
|
||||
param.Iy.convertTo(param.Iy, CV_32F, 1.0/255);
|
||||
|
||||
// Compute gradients magnitude.
|
||||
double max_val = 0.0;
|
||||
magnitude(param.Iy, param.Ix, param.gradient_magnitude);
|
||||
minMaxLoc(param.gradient_magnitude, 0, &max_val);
|
||||
param.gradient_magnitude.convertTo(param.gradient_magnitude, CV_32F, -1/max_val, 1.0);
|
||||
|
||||
param.img.copyTo(param.img_pre_render);
|
||||
param.img.copyTo(param.img_render);
|
||||
|
||||
namedWindow("lasso");
|
||||
setMouseCallback("lasso", onMouse, ¶m);
|
||||
imshow("lasso", param.img);
|
||||
waitKey(0);
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help()
|
||||
{
|
||||
// print a welcome message, and the OpenCV version
|
||||
cout << "\nThis is a demo of Lukas-Kanade optical flow lkdemo(),\n"
|
||||
"Using OpenCV version " << CV_VERSION << endl;
|
||||
cout << "\nIt uses camera by default, but you can provide a path to video as an argument.\n";
|
||||
cout << "\nHot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tr - auto-initialize tracking\n"
|
||||
"\tc - delete all the points\n"
|
||||
"\tn - switch the \"night\" mode on/off\n"
|
||||
"To add/remove a feature point click it\n" << endl;
|
||||
}
|
||||
|
||||
Point2f point;
|
||||
bool addRemovePt = false;
|
||||
|
||||
static void onMouse( int event, int x, int y, int /*flags*/, void* /*param*/ )
|
||||
{
|
||||
if( event == EVENT_LBUTTONDOWN )
|
||||
{
|
||||
point = Point2f((float)x, (float)y);
|
||||
addRemovePt = true;
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
VideoCapture cap;
|
||||
TermCriteria termcrit(TermCriteria::COUNT|TermCriteria::EPS,20,0.03);
|
||||
Size subPixWinSize(10,10), winSize(31,31);
|
||||
|
||||
const int MAX_COUNT = 500;
|
||||
bool needToInit = false;
|
||||
bool nightMode = false;
|
||||
|
||||
help();
|
||||
cv::CommandLineParser parser(argc, argv, "{@input|0|}");
|
||||
string input = parser.get<string>("@input");
|
||||
|
||||
if( input.size() == 1 && isdigit(input[0]) )
|
||||
cap.open(input[0] - '0');
|
||||
else
|
||||
cap.open(input);
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
cout << "Could not initialize capturing...\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
namedWindow( "LK Demo", 1 );
|
||||
setMouseCallback( "LK Demo", onMouse, 0 );
|
||||
|
||||
Mat gray, prevGray, image, frame;
|
||||
vector<Point2f> points[2];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
cap >> frame;
|
||||
if( frame.empty() )
|
||||
break;
|
||||
|
||||
frame.copyTo(image);
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
|
||||
if( nightMode )
|
||||
image = Scalar::all(0);
|
||||
|
||||
if( needToInit )
|
||||
{
|
||||
// automatic initialization
|
||||
goodFeaturesToTrack(gray, points[1], MAX_COUNT, 0.01, 10, Mat(), 3, 3, 0, 0.04);
|
||||
cornerSubPix(gray, points[1], subPixWinSize, Size(-1,-1), termcrit);
|
||||
addRemovePt = false;
|
||||
}
|
||||
else if( !points[0].empty() )
|
||||
{
|
||||
vector<uchar> status;
|
||||
vector<float> err;
|
||||
if(prevGray.empty())
|
||||
gray.copyTo(prevGray);
|
||||
calcOpticalFlowPyrLK(prevGray, gray, points[0], points[1], status, err, winSize,
|
||||
3, termcrit, 0, 0.001);
|
||||
size_t i, k;
|
||||
for( i = k = 0; i < points[1].size(); i++ )
|
||||
{
|
||||
if( addRemovePt )
|
||||
{
|
||||
if( norm(point - points[1][i]) <= 5 )
|
||||
{
|
||||
addRemovePt = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if( !status[i] )
|
||||
continue;
|
||||
|
||||
points[1][k++] = points[1][i];
|
||||
circle( image, points[1][i], 3, Scalar(0,255,0), -1, 8);
|
||||
}
|
||||
points[1].resize(k);
|
||||
}
|
||||
|
||||
if( addRemovePt && points[1].size() < (size_t)MAX_COUNT )
|
||||
{
|
||||
vector<Point2f> tmp;
|
||||
tmp.push_back(point);
|
||||
cornerSubPix( gray, tmp, winSize, Size(-1,-1), termcrit);
|
||||
points[1].push_back(tmp[0]);
|
||||
addRemovePt = false;
|
||||
}
|
||||
|
||||
needToInit = false;
|
||||
imshow("LK Demo", image);
|
||||
|
||||
char c = (char)waitKey(10);
|
||||
if( c == 27 )
|
||||
break;
|
||||
switch( c )
|
||||
{
|
||||
case 'r':
|
||||
needToInit = true;
|
||||
break;
|
||||
case 'c':
|
||||
points[0].clear();
|
||||
points[1].clear();
|
||||
break;
|
||||
case 'n':
|
||||
nightMode = !nightMode;
|
||||
break;
|
||||
}
|
||||
|
||||
std::swap(points[1], points[0]);
|
||||
cv::swap(prevGray, gray);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
#include <opencv2/dnn.hpp>
|
||||
#include "../dnn/common.hpp"
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
using namespace cv::dnn;
|
||||
#endif
|
||||
using namespace mcc;
|
||||
|
||||
const string about =
|
||||
"This sample demonstrates mcc checker detection with DNN based model and thresholding (default) techniques.\n\n"
|
||||
"To run default:\n"
|
||||
"\t ./example_cpp_macbeth_chart_detection --input=path/to/your/input/image/or/video (don't give --input flag if want to use device camera)\n"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
"With DNN model:\n"
|
||||
"\t ./example_cpp_macbeth_chart_detection mcc --input=path/to/your/input/image/or/video\n\n"
|
||||
"Model path can also be specified using --model argument. And config path can be specified using --config. Download it using python download_models.py mcc from dnn samples directory\n\n"
|
||||
#else
|
||||
"Note: DNN-based detection is not available in this build.\n\n"
|
||||
#endif
|
||||
;
|
||||
|
||||
const string param_keys =
|
||||
"{ help h | | Print help message. }"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
"{ @alias | | An alias name of model to extract preprocessing parameters from models.yml file. }"
|
||||
"{ zoo | ../dnn/models.yml | An optional path to file with preprocessing parameters }"
|
||||
#endif
|
||||
"{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera.}"
|
||||
"{ type | 0 | chartType: 0-Standard, 1-DigitalSG, 2-Vinyl, default:0 }"
|
||||
"{ num_charts | 1 | Maximum number of charts in the image }"
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
"{ model | | Path to the model file for using dnn model. }";
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
const string backend_keys = format(
|
||||
"{ backend | default | Choose one of computation backends: "
|
||||
"default: automatically (by default), "
|
||||
"openvino: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), "
|
||||
"opencv: OpenCV implementation, "
|
||||
"vkcom: VKCOM, "
|
||||
"cuda: CUDA, "
|
||||
"webnn: WebNN }");
|
||||
|
||||
const string target_keys = format(
|
||||
"{ target | cpu | Choose one of target computation devices: "
|
||||
"cpu: CPU target (by default), "
|
||||
"opencl: OpenCL, "
|
||||
"opencl_fp16: OpenCL fp16 (half-float precision), "
|
||||
"vpu: VPU, "
|
||||
"vulkan: Vulkan, "
|
||||
"cuda: CUDA, "
|
||||
"cuda_fp16: CUDA fp16 (half-float preprocess) }");
|
||||
#endif
|
||||
|
||||
// Initialize keys before use
|
||||
string keys = param_keys;
|
||||
static void initKeys() {
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
keys += backend_keys + target_keys;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool processFrame(const Mat& frame, Ptr<CCheckerDetector> detector, Mat& src, Mat& tgt, int nc){
|
||||
Mat imageCopy = frame.clone();
|
||||
if (!detector->process(frame, nc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
vector<Ptr<CChecker>> checkers = detector->getListColorChecker();
|
||||
detector->draw(checkers, frame);
|
||||
src = checkers[0]->getChartsRGB(false);
|
||||
tgt = detector->getRefColors();
|
||||
|
||||
imshow("Image result", frame);
|
||||
imshow("Original", imageCopy);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
initKeys();
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about(about);
|
||||
|
||||
if (parser.has("help"))
|
||||
{
|
||||
cout << about << endl;
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
string modelName = parser.get<String>("@alias");
|
||||
string zooFile = parser.get<String>("zoo");
|
||||
const char* path = getenv("OPENCV_SAMPLES_DATA_PATH");
|
||||
if ((path != NULL) || parser.has("@alias") || (parser.get<String>("model") != "")) {
|
||||
zooFile = findFile(zooFile);
|
||||
}
|
||||
else{
|
||||
cout<<"[WARN] set the environment variables or pass the arguments --model, --config and models.yml file using --zoo for using dnn based detector. Continuing with default detector.\n\n";
|
||||
}
|
||||
|
||||
keys += genPreprocArguments(modelName, zooFile);
|
||||
parser = CommandLineParser(argc, argv, keys);
|
||||
#endif
|
||||
|
||||
int t = parser.get<int>("type");
|
||||
|
||||
CV_Assert(0 <= t && t <= 2);
|
||||
ColorChart chartType = ColorChart(t);
|
||||
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
const string sha1 = parser.get<String>("sha1");
|
||||
const string model_path = findModel(parser.get<string>("model"), sha1);
|
||||
const string config_sha1 = parser.get<String>("config_sha1");
|
||||
const string pbtxt_path = findModel(parser.get<string>("config"), config_sha1);
|
||||
const string backend = parser.get<String>("backend");
|
||||
const string target = parser.get<String>("target");
|
||||
#endif
|
||||
|
||||
int nc = parser.get<int>("num_charts");
|
||||
|
||||
Ptr<CCheckerDetector> detector;
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
if (model_path != "" && pbtxt_path != ""){
|
||||
EngineType engine = ENGINE_AUTO;
|
||||
if (backend != "default" || target != "cpu"){
|
||||
engine = ENGINE_CLASSIC;
|
||||
}
|
||||
Net net = readNetFromTensorflow(model_path, pbtxt_path, engine);
|
||||
net.setPreferableBackend(getBackendID(backend));
|
||||
net.setPreferableTarget(getTargetID(target));
|
||||
|
||||
detector = CCheckerDetector::create(net);
|
||||
cout<<"Detecting checkers using neural network."<<endl;
|
||||
}
|
||||
else{
|
||||
detector = CCheckerDetector::create();
|
||||
}
|
||||
#else
|
||||
detector = CCheckerDetector::create();
|
||||
#endif
|
||||
detector->setColorChartType(chartType);
|
||||
|
||||
bool isVideo = true;
|
||||
Mat image;
|
||||
VideoCapture cap;
|
||||
|
||||
if (parser.has("input")){
|
||||
const string inputFile = parser.get<String>("input");
|
||||
image = imread(findFile(inputFile));
|
||||
if (!image.empty())
|
||||
{
|
||||
isVideo = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not an image, so try opening it as a video.
|
||||
cap.open(findFile(inputFile));
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "[ERROR] Could not open file as an image or video: " << inputFile << endl;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
cap.open(0);
|
||||
|
||||
Mat src, tgt;
|
||||
bool found = false;
|
||||
if (isVideo){
|
||||
cout<<"To print the actual colors and reference colors for current frame press SPACEBAR. To resume press SPACEBAR again"<<endl;
|
||||
|
||||
while (cap.grab())
|
||||
{
|
||||
Mat frame;
|
||||
cap.retrieve(frame);
|
||||
|
||||
found = processFrame(frame, detector, src, tgt, nc);
|
||||
|
||||
int key = waitKey(10);
|
||||
if (key == ' '){
|
||||
if(found){
|
||||
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
|
||||
cout<<"Actual colors: "<<src<<endl<<endl;
|
||||
cout<<"Press spacebar to resume."<<endl;
|
||||
|
||||
waitKey(0);
|
||||
cout << "Resumed! Processing continues..." << endl;
|
||||
}
|
||||
else{
|
||||
cout<<"No color chart detected!!"<<endl;
|
||||
}
|
||||
}
|
||||
else if (key == 27) exit(0);
|
||||
}
|
||||
if(found){
|
||||
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
|
||||
cout<<"Actual colors: "<<src<<endl<<endl;
|
||||
}
|
||||
}
|
||||
else{
|
||||
found = processFrame(image, detector, src, tgt, nc);
|
||||
if(found){
|
||||
cout<<"Reference colors: "<<tgt<<endl<<"--------------------"<<endl;
|
||||
cout<<"Actual colors: "<<src<<endl<<endl;
|
||||
waitKey(0);
|
||||
}
|
||||
else{
|
||||
cout<<"No chart detected!!"<<endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
|
||||
printf("\nShow off image morphology: erosion, dialation, open and close\n"
|
||||
"Call:\n %s [image]\n"
|
||||
"This program also shows use of rect, ellipse, cross and diamond kernels\n\n", argv[0]);
|
||||
printf( "Hot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tr - use rectangle structuring element\n"
|
||||
"\te - use elliptic structuring element\n"
|
||||
"\tc - use cross-shaped structuring element\n"
|
||||
"\td - use diamond-shaped structuring element\n"
|
||||
"\tSPACE - loop through all the options\n" );
|
||||
}
|
||||
|
||||
Mat src, dst;
|
||||
|
||||
int element_shape = MORPH_RECT;
|
||||
|
||||
//the address of variable which receives trackbar position update
|
||||
int max_iters = 10;
|
||||
int open_close_pos = 0;
|
||||
int erode_dilate_pos = 0;
|
||||
|
||||
// callback function for open/close trackbar
|
||||
static void OpenClose(int, void*)
|
||||
{
|
||||
int n = open_close_pos;
|
||||
int an = abs(n);
|
||||
Mat element = getStructuringElement(element_shape, Size(an*2+1, an*2+1), Point(an, an) );
|
||||
if( n < 0 )
|
||||
morphologyEx(src, dst, MORPH_OPEN, element);
|
||||
else
|
||||
morphologyEx(src, dst, MORPH_CLOSE, element);
|
||||
imshow("Open/Close",dst);
|
||||
}
|
||||
|
||||
// callback function for erode/dilate trackbar
|
||||
static void ErodeDilate(int, void*)
|
||||
{
|
||||
int n = erode_dilate_pos;
|
||||
int an = abs(n);
|
||||
Mat element = getStructuringElement(element_shape, Size(an*2+1, an*2+1), Point(an, an) );
|
||||
if( n < 0 )
|
||||
erode(src, dst, element);
|
||||
else
|
||||
dilate(src, dst, element);
|
||||
imshow("Erode/Dilate",dst);
|
||||
}
|
||||
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||}{ @image | baboon.jpg | }");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
std::string filename = samples::findFile(parser.get<std::string>("@image"));
|
||||
if( (src = imread(filename,IMREAD_COLOR)).empty() )
|
||||
{
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//create windows for output images
|
||||
namedWindow("Open/Close",1);
|
||||
namedWindow("Erode/Dilate",1);
|
||||
|
||||
open_close_pos = erode_dilate_pos = max_iters;
|
||||
createTrackbar("iterations", "Open/Close",&open_close_pos,max_iters*2+1,OpenClose);
|
||||
setTrackbarMin("iterations", "Open/Close", -max_iters);
|
||||
setTrackbarMax("iterations", "Open/Close", max_iters);
|
||||
setTrackbarPos("iterations", "Open/Close", 0);
|
||||
|
||||
createTrackbar("iterations", "Erode/Dilate",&erode_dilate_pos,max_iters*2+1,ErodeDilate);
|
||||
setTrackbarMin("iterations", "Erode/Dilate", -max_iters);
|
||||
setTrackbarMax("iterations", "Erode/Dilate", max_iters);
|
||||
setTrackbarPos("iterations", "Erode/Dilate", 0);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
OpenClose(open_close_pos, 0);
|
||||
ErodeDilate(erode_dilate_pos, 0);
|
||||
char c = (char)waitKey(0);
|
||||
|
||||
if( c == 27 )
|
||||
break;
|
||||
if( c == 'e' )
|
||||
element_shape = MORPH_ELLIPSE;
|
||||
else if( c == 'r' )
|
||||
element_shape = MORPH_RECT;
|
||||
else if( c == 'c' )
|
||||
element_shape = MORPH_CROSS;
|
||||
else if( c == 'd' )
|
||||
element_shape = MORPH_DIAMOND;
|
||||
else if( c == ' ' )
|
||||
element_shape = (element_shape + 1) % 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <opencv2/calib3d.hpp>
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/objdetect.hpp>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
// ! [detectPointsAndCalibrate_signature]
|
||||
static void detectPointsAndCalibrate (cv::Size pattern_size, float pattern_distance, const std::string &pattern_type,
|
||||
const std::vector<uchar> &models, const std::vector<std::string> &filenames,
|
||||
const cv::String* dict_path=nullptr)
|
||||
// ! [detectPointsAndCalibrate_signature]
|
||||
{
|
||||
// ! [calib_init]
|
||||
std::vector<cv::Point3f> board (pattern_size.area());
|
||||
const int num_cameras = (int)models.size();
|
||||
std::vector<std::vector<cv::Mat>> image_points_all;
|
||||
std::vector<cv::Size> image_sizes;
|
||||
std::vector<cv::Mat> Ks, distortions, Ts, Rs;
|
||||
if (pattern_type == "checkerboard" || pattern_type == "charuco") {
|
||||
for (int i = 0; i < pattern_size.height; i++) {
|
||||
for (int j = 0; j < pattern_size.width; j++) {
|
||||
board[i*pattern_size.width+j] = cv::Point3f((float)j, (float)i, 0.f) * pattern_distance;
|
||||
}
|
||||
}
|
||||
} else if (pattern_type == "circles") {
|
||||
for (int i = 0; i < pattern_size.height; i++) {
|
||||
for (int j = 0; j < pattern_size.width; j++) {
|
||||
board[i*pattern_size.width+j] = cv::Point3f((float)j, (float)i, 0.f) * pattern_distance;
|
||||
}
|
||||
}
|
||||
} else if (pattern_type == "acircles") {
|
||||
for (int i = 0; i < pattern_size.height; i++) {
|
||||
for (int j = 0; j < pattern_size.width; j++) {
|
||||
if (i % 2 == 1) {
|
||||
board[i*pattern_size.width+j] = cv::Point3f((j + .5f)*pattern_distance, (i/2 + .5f) * pattern_distance, 0.f);
|
||||
} else{
|
||||
board[i*pattern_size.width+j] = cv::Point3f(j*pattern_distance, (i/2)*pattern_distance, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
CV_Error(cv::Error::StsNotImplemented, "pattern_type is not implemented!");
|
||||
}
|
||||
// ! [calib_init]
|
||||
// ! [charuco_detector]
|
||||
cv::Ptr<cv::aruco::CharucoDetector> detector;
|
||||
if (pattern_type == "charuco") {
|
||||
CV_Assert(dict_path != nullptr);
|
||||
cv::FileStorage fs(*dict_path, cv::FileStorage::READ);
|
||||
CV_Assert(fs.isOpened());
|
||||
|
||||
int dict_int;
|
||||
double square_size, marker_size;
|
||||
fs["dictionary"] >> dict_int;
|
||||
fs["square_size"] >> square_size;
|
||||
fs["marker_size"] >> marker_size;
|
||||
|
||||
auto dictionary = cv::aruco::getPredefinedDictionary(dict_int);
|
||||
// For charuco board, the size is defined to be the number of box (not inner corner)
|
||||
auto charuco_board = cv::aruco::CharucoBoard(
|
||||
cv::Size(pattern_size.width+1, pattern_size.height+1),
|
||||
static_cast<float>(square_size), static_cast<float>(marker_size), dictionary);
|
||||
|
||||
// It is suggested to use refinement in detecting charuco board
|
||||
auto detector_params = cv::aruco::DetectorParameters();
|
||||
auto charuco_params = cv::aruco::CharucoParameters();
|
||||
charuco_params.tryRefineMarkers = true;
|
||||
detector_params.cornerRefinementMethod = cv::aruco::CORNER_REFINE_CONTOUR;
|
||||
detector = cv::makePtr<cv::aruco::CharucoDetector>(charuco_board, charuco_params, detector_params);
|
||||
}
|
||||
// ! [charuco_detector]
|
||||
// ! [detect_pattern]
|
||||
int num_frames = -1;
|
||||
for (const auto &filename : filenames) {
|
||||
std::fstream file(filename);
|
||||
CV_Assert(file.is_open());
|
||||
std::string img_file;
|
||||
std::vector<cv::Mat> image_points_cameras;
|
||||
bool save_img_size = true;
|
||||
while (std::getline(file, img_file)) {
|
||||
if (img_file.empty()){
|
||||
image_points_cameras.emplace_back(cv::Mat());
|
||||
continue;
|
||||
}
|
||||
cv::Mat img = cv::imread(img_file), corners;
|
||||
if (save_img_size) {
|
||||
image_sizes.emplace_back(cv::Size(img.cols, img.rows));
|
||||
save_img_size = false;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
if (pattern_type == "checkerboard") {
|
||||
cv::cvtColor(img, img, cv::COLOR_BGR2GRAY);
|
||||
success = cv::findChessboardCorners(img, pattern_size, corners);
|
||||
}
|
||||
else if (pattern_type == "circles")
|
||||
{
|
||||
success = cv::findCirclesGrid(img, pattern_size, corners, cv::CALIB_CB_SYMMETRIC_GRID);
|
||||
}
|
||||
else if (pattern_type == "acircles")
|
||||
{
|
||||
success = cv::findCirclesGrid(img, pattern_size, corners, cv::CALIB_CB_ASYMMETRIC_GRID);
|
||||
}
|
||||
else if (pattern_type == "charuco")
|
||||
{
|
||||
std::vector<int> ids; cv::Mat corners_sub;
|
||||
detector->detectBoard(img, corners_sub, ids);
|
||||
corners.create(static_cast<int>(board.size()), 2, CV_32F);
|
||||
if (ids.size() < 4)
|
||||
success = false;
|
||||
else {
|
||||
success = true;
|
||||
int head = 0;
|
||||
for (int i = 0; i < static_cast<int>(board.size()); i++) {
|
||||
if (head < static_cast<int>(ids.size()) && ids[head] == i) {
|
||||
corners.at<float>(i, 0) = corners_sub.at<float>(head, 0);
|
||||
corners.at<float>(i, 1) = corners_sub.at<float>(head, 1);
|
||||
head++;
|
||||
} else {
|
||||
// points outside of frame border are dropped by calibrateMultiview
|
||||
corners.at<float>(i, 0) = -1.;
|
||||
corners.at<float>(i, 1) = -1.;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cv::Mat corners2;
|
||||
corners.convertTo(corners2, CV_32FC2);
|
||||
|
||||
if (success && corners.rows == pattern_size.area())
|
||||
image_points_cameras.emplace_back(corners2);
|
||||
else
|
||||
image_points_cameras.emplace_back(cv::Mat());
|
||||
}
|
||||
if (num_frames == -1)
|
||||
num_frames = (int)image_points_cameras.size();
|
||||
else
|
||||
CV_Assert(num_frames == (int)image_points_cameras.size());
|
||||
image_points_all.emplace_back(image_points_cameras);
|
||||
}
|
||||
// ! [detect_pattern]
|
||||
// ! [detection_matrix]
|
||||
cv::Mat visibility(num_cameras, num_frames, CV_8UC1);
|
||||
for (int i = 0; i < num_cameras; i++) {
|
||||
for (int j = 0; j < num_frames; j++) {
|
||||
visibility.at<unsigned char>(i,j) = image_points_all[i][j].empty() ? 0 : 1;
|
||||
}
|
||||
}
|
||||
// ! [detection_matrix]
|
||||
CV_Assert(num_frames != -1);
|
||||
|
||||
std::vector<std::vector<cv::Point3f>> objPoints(num_frames, board);
|
||||
|
||||
// ! [multiview_calib]
|
||||
|
||||
const double rmse = calibrateMultiview(objPoints, image_points_all, image_sizes, visibility,
|
||||
models, Ks, distortions, Rs, Ts);
|
||||
// ! [multiview_calib]
|
||||
std::cout << "average RMSE over detection mask " << rmse << "\n";
|
||||
for (int c = 0; c < (int)Rs.size(); c++) {
|
||||
std::cout << "camera " << c << '\n';
|
||||
std::cout << "rotation\n" << Rs[c] << "\n";
|
||||
std::cout << "translation\n" << Ts[c] << "\n";
|
||||
std::cout << "intrinsic matrix\n" << Ks[c] << "\n";
|
||||
std::cout << "distortion\n" << distortions[c] << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
cv::String keys =
|
||||
"{help h usage ? || print help }"
|
||||
"{pattern_size || (inner) grid width, (inner) grid height }"
|
||||
"{pattern_distance || pattern scale}"
|
||||
"{pattern_type | checkerboard | pattern type, e.g., checkerboard or acircles or charuco (recommended)}"
|
||||
"{is_fisheye || cameras type fisheye (1), pinhole(0), separated by comma (no space)}"
|
||||
"{filenames || files containing path to image names separated by comma (no space)}"
|
||||
"{board_dict_path || file containing dictionary information (required field: dictionary, square_size, marker_size). Needed if pattern_type is charuco.}";
|
||||
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help")) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
CV_Assert(parser.has("pattern_size") && parser.has("pattern_type") &&
|
||||
parser.has("is_fisheye") && parser.has("filenames"));
|
||||
CV_Assert(parser.get<cv::String>("pattern_type") == "checkerboard" ||
|
||||
parser.get<cv::String>("pattern_type") == "circles" ||
|
||||
parser.get<cv::String>("pattern_type") == "acircles" ||
|
||||
parser.get<cv::String>("pattern_type") == "charuco"
|
||||
);
|
||||
if (parser.get<cv::String>("pattern_type") == "charuco")
|
||||
CV_Assert(parser.has("board_dict_path"));
|
||||
|
||||
cv::Size pattern_size;
|
||||
const cv::String pattern_size_str = parser.get<cv::String>("pattern_size");
|
||||
std::string temp_str;
|
||||
int pattern_size_count = 0;
|
||||
for (char i : pattern_size_str) {
|
||||
if (i == ',') {
|
||||
if (pattern_size_count == 0)
|
||||
pattern_size.width = std::stoi(temp_str);
|
||||
pattern_size_count++;
|
||||
temp_str = "";
|
||||
} else {
|
||||
temp_str += i;
|
||||
}
|
||||
}
|
||||
CV_Assert(pattern_size_count == 1);
|
||||
pattern_size.height = std::stoi(temp_str);
|
||||
|
||||
std::vector<uchar> models;
|
||||
const cv::String is_fisheye_str = parser.get<cv::String>("is_fisheye");
|
||||
for (char i : is_fisheye_str) {
|
||||
if (i == '0') {
|
||||
models.push_back(cv::CALIB_MODEL_PINHOLE);
|
||||
} else if (i == '1') {
|
||||
models.push_back(cv::CALIB_MODEL_FISHEYE);
|
||||
}
|
||||
}
|
||||
const cv::String filenames_str = parser.get<cv::String>("filenames");
|
||||
std::vector<std::string> filenames;
|
||||
temp_str = "";
|
||||
for (char i : filenames_str) {
|
||||
if (i == ',') {
|
||||
filenames.emplace_back(temp_str);
|
||||
temp_str = "";
|
||||
} else {
|
||||
temp_str += i;
|
||||
}
|
||||
}
|
||||
filenames.emplace_back(temp_str);
|
||||
CV_CheckEQ(filenames.size(), models.size(), "filenames size must be equal to number of cameras!");
|
||||
|
||||
if (parser.has("board_dict_path")) {
|
||||
cv::String board_dict_path = parser.get<cv::String>("board_dict_path");
|
||||
detectPointsAndCalibrate (pattern_size, parser.get<float>("pattern_distance"), parser.get<cv::String>("pattern_type"), models, filenames, &board_dict_path);
|
||||
} else {
|
||||
detectPointsAndCalibrate (pattern_size, parser.get<float>("pattern_distance"), parser.get<cv::String>("pattern_type"), models, filenames);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* npr_demo.cpp
|
||||
*
|
||||
* Author:
|
||||
* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
|
||||
*
|
||||
* This tutorial demonstrates how to use OpenCV Non-Photorealistic Rendering Module.
|
||||
* 1) Edge Preserve Smoothing
|
||||
* -> Using Normalized convolution Filter
|
||||
* -> Using Recursive Filter
|
||||
* 2) Detail Enhancement
|
||||
* 3) Pencil sketch/Color Pencil Drawing
|
||||
* 4) Stylization
|
||||
*
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||show help message}{@image|lena.jpg|input image}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
string filename = samples::findFile(parser.get<string>("@image"));
|
||||
|
||||
Mat I = imread(filename);
|
||||
|
||||
int num,type;
|
||||
|
||||
if(I.empty())
|
||||
{
|
||||
cout << "Image not found" << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
cout << " Edge Preserve Filter" << endl;
|
||||
cout << "----------------------" << endl;
|
||||
|
||||
cout << "Options: " << endl;
|
||||
cout << endl;
|
||||
|
||||
cout << "1) Edge Preserve Smoothing" << endl;
|
||||
cout << " -> Using Normalized convolution Filter" << endl;
|
||||
cout << " -> Using Recursive Filter" << endl;
|
||||
cout << "2) Detail Enhancement" << endl;
|
||||
cout << "3) Pencil sketch/Color Pencil Drawing" << endl;
|
||||
cout << "4) Stylization" << endl;
|
||||
cout << endl;
|
||||
|
||||
cout << "Press number 1-4 to choose from above techniques: ";
|
||||
|
||||
cin >> num;
|
||||
|
||||
Mat img;
|
||||
|
||||
if(num == 1)
|
||||
{
|
||||
cout << endl;
|
||||
cout << "Press 1 for Normalized Convolution Filter and 2 for Recursive Filter: ";
|
||||
|
||||
cin >> type;
|
||||
|
||||
edgePreservingFilter(I,img,type);
|
||||
imshow("Edge Preserve Smoothing",img);
|
||||
|
||||
}
|
||||
else if(num == 2)
|
||||
{
|
||||
detailEnhance(I,img);
|
||||
imshow("Detail Enhanced",img);
|
||||
}
|
||||
else if(num == 3)
|
||||
{
|
||||
Mat img1;
|
||||
pencilSketch(I,img1, img, 10 , 0.1f, 0.03f);
|
||||
imshow("Pencil Sketch",img1);
|
||||
imshow("Color Pencil Sketch",img);
|
||||
}
|
||||
else if(num == 4)
|
||||
{
|
||||
stylization(I,img);
|
||||
imshow("Stylization",img);
|
||||
}
|
||||
waitKey(0);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include <iostream>
|
||||
|
||||
static const std::string keys = "{ b build | | print complete build info }"
|
||||
"{ h help | | print this help }";
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("This sample outputs OpenCV version and build configuration.");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
}
|
||||
else if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
}
|
||||
else if (parser.has("build"))
|
||||
{
|
||||
std::cout << cv::getBuildInformation() << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Welcome to OpenCV " << CV_VERSION << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* pca.cpp
|
||||
*
|
||||
* Author:
|
||||
* Kevin Hughes <kevinhughes27[at]gmail[dot]com>
|
||||
*
|
||||
* Special Thanks to:
|
||||
* Philipp Wagner <bytefish[at]gmx[dot]de>
|
||||
*
|
||||
* This program demonstrates how to use OpenCV PCA with a
|
||||
* specified amount of variance to retain. The effect
|
||||
* is illustrated further by using a trackbar to
|
||||
* change the value for retained variance.
|
||||
*
|
||||
* The program takes as input a text file with each line
|
||||
* begin the full path to an image. PCA will be performed
|
||||
* on this list of images. The author recommends using
|
||||
* the first 15 faces of the AT&T face data set:
|
||||
* http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html
|
||||
*
|
||||
* so for example your input text file would look like this:
|
||||
*
|
||||
* <path_to_at&t_faces>/orl_faces/s1/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s2/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s3/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s4/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s5/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s6/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s7/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s8/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s9/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s10/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s11/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s12/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s13/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s14/1.pgm
|
||||
* <path_to_at&t_faces>/orl_faces/s15/1.pgm
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
///////////////////////
|
||||
// Functions
|
||||
static void read_imgList(const string& filename, vector<Mat>& images) {
|
||||
std::ifstream file(filename.c_str(), ifstream::in);
|
||||
if (!file) {
|
||||
string error_message = "No valid input file was given, please check the given filename.";
|
||||
CV_Error(Error::StsBadArg, error_message);
|
||||
}
|
||||
string line;
|
||||
while (getline(file, line)) {
|
||||
images.push_back(imread(line, IMREAD_GRAYSCALE));
|
||||
}
|
||||
}
|
||||
|
||||
static Mat formatImagesForPCA(const vector<Mat> &data)
|
||||
{
|
||||
Mat dst(static_cast<int>(data.size()), data[0].rows*data[0].cols, CV_32F);
|
||||
for(unsigned int i = 0; i < data.size(); i++)
|
||||
{
|
||||
Mat image_row = data[i].clone().reshape(1,1);
|
||||
Mat row_i = dst.row(i);
|
||||
image_row.convertTo(row_i,CV_32F);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
static Mat toGrayscale(InputArray _src) {
|
||||
Mat src = _src.getMat();
|
||||
// only allow one channel
|
||||
if(src.channels() != 1) {
|
||||
CV_Error(Error::StsBadArg, "Only Matrices with one channel are supported");
|
||||
}
|
||||
// create and return normalized image
|
||||
Mat dst;
|
||||
cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
|
||||
return dst;
|
||||
}
|
||||
|
||||
struct params
|
||||
{
|
||||
Mat data;
|
||||
int ch;
|
||||
int rows;
|
||||
PCA pca;
|
||||
string winName;
|
||||
};
|
||||
|
||||
static void onTrackbar(int pos, void* ptr)
|
||||
{
|
||||
cout << "Retained Variance = " << pos << "% ";
|
||||
cout << "re-calculating PCA..." << std::flush;
|
||||
|
||||
double var = pos / 100.0;
|
||||
|
||||
struct params *p = (struct params *)ptr;
|
||||
|
||||
p->pca = PCA(p->data, cv::Mat(), PCA::DATA_AS_ROW, var);
|
||||
|
||||
Mat point = p->pca.project(p->data.row(0));
|
||||
Mat reconstruction = p->pca.backProject(point);
|
||||
reconstruction = reconstruction.reshape(p->ch, p->rows);
|
||||
reconstruction = toGrayscale(reconstruction);
|
||||
|
||||
imshow(p->winName, reconstruction);
|
||||
cout << "done! # of principal components: " << p->pca.eigenvectors.rows << endl;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////
|
||||
// Main
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{@input||image list}{help h||show help message}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
exit(0);
|
||||
}
|
||||
// Get the path to your CSV.
|
||||
string imgList = parser.get<string>("@input");
|
||||
if (imgList.empty())
|
||||
{
|
||||
parser.printMessage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// vector to hold the images
|
||||
vector<Mat> images;
|
||||
|
||||
// Read in the data. This can fail if not valid
|
||||
try {
|
||||
read_imgList(imgList, images);
|
||||
} catch (const cv::Exception& e) {
|
||||
cerr << "Error opening file \"" << imgList << "\". Reason: " << e.msg << endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Quit if there are not enough images for this demo.
|
||||
if(images.size() <= 1) {
|
||||
string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
|
||||
CV_Error(Error::StsError, error_message);
|
||||
}
|
||||
|
||||
// Reshape and stack images into a rowMatrix
|
||||
Mat data = formatImagesForPCA(images);
|
||||
|
||||
// perform PCA
|
||||
PCA pca(data, cv::Mat(), PCA::DATA_AS_ROW, 0.95); // trackbar is initially set here, also this is a common value for retainedVariance
|
||||
|
||||
// Demonstration of the effect of retainedVariance on the first image
|
||||
Mat point = pca.project(data.row(0)); // project into the eigenspace, thus the image becomes a "point"
|
||||
Mat reconstruction = pca.backProject(point); // re-create the image from the "point"
|
||||
reconstruction = reconstruction.reshape(images[0].channels(), images[0].rows); // reshape from a row vector into image shape
|
||||
reconstruction = toGrayscale(reconstruction); // re-scale for displaying purposes
|
||||
|
||||
// init highgui window
|
||||
string winName = "Reconstruction | press 'q' to quit";
|
||||
namedWindow(winName, WINDOW_NORMAL);
|
||||
|
||||
// params struct to pass to the trackbar handler
|
||||
params p;
|
||||
p.data = data;
|
||||
p.ch = images[0].channels();
|
||||
p.rows = images[0].rows;
|
||||
p.pca = pca;
|
||||
p.winName = winName;
|
||||
|
||||
// create the tracbar
|
||||
int pos = 95;
|
||||
createTrackbar("Retained Variance (%)", winName, &pos, 100, onTrackbar, (void*)&p);
|
||||
|
||||
// display until user presses q
|
||||
imshow(winName, reconstruction);
|
||||
|
||||
char key = 0;
|
||||
while(key != 'q')
|
||||
key = (char)waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static int liveQRCodeDetect();
|
||||
static int imageQRCodeDetect(const string& in_file);
|
||||
|
||||
static bool g_useArucoBased = false;
|
||||
static bool g_modeMultiQR = false;
|
||||
static bool g_detectOnly = false;
|
||||
|
||||
static string g_out_file_name, g_out_file_ext;
|
||||
static int g_save_idx = 0;
|
||||
|
||||
static bool g_saveDetections = false;
|
||||
static bool g_saveAll = false;
|
||||
|
||||
static string getQRModeString()
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "QR"
|
||||
<< (g_modeMultiQR ? " multi" : "")
|
||||
<< (g_detectOnly ? " detector" : " decoder");
|
||||
return out.str();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const string keys =
|
||||
"{h help ? | | print help messages }"
|
||||
"{i in | | input image path (also switches to image detection mode) }"
|
||||
"{aruco_based | false | use Aruco-based QR code detector instead of contour-based }"
|
||||
"{detect | false | detect QR code only (skip decoding) }"
|
||||
"{m multi | | use detect for multiple qr-codes }"
|
||||
"{o out | qr_code.png | path to result file }"
|
||||
"{save_detections | false | save all QR detections (video mode only) }"
|
||||
"{save_all | false | save all processed frames (video mode only) }"
|
||||
;
|
||||
CommandLineParser cmd_parser(argc, argv, keys);
|
||||
|
||||
cmd_parser.about("This program detects the QR-codes from camera or images using the OpenCV library.");
|
||||
if (cmd_parser.has("help"))
|
||||
{
|
||||
cmd_parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
string in_file_name = cmd_parser.get<string>("in"); // path to input image
|
||||
|
||||
if (cmd_parser.has("out"))
|
||||
{
|
||||
std::string fpath = cmd_parser.get<string>("out"); // path to output image
|
||||
std::string::size_type idx = fpath.rfind('.');
|
||||
if (idx != std::string::npos)
|
||||
{
|
||||
g_out_file_name = fpath.substr(0, idx);
|
||||
g_out_file_ext = fpath.substr(idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_out_file_name = fpath;
|
||||
g_out_file_ext = ".png";
|
||||
}
|
||||
}
|
||||
if (!cmd_parser.check())
|
||||
{
|
||||
cmd_parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_modeMultiQR = cmd_parser.has("multi") && cmd_parser.get<bool>("multi");
|
||||
g_detectOnly = cmd_parser.has("detect") && cmd_parser.get<bool>("detect");
|
||||
g_useArucoBased = cmd_parser.has("aruco_based") && cmd_parser.get<bool>("aruco_based");
|
||||
|
||||
g_saveDetections = cmd_parser.has("save_detections") && cmd_parser.get<bool>("save_detections");
|
||||
g_saveAll = cmd_parser.has("save_all") && cmd_parser.get<bool>("save_all");
|
||||
|
||||
int return_code = 0;
|
||||
if (in_file_name.empty())
|
||||
{
|
||||
return_code = liveQRCodeDetect();
|
||||
}
|
||||
else
|
||||
{
|
||||
return_code = imageQRCodeDetect(samples::findFile(in_file_name));
|
||||
}
|
||||
return return_code;
|
||||
}
|
||||
|
||||
static
|
||||
void drawQRCodeContour(Mat &color_image, const vector<Point>& corners)
|
||||
{
|
||||
if (!corners.empty())
|
||||
{
|
||||
double show_radius = (color_image.rows > color_image.cols)
|
||||
? (2.813 * color_image.rows) / color_image.cols
|
||||
: (2.813 * color_image.cols) / color_image.rows;
|
||||
double contour_radius = show_radius * 0.4;
|
||||
|
||||
vector< vector<Point> > contours;
|
||||
contours.push_back(corners);
|
||||
drawContours(color_image, contours, 0, Scalar(211, 0, 148), cvRound(contour_radius));
|
||||
|
||||
RNG rng(1000);
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
Scalar color = Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255));
|
||||
circle(color_image, corners[i], cvRound(show_radius), color, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void drawFPS(Mat &color_image, double fps)
|
||||
{
|
||||
ostringstream convert;
|
||||
convert << cv::format("%.2f", fps) << " FPS (" << getQRModeString() << ")";
|
||||
putText(color_image, convert.str(), Point(25, 25), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255), 2);
|
||||
}
|
||||
|
||||
static
|
||||
void drawQRCodeResults(Mat& frame, const vector<Point>& corners, const vector<cv::String>& decode_info, double fps)
|
||||
{
|
||||
if (!corners.empty())
|
||||
{
|
||||
for (size_t i = 0; i < corners.size(); i += 4)
|
||||
{
|
||||
size_t qr_idx = i / 4;
|
||||
vector<Point> qrcode_contour(corners.begin() + i, corners.begin() + i + 4);
|
||||
drawQRCodeContour(frame, qrcode_contour);
|
||||
|
||||
cout << "QR[" << qr_idx << "] @ " << Mat(qrcode_contour).reshape(2, 1) << ": ";
|
||||
if (decode_info.size() > qr_idx)
|
||||
{
|
||||
if (!decode_info[qr_idx].empty())
|
||||
cout << "'" << decode_info[qr_idx] << "'" << endl;
|
||||
else
|
||||
cout << "can't decode QR code" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "decode information is not available (disabled)" << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "QR code is not detected" << endl;
|
||||
}
|
||||
|
||||
drawFPS(frame, fps);
|
||||
}
|
||||
|
||||
static
|
||||
void runQR(
|
||||
const GraphicalCodeDetector& qrcode, const Mat& input,
|
||||
vector<Point>& corners, vector<cv::String>& decode_info
|
||||
// +global: bool g_modeMultiQR, bool g_detectOnly
|
||||
)
|
||||
{
|
||||
if (!g_modeMultiQR)
|
||||
{
|
||||
if (!g_detectOnly)
|
||||
{
|
||||
String decode_info1 = qrcode.detectAndDecode(input, corners);
|
||||
decode_info.push_back(decode_info1);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool detection_result = qrcode.detect(input, corners);
|
||||
CV_UNUSED(detection_result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!g_detectOnly)
|
||||
{
|
||||
bool result_detection = qrcode.detectAndDecodeMulti(input, decode_info, corners);
|
||||
CV_UNUSED(result_detection);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool result_detection = qrcode.detectMulti(input, corners);
|
||||
CV_UNUSED(result_detection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
double processQRCodeDetection(const GraphicalCodeDetector& qrcode, const Mat& input, Mat& result, vector<Point>& corners)
|
||||
{
|
||||
if (input.channels() == 1)
|
||||
cvtColor(input, result, COLOR_GRAY2BGR);
|
||||
else
|
||||
input.copyTo(result);
|
||||
|
||||
cout << "Run " << getQRModeString()
|
||||
<< " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
|
||||
<< endl;
|
||||
|
||||
TickMeter timer;
|
||||
|
||||
vector<cv::String> decode_info;
|
||||
timer.start();
|
||||
runQR(qrcode, input, corners, decode_info);
|
||||
timer.stop();
|
||||
|
||||
double fps = 1 / timer.getTimeSec();
|
||||
drawQRCodeResults(result, corners, decode_info, fps);
|
||||
|
||||
return fps;
|
||||
}
|
||||
|
||||
int liveQRCodeDetect()
|
||||
{
|
||||
VideoCapture cap(0);
|
||||
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cout << "Cannot open a camera" << endl;
|
||||
return 2;
|
||||
}
|
||||
|
||||
cout << "Press 'm' to switch between detectAndDecode and detectAndDecodeMulti" << endl;
|
||||
cout << "Press 'd' to switch between decoder and detector" << endl;
|
||||
cout << "Press ' ' (space) to save result into images" << endl;
|
||||
cout << "Press 'ESC' to exit" << endl;
|
||||
GraphicalCodeDetector qrcode = QRCodeDetector();
|
||||
if (g_useArucoBased)
|
||||
qrcode = QRCodeDetectorAruco();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
Mat frame;
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
{
|
||||
cout << "End of video stream" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
bool forceSave = g_saveAll;
|
||||
|
||||
Mat result;
|
||||
|
||||
try
|
||||
{
|
||||
vector<Point> corners;
|
||||
double fps = processQRCodeDetection(qrcode, frame, result, corners);
|
||||
cout << "FPS: " << fps << endl;
|
||||
forceSave |= (g_saveDetections && !corners.empty());
|
||||
//forceSave |= fps < 1.0;
|
||||
}
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
cerr << "ERROR exception: " << e.what() << endl;
|
||||
forceSave = true;
|
||||
}
|
||||
|
||||
if (!result.empty())
|
||||
imshow("QR code", result);
|
||||
|
||||
int code = waitKey(1);
|
||||
if (code < 0 && !forceSave)
|
||||
continue; // timeout
|
||||
char c = (char)code;
|
||||
if (c == ' ' || forceSave)
|
||||
{
|
||||
string fsuffix = cv::format("-%05d", g_save_idx++);
|
||||
|
||||
string fname_input = g_out_file_name + fsuffix + "_input.png";
|
||||
cout << "Saving QR code detection input: '" << fname_input << "' ..." << endl;
|
||||
imwrite(fname_input, frame);
|
||||
|
||||
string fname = g_out_file_name + fsuffix + g_out_file_ext;
|
||||
cout << "Saving QR code detection result: '" << fname << "' ..." << endl;
|
||||
imwrite(fname, result);
|
||||
|
||||
cout << "Saved" << endl;
|
||||
}
|
||||
if (c == 'm')
|
||||
{
|
||||
g_modeMultiQR = !g_modeMultiQR;
|
||||
cout << "Switching QR code mode ==> " << (g_modeMultiQR ? "detectAndDecodeMulti" : "detectAndDecode") << endl;
|
||||
}
|
||||
if (c == 'd')
|
||||
{
|
||||
g_detectOnly = !g_detectOnly;
|
||||
cout << "Switching QR decoder mode ==> " << (g_detectOnly ? "detect" : "decode") << endl;
|
||||
}
|
||||
if (c == 27)
|
||||
{
|
||||
cout << "'ESC' is pressed. Exiting..." << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
cout << "Exit." << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int imageQRCodeDetect(const string& in_file)
|
||||
{
|
||||
const int count_experiments = 10;
|
||||
|
||||
Mat input = imread(in_file, IMREAD_COLOR);
|
||||
cout << "Run " << getQRModeString()
|
||||
<< " on image: " << input.size() << " (" << typeToString(input.type()) << ")"
|
||||
<< endl;
|
||||
|
||||
GraphicalCodeDetector qrcode = QRCodeDetector();
|
||||
if (g_useArucoBased)
|
||||
qrcode = QRCodeDetectorAruco();
|
||||
|
||||
vector<Point> corners;
|
||||
vector<cv::String> decode_info;
|
||||
|
||||
TickMeter timer;
|
||||
for (size_t i = 0; i < count_experiments; i++)
|
||||
{
|
||||
corners.clear();
|
||||
decode_info.clear();
|
||||
|
||||
timer.start();
|
||||
runQR(qrcode, input, corners, decode_info);
|
||||
timer.stop();
|
||||
}
|
||||
double fps = count_experiments / timer.getTimeSec();
|
||||
cout << "FPS: " << fps << endl;
|
||||
|
||||
Mat result; input.copyTo(result);
|
||||
drawQRCodeResults(result, corners, decode_info, fps);
|
||||
|
||||
imshow("QR", result); waitKey(1);
|
||||
|
||||
if (!g_out_file_name.empty())
|
||||
{
|
||||
string out_file = g_out_file_name + g_out_file_ext;
|
||||
cout << "Saving result: " << out_file << endl;
|
||||
imwrite(out_file, result);
|
||||
}
|
||||
|
||||
cout << "Press any key to exit ..." << endl;
|
||||
waitKey(0);
|
||||
cout << "Exit." << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
*
|
||||
* select3obj.cpp With a calibration chessboard on a table, mark an object in a 3D box and
|
||||
* track that object in all subsequent frames as long as the camera can see
|
||||
* the chessboard. Also segments the object using the box projection. This
|
||||
* program is useful for collecting large datasets of many views of an object
|
||||
* on a table.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/calib.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static string helphelp(char** argv)
|
||||
{
|
||||
return string("\nThis program's purpose is to collect data sets of an object and its segmentation mask.\n")
|
||||
+ "\n"
|
||||
"It shows how to use a calibrated camera together with a calibration pattern to\n"
|
||||
"compute the homography of the plane the calibration pattern is on. It also shows grabCut\n"
|
||||
"segmentation etc.\n"
|
||||
"\n"
|
||||
+ argv[0]
|
||||
+ " -w=<board_width> -h=<board_height> [-s=<square_size>]\n"
|
||||
" -i=<camera_intrinsics_filename> -o=<output_prefix>\n"
|
||||
"\n"
|
||||
" -w=<board_width> Number of chessboard corners wide\n"
|
||||
" -h=<board_height> Number of chessboard corners width\n"
|
||||
" [-s=<square_size>] Optional measure of chessboard squares in meters\n"
|
||||
" -i=<camera_intrinsics_filename> Camera matrix .yml file from calibration.cpp\n"
|
||||
" -o=<output_prefix> Prefix the output segmentation images with this\n"
|
||||
" [video_filename/cameraId] If present, read from that video file or that ID\n"
|
||||
"\n"
|
||||
"Using a camera's intrinsics (from calibrating a camera -- see calibration.cpp) and an\n"
|
||||
"image of the object sitting on a planar surface with a calibration pattern of\n"
|
||||
"(board_width x board_height) on the surface, we draw a 3D box around the object. From\n"
|
||||
"then on, we can move a camera and as long as it sees the chessboard calibration pattern,\n"
|
||||
"it will store a mask of where the object is. We get successive images using <output_prefix>\n"
|
||||
"of the segmentation mask containing the object. This makes creating training sets easy.\n"
|
||||
"It is best if the chessboard is odd x even in dimensions to avoid ambiguous poses.\n"
|
||||
"\n"
|
||||
"The actions one can use while the program is running are:\n"
|
||||
"\n"
|
||||
" Select object as 3D box with the mouse.\n"
|
||||
" First draw one line on the plane to outline the projection of that object on the plane\n"
|
||||
" Then extend that line into a box to encompass the projection of that object onto the plane\n"
|
||||
" The use the mouse again to extend the box upwards from the plane to encase the object.\n"
|
||||
" Then use the following commands\n"
|
||||
" ESC - Reset the selection\n"
|
||||
" SPACE - Skip the frame; move to the next frame (not in video mode)\n"
|
||||
" ENTER - Confirm the selection. Grab next object in video mode.\n"
|
||||
" q - Exit the program\n"
|
||||
"\n\n";
|
||||
}
|
||||
// static void help()
|
||||
// {
|
||||
// puts(helphelp);
|
||||
// }
|
||||
|
||||
|
||||
struct MouseEvent
|
||||
{
|
||||
MouseEvent() { event = -1; buttonState = 0; }
|
||||
Point pt;
|
||||
int event;
|
||||
int buttonState;
|
||||
};
|
||||
|
||||
static void onMouse(int event, int x, int y, int flags, void* userdata)
|
||||
{
|
||||
MouseEvent* data = (MouseEvent*)userdata;
|
||||
data->event = event;
|
||||
data->pt = Point(x,y);
|
||||
data->buttonState = flags;
|
||||
}
|
||||
|
||||
static bool readCameraMatrix(const string& filename,
|
||||
Mat& cameraMatrix, Mat& distCoeffs,
|
||||
Size& calibratedImageSize )
|
||||
{
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
fs["image_width"] >> calibratedImageSize.width;
|
||||
fs["image_height"] >> calibratedImageSize.height;
|
||||
fs["distortion_coefficients"] >> distCoeffs;
|
||||
fs["camera_matrix"] >> cameraMatrix;
|
||||
|
||||
if( distCoeffs.type() != CV_64F )
|
||||
distCoeffs = Mat_<double>(distCoeffs);
|
||||
if( cameraMatrix.type() != CV_64F )
|
||||
cameraMatrix = Mat_<double>(cameraMatrix);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners)
|
||||
{
|
||||
corners.resize(0);
|
||||
|
||||
for( int i = 0; i < boardSize.height; i++ )
|
||||
for( int j = 0; j < boardSize.width; j++ )
|
||||
corners.push_back(Point3f(float(j*squareSize),
|
||||
float(i*squareSize), 0));
|
||||
}
|
||||
|
||||
|
||||
static Point3f image2plane(Point2f imgpt, const Mat& R, const Mat& tvec,
|
||||
const Mat& cameraMatrix, double Z)
|
||||
{
|
||||
Mat R1 = R.clone();
|
||||
R1.col(2) = R1.col(2)*Z + tvec;
|
||||
Mat_<double> v = (cameraMatrix*R1).inv()*(Mat_<double>({3,1}, {imgpt.x, imgpt.y, 1}));
|
||||
double iw = fabs(v(2,0)) > DBL_EPSILON ? 1./v(2,0) : 0;
|
||||
return Point3f((float)(v(0,0)*iw), (float)(v(1,0)*iw), (float)Z);
|
||||
}
|
||||
|
||||
|
||||
static Rect extract3DBox(const Mat& frame, Mat& shownFrame, Mat& selectedObjFrame,
|
||||
const Mat& cameraMatrix, const Mat& rvec, const Mat& tvec,
|
||||
const vector<Point3f>& box, int nobjpt, bool runExtraSegmentation)
|
||||
{
|
||||
selectedObjFrame = Mat::zeros(frame.size(), frame.type());
|
||||
if( nobjpt == 0 )
|
||||
return Rect();
|
||||
vector<Point3f> objpt;
|
||||
vector<Point2f> imgpt;
|
||||
|
||||
objpt.push_back(box[0]);
|
||||
if( nobjpt > 1 )
|
||||
objpt.push_back(box[1]);
|
||||
if( nobjpt > 2 )
|
||||
{
|
||||
objpt.push_back(box[2]);
|
||||
objpt.push_back(objpt[2] - objpt[1] + objpt[0]);
|
||||
}
|
||||
if( nobjpt > 3 )
|
||||
for( int i = 0; i < 4; i++ )
|
||||
objpt.push_back(Point3f(objpt[i].x, objpt[i].y, box[3].z));
|
||||
|
||||
projectPoints(Mat(objpt), rvec, tvec, cameraMatrix, Mat(), imgpt);
|
||||
|
||||
if( !shownFrame.empty() )
|
||||
{
|
||||
if( nobjpt == 1 )
|
||||
circle(shownFrame, imgpt[0], 3, Scalar(0,255,0), -1, LINE_AA);
|
||||
else if( nobjpt == 2 )
|
||||
{
|
||||
circle(shownFrame, imgpt[0], 3, Scalar(0,255,0), -1, LINE_AA);
|
||||
circle(shownFrame, imgpt[1], 3, Scalar(0,255,0), -1, LINE_AA);
|
||||
line(shownFrame, imgpt[0], imgpt[1], Scalar(0,255,0), 3, LINE_AA);
|
||||
}
|
||||
else if( nobjpt == 3 )
|
||||
for( int i = 0; i < 4; i++ )
|
||||
{
|
||||
circle(shownFrame, imgpt[i], 3, Scalar(0,255,0), -1, LINE_AA);
|
||||
line(shownFrame, imgpt[i], imgpt[(i+1)%4], Scalar(0,255,0), 3, LINE_AA);
|
||||
}
|
||||
else
|
||||
for( int i = 0; i < 8; i++ )
|
||||
{
|
||||
circle(shownFrame, imgpt[i], 3, Scalar(0,255,0), -1, LINE_AA);
|
||||
line(shownFrame, imgpt[i], imgpt[(i+1)%4 + (i/4)*4], Scalar(0,255,0), 3, LINE_AA);
|
||||
line(shownFrame, imgpt[i], imgpt[i%4], Scalar(0,255,0), 3, LINE_AA);
|
||||
}
|
||||
}
|
||||
|
||||
if( nobjpt <= 2 )
|
||||
return Rect();
|
||||
vector<Point> hull;
|
||||
convexHull(Mat_<Point>(Mat(imgpt)), hull);
|
||||
Mat selectedObjMask = Mat::zeros(frame.size(), CV_8U);
|
||||
fillConvexPoly(selectedObjMask, &hull[0], (int)hull.size(), Scalar::all(255), 8, 0);
|
||||
Rect roi = boundingRect(Mat(hull)) & Rect(Point(), frame.size());
|
||||
|
||||
if( runExtraSegmentation )
|
||||
{
|
||||
selectedObjMask = Scalar::all(GC_BGD);
|
||||
fillConvexPoly(selectedObjMask, &hull[0], (int)hull.size(), Scalar::all(GC_PR_FGD), 8, 0);
|
||||
Mat bgdModel, fgdModel;
|
||||
grabCut(frame, selectedObjMask, roi, bgdModel, fgdModel,
|
||||
3, GC_INIT_WITH_RECT + GC_INIT_WITH_MASK);
|
||||
bitwise_and(selectedObjMask, Scalar::all(1), selectedObjMask);
|
||||
}
|
||||
|
||||
frame.copyTo(selectedObjFrame, selectedObjMask);
|
||||
return roi;
|
||||
}
|
||||
|
||||
|
||||
static int select3DBox(const string& windowname, const string& selWinName, const Mat& frame,
|
||||
const Mat& cameraMatrix, const Mat& rvec, const Mat& tvec,
|
||||
vector<Point3f>& box)
|
||||
{
|
||||
const float eps = 1e-3f;
|
||||
MouseEvent mouse;
|
||||
|
||||
setMouseCallback(windowname, onMouse, &mouse);
|
||||
vector<Point3f> tempobj(8);
|
||||
vector<Point2f> imgpt(4), tempimg(8);
|
||||
vector<Point> temphull;
|
||||
int nobjpt = 0;
|
||||
Mat R, selectedObjMask, selectedObjFrame, shownFrame;
|
||||
Rodrigues(rvec, R);
|
||||
box.resize(4);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
float Z = 0.f;
|
||||
bool dragging = (mouse.buttonState & EVENT_FLAG_LBUTTON) != 0;
|
||||
int npt = nobjpt;
|
||||
|
||||
if( (mouse.event == EVENT_LBUTTONDOWN ||
|
||||
mouse.event == EVENT_LBUTTONUP ||
|
||||
dragging) && nobjpt < 4 )
|
||||
{
|
||||
Point2f m = mouse.pt;
|
||||
|
||||
if( nobjpt < 2 )
|
||||
imgpt[npt] = m;
|
||||
else
|
||||
{
|
||||
tempobj.resize(1);
|
||||
int nearestIdx = npt-1;
|
||||
if( nobjpt == 3 )
|
||||
{
|
||||
nearestIdx = 0;
|
||||
for( int i = 1; i < npt; i++ )
|
||||
if( norm(m - imgpt[i]) < norm(m - imgpt[nearestIdx]) )
|
||||
nearestIdx = i;
|
||||
}
|
||||
|
||||
if( npt == 2 )
|
||||
{
|
||||
float dx = box[1].x - box[0].x, dy = box[1].y - box[0].y;
|
||||
float len = 1.f/std::sqrt(dx*dx+dy*dy);
|
||||
tempobj[0] = Point3f(dy*len + box[nearestIdx].x,
|
||||
-dx*len + box[nearestIdx].y, 0.f);
|
||||
}
|
||||
else
|
||||
tempobj[0] = Point3f(box[nearestIdx].x, box[nearestIdx].y, 1.f);
|
||||
|
||||
projectPoints(Mat(tempobj), rvec, tvec, cameraMatrix, Mat(), tempimg);
|
||||
|
||||
Point2f a = imgpt[nearestIdx], b = tempimg[0], d1 = b - a, d2 = m - a;
|
||||
float n1 = (float)norm(d1), n2 = (float)norm(d2);
|
||||
if( n1*n2 < eps )
|
||||
imgpt[npt] = a;
|
||||
else
|
||||
{
|
||||
Z = d1.dot(d2)/(n1*n1);
|
||||
imgpt[npt] = d1*Z + a;
|
||||
}
|
||||
}
|
||||
box[npt] = image2plane(imgpt[npt], R, tvec, cameraMatrix, npt<3 ? 0 : Z);
|
||||
|
||||
if( (npt == 0 && mouse.event == EVENT_LBUTTONDOWN) ||
|
||||
(npt > 0 && norm(box[npt] - box[npt-1]) > eps &&
|
||||
mouse.event == EVENT_LBUTTONUP) )
|
||||
{
|
||||
nobjpt++;
|
||||
if( nobjpt < 4 )
|
||||
{
|
||||
imgpt[nobjpt] = imgpt[nobjpt-1];
|
||||
box[nobjpt] = box[nobjpt-1];
|
||||
}
|
||||
}
|
||||
|
||||
// reset the event
|
||||
mouse.event = -1;
|
||||
//mouse.buttonState = 0;
|
||||
npt++;
|
||||
}
|
||||
|
||||
frame.copyTo(shownFrame);
|
||||
extract3DBox(frame, shownFrame, selectedObjFrame,
|
||||
cameraMatrix, rvec, tvec, box, npt, false);
|
||||
imshow(windowname, shownFrame);
|
||||
imshow(selWinName, selectedObjFrame);
|
||||
|
||||
char c = (char)waitKey(30);
|
||||
if( c == 27 )
|
||||
{
|
||||
nobjpt = 0;
|
||||
}
|
||||
if( c == 'q' || c == 'Q' || c == ' ' )
|
||||
{
|
||||
box.clear();
|
||||
return c == ' ' ? -1 : -100;
|
||||
}
|
||||
if( (c == '\r' || c == '\n') && nobjpt == 4 && box[3].z != 0 )
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool readModelViews( const string& filename, vector<Point3f>& box,
|
||||
vector<string>& imagelist,
|
||||
vector<Rect>& roiList, vector<Vec6f>& poseList )
|
||||
{
|
||||
imagelist.resize(0);
|
||||
roiList.resize(0);
|
||||
poseList.resize(0);
|
||||
box.resize(0);
|
||||
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
fs["box"] >> box;
|
||||
|
||||
FileNode all = fs["views"];
|
||||
if( all.type() != FileNode::SEQ )
|
||||
return false;
|
||||
FileNodeIterator it = all.begin(), it_end = all.end();
|
||||
|
||||
for(; it != it_end; ++it)
|
||||
{
|
||||
FileNode n = *it;
|
||||
imagelist.push_back((string)n["image"]);
|
||||
FileNode nr = n["rect"];
|
||||
roiList.push_back(Rect((int)nr[0], (int)nr[1], (int)nr[2], (int)nr[3]));
|
||||
FileNode np = n["pose"];
|
||||
poseList.push_back(Vec6f((float)np[0], (float)np[1], (float)np[2],
|
||||
(float)np[3], (float)np[4], (float)np[5]));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool writeModelViews(const string& filename, const vector<Point3f>& box,
|
||||
const vector<string>& imagelist,
|
||||
const vector<Rect>& roiList,
|
||||
const vector<Vec6f>& poseList)
|
||||
{
|
||||
FileStorage fs(filename, FileStorage::WRITE);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
|
||||
fs << "box" << "[:";
|
||||
fs << box << "]" << "views" << "[";
|
||||
|
||||
size_t i, nviews = imagelist.size();
|
||||
|
||||
CV_Assert( nviews == roiList.size() && nviews == poseList.size() );
|
||||
|
||||
for( i = 0; i < nviews; i++ )
|
||||
{
|
||||
Rect r = roiList[i];
|
||||
Vec6f p = poseList[i];
|
||||
|
||||
fs << "{" << "image" << imagelist[i] <<
|
||||
"roi" << "[:" << r.x << r.y << r.width << r.height << "]" <<
|
||||
"pose" << "[:" << p[0] << p[1] << p[2] << p[3] << p[4] << p[5] << "]" << "}";
|
||||
}
|
||||
fs << "]";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool readStringList( const string& filename, vector<string>& l )
|
||||
{
|
||||
l.resize(0);
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
FileNode n = fs.getFirstTopLevelNode();
|
||||
if( n.type() != FileNode::SEQ )
|
||||
return false;
|
||||
FileNodeIterator it = n.begin(), it_end = n.end();
|
||||
for( ; it != it_end; ++it )
|
||||
l.push_back((string)*it);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
string help = string("Usage: ") + argv[0] + " -w=<board_width> -h=<board_height> [-s=<square_size>]\n" +
|
||||
"\t-i=<intrinsics_filename> -o=<output_prefix> [video_filename/cameraId]\n";
|
||||
const char* screen_help =
|
||||
"Actions: \n"
|
||||
"\tSelect object as 3D box with the mouse. That's it\n"
|
||||
"\tESC - Reset the selection\n"
|
||||
"\tSPACE - Skip the frame; move to the next frame (not in video mode)\n"
|
||||
"\tENTER - Confirm the selection. Grab next object in video mode.\n"
|
||||
"\tq - Exit the program\n";
|
||||
|
||||
cv::CommandLineParser parser(argc, argv, "{help h||}{w||}{h||}{s|1|}{i||}{o||}{@input|0|}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
puts(helphelp(argv).c_str());
|
||||
puts(help.c_str());
|
||||
return 0;
|
||||
}
|
||||
string intrinsicsFilename;
|
||||
string outprefix = "";
|
||||
string inputName = "";
|
||||
int cameraId = 0;
|
||||
Size boardSize;
|
||||
double squareSize;
|
||||
vector<string> imageList;
|
||||
intrinsicsFilename = parser.get<string>("i");
|
||||
outprefix = parser.get<string>("o");
|
||||
boardSize.width = parser.get<int>("w");
|
||||
boardSize.height = parser.get<int>("h");
|
||||
squareSize = parser.get<double>("s");
|
||||
if ( parser.get<string>("@input").size() == 1 && isdigit(parser.get<string>("@input")[0]) )
|
||||
cameraId = parser.get<int>("@input");
|
||||
else
|
||||
inputName = samples::findFileOrKeep(parser.get<string>("@input"));
|
||||
if (!parser.check())
|
||||
{
|
||||
puts(help.c_str());
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
if ( boardSize.width <= 0 )
|
||||
{
|
||||
printf("Incorrect -w parameter (must be a positive integer)\n");
|
||||
puts(help.c_str());
|
||||
return 0;
|
||||
}
|
||||
if ( boardSize.height <= 0 )
|
||||
{
|
||||
printf("Incorrect -h parameter (must be a positive integer)\n");
|
||||
puts(help.c_str());
|
||||
return 0;
|
||||
}
|
||||
if ( squareSize <= 0 )
|
||||
{
|
||||
printf("Incorrect -s parameter (must be a positive real number)\n");
|
||||
puts(help.c_str());
|
||||
return 0;
|
||||
}
|
||||
Mat cameraMatrix, distCoeffs;
|
||||
Size calibratedImageSize;
|
||||
readCameraMatrix(intrinsicsFilename, cameraMatrix, distCoeffs, calibratedImageSize );
|
||||
|
||||
VideoCapture capture;
|
||||
if( !inputName.empty() )
|
||||
{
|
||||
if( !readStringList(inputName, imageList) &&
|
||||
!capture.open(inputName))
|
||||
{
|
||||
fprintf( stderr, "The input file could not be opened\n" );
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
capture.open(cameraId);
|
||||
|
||||
if( !capture.isOpened() && imageList.empty() )
|
||||
return fprintf( stderr, "Could not initialize video capture\n" ), -2;
|
||||
|
||||
const char* outbarename = 0;
|
||||
{
|
||||
outbarename = strrchr(outprefix.c_str(), '/');
|
||||
const char* tmp = strrchr(outprefix.c_str(), '\\');
|
||||
char cmd[1000];
|
||||
snprintf(cmd, sizeof(cmd), "mkdir %s", outprefix.c_str());
|
||||
if( tmp && tmp > outbarename )
|
||||
outbarename = tmp;
|
||||
if( outbarename )
|
||||
{
|
||||
cmd[6 + outbarename - outprefix.c_str()] = '\0';
|
||||
int result = system(cmd);
|
||||
CV_Assert(result == 0);
|
||||
outbarename++;
|
||||
}
|
||||
else
|
||||
outbarename = outprefix.c_str();
|
||||
}
|
||||
|
||||
Mat frame, shownFrame, selectedObjFrame, mapxy;
|
||||
|
||||
namedWindow("View", 1);
|
||||
namedWindow("Selected Object", 1);
|
||||
setMouseCallback("View", onMouse, 0);
|
||||
bool boardFound = false;
|
||||
|
||||
string indexFilename = cv::format("%s_index.yml", outprefix.c_str());
|
||||
|
||||
vector<string> capturedImgList;
|
||||
vector<Rect> roiList;
|
||||
vector<Vec6f> poseList;
|
||||
vector<Point3f> box, boardPoints;
|
||||
|
||||
readModelViews(indexFilename, box, capturedImgList, roiList, poseList);
|
||||
calcChessboardCorners(boardSize, (float)squareSize, boardPoints);
|
||||
int frameIdx = 0;
|
||||
bool grabNext = !imageList.empty();
|
||||
|
||||
puts(screen_help);
|
||||
|
||||
for(int i = 0;;i++)
|
||||
{
|
||||
Mat frame0;
|
||||
if( !imageList.empty() )
|
||||
{
|
||||
if( i < (int)imageList.size() )
|
||||
frame0 = imread(string(imageList[i]), 1);
|
||||
}
|
||||
else
|
||||
capture >> frame0;
|
||||
if( frame0.empty() )
|
||||
break;
|
||||
if( frame.empty() )
|
||||
{
|
||||
if( frame0.size() != calibratedImageSize )
|
||||
{
|
||||
double sx = (double)frame0.cols/calibratedImageSize.width;
|
||||
double sy = (double)frame0.rows/calibratedImageSize.height;
|
||||
|
||||
// adjust the camera matrix for the new resolution
|
||||
cameraMatrix.at<double>(0,0) *= sx;
|
||||
cameraMatrix.at<double>(0,2) *= sx;
|
||||
cameraMatrix.at<double>(1,1) *= sy;
|
||||
cameraMatrix.at<double>(1,2) *= sy;
|
||||
}
|
||||
Mat dummy;
|
||||
initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(),
|
||||
cameraMatrix, frame0.size(),
|
||||
CV_32FC2, mapxy, dummy );
|
||||
distCoeffs = Mat::zeros(5, 1, CV_64F);
|
||||
}
|
||||
remap(frame0, frame, mapxy, Mat(), INTER_LINEAR);
|
||||
vector<Point2f> foundBoardCorners;
|
||||
boardFound = findChessboardCorners(frame, boardSize, foundBoardCorners);
|
||||
|
||||
Mat rvec, tvec;
|
||||
if( boardFound )
|
||||
solvePnP(Mat(boardPoints), Mat(foundBoardCorners), cameraMatrix,
|
||||
distCoeffs, rvec, tvec, false);
|
||||
|
||||
frame.copyTo(shownFrame);
|
||||
drawChessboardCorners(shownFrame, boardSize, Mat(foundBoardCorners), boardFound);
|
||||
selectedObjFrame = Mat::zeros(frame.size(), frame.type());
|
||||
|
||||
if( boardFound && grabNext )
|
||||
{
|
||||
if( box.empty() )
|
||||
{
|
||||
int code = select3DBox("View", "Selected Object", frame,
|
||||
cameraMatrix, rvec, tvec, box);
|
||||
if( code == -100 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !box.empty() )
|
||||
{
|
||||
Rect r = extract3DBox(frame, shownFrame, selectedObjFrame,
|
||||
cameraMatrix, rvec, tvec, box, 4, true);
|
||||
if( !r.empty() )
|
||||
{
|
||||
const int maxFrameIdx = 10000;
|
||||
char path[1000];
|
||||
for(;frameIdx < maxFrameIdx;frameIdx++)
|
||||
{
|
||||
snprintf(path, sizeof(path), "%s%04d.jpg", outprefix.c_str(), frameIdx);
|
||||
FILE* f = fopen(path, "rb");
|
||||
if( !f )
|
||||
break;
|
||||
fclose(f);
|
||||
}
|
||||
if( frameIdx == maxFrameIdx )
|
||||
{
|
||||
printf("Can not save the image as %s<...>.jpg", outprefix.c_str());
|
||||
break;
|
||||
}
|
||||
imwrite(path, selectedObjFrame(r));
|
||||
|
||||
capturedImgList.push_back(string(path));
|
||||
roiList.push_back(r);
|
||||
|
||||
float p[6];
|
||||
Mat RV(3, 1, CV_32F, p), TV(3, 1, CV_32F, p+3);
|
||||
rvec.convertTo(RV, RV.type());
|
||||
tvec.convertTo(TV, TV.type());
|
||||
poseList.push_back(Vec6f(p[0], p[1], p[2], p[3], p[4], p[5]));
|
||||
}
|
||||
}
|
||||
grabNext = !imageList.empty();
|
||||
}
|
||||
|
||||
imshow("View", shownFrame);
|
||||
imshow("Selected Object", selectedObjFrame);
|
||||
char c = (char)waitKey(imageList.empty() && !box.empty() ? 30 : 300);
|
||||
if( c == 'q' || c == 'Q' )
|
||||
break;
|
||||
if( c == '\r' || c == '\n' )
|
||||
grabNext = true;
|
||||
}
|
||||
|
||||
writeModelViews(indexFilename, box, capturedImgList, roiList, poseList);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/simd_intrinsics.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
printf("================== macro dump ===================\n");
|
||||
#ifdef CV_SIMD
|
||||
printf("CV_SIMD is defined: " CVAUX_STR(CV_SIMD) "\n");
|
||||
#ifdef CV_SIMD_WIDTH
|
||||
printf("CV_SIMD_WIDTH is defined: " CVAUX_STR(CV_SIMD_WIDTH) "\n");
|
||||
#endif
|
||||
#ifdef CV_SIMD128
|
||||
printf("CV_SIMD128 is defined: " CVAUX_STR(CV_SIMD128) "\n");
|
||||
#endif
|
||||
#ifdef CV_SIMD256
|
||||
printf("CV_SIMD256 is defined: " CVAUX_STR(CV_SIMD256) "\n");
|
||||
#endif
|
||||
#ifdef CV_SIMD512
|
||||
printf("CV_SIMD512 is defined: " CVAUX_STR(CV_SIMD512) "\n");
|
||||
#endif
|
||||
#ifdef CV_SIMD_64F
|
||||
printf("CV_SIMD_64F is defined: " CVAUX_STR(CV_SIMD_64F) "\n");
|
||||
#endif
|
||||
#ifdef CV_SIMD_FP16
|
||||
printf("CV_SIMD_FP16 is defined: " CVAUX_STR(CV_SIMD_FP16) "\n");
|
||||
#endif
|
||||
#else
|
||||
printf("CV_SIMD is NOT defined\n");
|
||||
#endif
|
||||
|
||||
#ifdef CV_SIMD
|
||||
printf("================= sizeof checks =================\n");
|
||||
printf("sizeof(v_uint8) = %d\n", (int)sizeof(v_uint8));
|
||||
printf("sizeof(v_int32) = %d\n", (int)sizeof(v_int32));
|
||||
printf("sizeof(v_float32) = %d\n", (int)sizeof(v_float32));
|
||||
|
||||
printf("================== arithm check =================\n");
|
||||
v_uint8 a = vx_setall_u8(10);
|
||||
v_uint8 c = v_add(a, vx_setall_u8(45));
|
||||
printf("v_get0(vx_setall_u8(10) + vx_setall_u8(45)) => %d\n", (int)v_get0(c));
|
||||
#else
|
||||
printf("\nSIMD intrinsics are not available. Check compilation target and passed build options.\n");
|
||||
#endif
|
||||
|
||||
printf("===================== done ======================\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
Mat image;
|
||||
|
||||
bool backprojMode = false;
|
||||
bool selectObject = false;
|
||||
int trackObject = 0;
|
||||
bool showHist = true;
|
||||
Point origin;
|
||||
Rect selection;
|
||||
int vmin = 10, vmax = 256, smin = 30;
|
||||
|
||||
// User draws box around object to track. This triggers CAMShift to start tracking
|
||||
static void onMouse( int event, int x, int y, int, void* )
|
||||
{
|
||||
if( selectObject )
|
||||
{
|
||||
selection.x = MIN(x, origin.x);
|
||||
selection.y = MIN(y, origin.y);
|
||||
selection.width = std::abs(x - origin.x);
|
||||
selection.height = std::abs(y - origin.y);
|
||||
|
||||
selection &= Rect(0, 0, image.cols, image.rows);
|
||||
}
|
||||
|
||||
switch( event )
|
||||
{
|
||||
case EVENT_LBUTTONDOWN:
|
||||
origin = Point(x,y);
|
||||
selection = Rect(x,y,0,0);
|
||||
selectObject = true;
|
||||
break;
|
||||
case EVENT_LBUTTONUP:
|
||||
selectObject = false;
|
||||
if( selection.width > 0 && selection.height > 0 )
|
||||
trackObject = -1; // Set up CAMShift properties in main() loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string hot_keys =
|
||||
"\n\nHot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tc - stop the tracking\n"
|
||||
"\tb - switch to/from backprojection view\n"
|
||||
"\th - show/hide object histogram\n"
|
||||
"\tp - pause video\n"
|
||||
"To initialize tracking, select the object with mouse\n";
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
cout << "\nThis is a demo that shows mean-shift based tracking\n"
|
||||
"You select a color objects such as your face and it tracks it.\n"
|
||||
"This reads from video camera (0 by default, or the camera number the user enters\n"
|
||||
"Usage: \n\t";
|
||||
cout << argv[0] << " [camera number]\n";
|
||||
cout << hot_keys;
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h | | show help message}{@camera_number| 0 | camera number}"
|
||||
};
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
VideoCapture cap;
|
||||
Rect trackWindow;
|
||||
int hsize = 16;
|
||||
float hranges[] = {0,180};
|
||||
const float* phranges = hranges;
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
int camNum = parser.get<int>(0);
|
||||
cap.open(camNum);
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
help(argv);
|
||||
cout << "***Could not initialize capturing...***\n";
|
||||
cout << "Current parameter's value: \n";
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
cout << hot_keys;
|
||||
namedWindow( "Histogram", 0 );
|
||||
namedWindow( "CamShift Demo", 0 );
|
||||
setMouseCallback( "CamShift Demo", onMouse, 0 );
|
||||
createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );
|
||||
createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );
|
||||
createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );
|
||||
|
||||
Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
|
||||
bool paused = false;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if( !paused )
|
||||
{
|
||||
cap >> frame;
|
||||
if( frame.empty() )
|
||||
break;
|
||||
}
|
||||
|
||||
frame.copyTo(image);
|
||||
|
||||
if( !paused )
|
||||
{
|
||||
cvtColor(image, hsv, COLOR_BGR2HSV);
|
||||
|
||||
if( trackObject )
|
||||
{
|
||||
int _vmin = vmin, _vmax = vmax;
|
||||
|
||||
inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
|
||||
Scalar(180, 256, MAX(_vmin, _vmax)), mask);
|
||||
int ch[] = {0, 0};
|
||||
hue.create(hsv.size(), hsv.depth());
|
||||
mixChannels(&hsv, 1, &hue, 1, ch, 1);
|
||||
|
||||
if( trackObject < 0 )
|
||||
{
|
||||
// Object has been selected by user, set up CAMShift search properties once
|
||||
Mat roi(hue, selection), maskroi(mask, selection);
|
||||
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
|
||||
normalize(hist, hist, 0, 255, NORM_MINMAX);
|
||||
|
||||
trackWindow = selection;
|
||||
trackObject = 1; // Don't set up again, unless user selects new ROI
|
||||
|
||||
histimg = Scalar::all(0);
|
||||
int binW = histimg.cols / hsize;
|
||||
Mat buf(1, hsize, CV_8UC3);
|
||||
for( int i = 0; i < hsize; i++ )
|
||||
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255);
|
||||
cvtColor(buf, buf, COLOR_HSV2BGR);
|
||||
|
||||
for( int i = 0; i < hsize; i++ )
|
||||
{
|
||||
int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);
|
||||
rectangle( histimg, Point(i*binW,histimg.rows),
|
||||
Point((i+1)*binW,histimg.rows - val),
|
||||
Scalar(buf.at<Vec3b>(i)), -1, 8 );
|
||||
}
|
||||
}
|
||||
|
||||
// Perform CAMShift
|
||||
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
|
||||
backproj &= mask;
|
||||
RotatedRect trackBox = CamShift(backproj, trackWindow,
|
||||
TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 ));
|
||||
if( trackWindow.area() <= 1 )
|
||||
{
|
||||
int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
|
||||
trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
|
||||
trackWindow.x + r, trackWindow.y + r) &
|
||||
Rect(0, 0, cols, rows);
|
||||
}
|
||||
|
||||
if( backprojMode )
|
||||
cvtColor( backproj, image, COLOR_GRAY2BGR );
|
||||
ellipse( image, trackBox, Scalar(0,0,255), 3, LINE_AA );
|
||||
}
|
||||
}
|
||||
else if( trackObject < 0 )
|
||||
paused = false;
|
||||
|
||||
if( selectObject && selection.width > 0 && selection.height > 0 )
|
||||
{
|
||||
Mat roi(image, selection);
|
||||
bitwise_not(roi, roi);
|
||||
}
|
||||
|
||||
imshow( "CamShift Demo", image );
|
||||
imshow( "Histogram", histimg );
|
||||
|
||||
char c = (char)waitKey(10);
|
||||
if( c == 27 )
|
||||
break;
|
||||
switch(c)
|
||||
{
|
||||
case 'b':
|
||||
backprojMode = !backprojMode;
|
||||
break;
|
||||
case 'c':
|
||||
trackObject = 0;
|
||||
histimg = Scalar::all(0);
|
||||
break;
|
||||
case 'h':
|
||||
showHist = !showHist;
|
||||
if( !showHist )
|
||||
destroyWindow( "Histogram" );
|
||||
else
|
||||
namedWindow( "Histogram", 1 );
|
||||
break;
|
||||
case 'p':
|
||||
paused = !paused;
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static const char* usage =
|
||||
"Chromatic Aberration Correction Sample\n"
|
||||
"Usage:\n"
|
||||
" ca_correction <input_image> <calibration_file> [bayer_pattern] [output_image]\n"
|
||||
"\n"
|
||||
"Arguments:\n"
|
||||
" input_image Path to the input image. Can be:\n"
|
||||
" • a 3-channel BGR image, or\n"
|
||||
" • a 1-channel raw Bayer image (see bayer_pattern)\n"
|
||||
" calibration_file OpenCV YAML/XML file with chromatic aberration calibration:\n"
|
||||
" image_width, image_height, red_channel/coeffs_x, coeffs_y,\n"
|
||||
" blue_channel/coeffs_x, coeffs_y.\n"
|
||||
" output_image (optional) Path to save the corrected image. Default: corrected.png\n"
|
||||
" bayer_pattern (optional) integer code for demosaicing a 1-channel raw image:\n"
|
||||
" cv::COLOR_BayerBG2BGR = 46\n"
|
||||
" cv::COLOR_BayerGB2BGR = 47\n"
|
||||
" cv::COLOR_BayerGR2BGR = 48\n"
|
||||
" cv::COLOR_BayerRG2BGR = 49\n"
|
||||
" If omitted or <0, input is assumed 3-channel BGR.\n"
|
||||
"\n"
|
||||
"Example:\n"
|
||||
" ca_correction input.png calib.yaml 46 corrected.png\n"
|
||||
"\n";
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const string keys =
|
||||
"{help h | | show this help message }"
|
||||
"{@input | | input image (BGR or Bayer)}"
|
||||
"{@calibration | | calibration file (YAML/XML) }"
|
||||
"{output |corrected.png| output image file }"
|
||||
"{bayer |-1 | Bayer pattern code for demosaic }"
|
||||
;
|
||||
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
parser.about("Chromatic Aberration Correction Sample");
|
||||
if (parser.has("help") || argc < 3)
|
||||
{
|
||||
cout << usage << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
string inputPath = parser.get<string>("@input");
|
||||
string calibPath = parser.get<string>("@calibration");
|
||||
string outputPath = parser.get<string>("output");
|
||||
int bayerPattern = parser.get<int>("bayer");
|
||||
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
|
||||
Mat input = imread(inputPath, IMREAD_UNCHANGED);
|
||||
if (input.empty())
|
||||
{
|
||||
cerr << "ERROR: Could not load input image: " << inputPath << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
FileStorage fs(calibPath, FileStorage::READ);
|
||||
if (!fs.isOpened())
|
||||
{
|
||||
cerr << "ERROR: Could not load coeffients file: " << calibPath << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Mat coeffMat;
|
||||
Size calibSize = {-1, -1};
|
||||
int degree = -1;
|
||||
|
||||
cv::loadChromaticAberrationParams(fs.root(), coeffMat, calibSize, degree);
|
||||
|
||||
Mat corrected;
|
||||
correctChromaticAberration(input, coeffMat, corrected, calibSize, degree, bayerPattern);
|
||||
|
||||
namedWindow("Original", WINDOW_AUTOSIZE);
|
||||
namedWindow("Corrected", WINDOW_AUTOSIZE);
|
||||
imshow("Original", input);
|
||||
imshow("Corrected", corrected);
|
||||
cout << "Press any key to continue..." << endl;
|
||||
waitKey();
|
||||
|
||||
if (!imwrite(outputPath, corrected))
|
||||
{
|
||||
cerr << "WARNING: Could not write output image: " << outputPath << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Saved corrected image to: " << outputPath << endl;
|
||||
}
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
cerr << "OpenCV error: " << e.what() << endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* cloning_demo.cpp
|
||||
*
|
||||
* Author:
|
||||
* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
|
||||
*
|
||||
* This tutorial demonstrates how to use OpenCV seamless cloning
|
||||
* module without GUI.
|
||||
*
|
||||
* 1- Normal Cloning
|
||||
* 2- Mixed Cloning
|
||||
* 3- Monochrome Transfer
|
||||
* 4- Color Change
|
||||
* 5- Illumination change
|
||||
* 6- Texture Flattening
|
||||
|
||||
* The program takes as input a source and a destination image (for 1-3 methods)
|
||||
* and outputs the cloned image.
|
||||
*
|
||||
* Download test images from opencv_extra repository.
|
||||
*/
|
||||
|
||||
#include "opencv2/photo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main()
|
||||
{
|
||||
cout << endl;
|
||||
cout << "Note: specify OPENCV_SAMPLES_DATA_PATH_HINT=<opencv_extra>/testdata/cv" << endl << endl;
|
||||
cout << "Cloning Module" << endl;
|
||||
cout << "---------------" << endl;
|
||||
cout << "Options: " << endl;
|
||||
cout << endl;
|
||||
cout << "1) Normal Cloning " << endl;
|
||||
cout << "2) Mixed Cloning " << endl;
|
||||
cout << "3) Monochrome Transfer " << endl;
|
||||
cout << "4) Local Color Change " << endl;
|
||||
cout << "5) Local Illumination Change " << endl;
|
||||
cout << "6) Texture Flattening " << endl;
|
||||
cout << endl;
|
||||
cout << "Press number 1-6 to choose from above techniques: ";
|
||||
int num = 1;
|
||||
cin >> num;
|
||||
cout << endl;
|
||||
|
||||
if(num == 1)
|
||||
{
|
||||
string folder = "cloning/Normal_Cloning/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "destination1.png");
|
||||
string original_path3 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(destination.empty())
|
||||
{
|
||||
cout << "Could not load destination image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path3 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = 400;
|
||||
p.y = 100;
|
||||
|
||||
seamlessClone(source, destination, mask, p, result, 1);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else if(num == 2)
|
||||
{
|
||||
string folder = "cloning/Mixed_Cloning/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "destination1.png");
|
||||
string original_path3 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(destination.empty())
|
||||
{
|
||||
cout << "Could not load destination image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path3 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = destination.size().width/2;
|
||||
p.y = destination.size().height/2;
|
||||
|
||||
seamlessClone(source, destination, mask, p, result, 2);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else if(num == 3)
|
||||
{
|
||||
string folder = "cloning/Monochrome_Transfer/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "destination1.png");
|
||||
string original_path3 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat destination = imread(original_path2, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path3, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(destination.empty())
|
||||
{
|
||||
cout << "Could not load destination image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path3 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
Point p;
|
||||
p.x = destination.size().width/2;
|
||||
p.y = destination.size().height/2;
|
||||
|
||||
seamlessClone(source, destination, mask, p, result, 3);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else if(num == 4)
|
||||
{
|
||||
string folder = "cloning/color_change/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
|
||||
colorChange(source, mask, result, 1.5, .5, .5);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else if(num == 5)
|
||||
{
|
||||
string folder = "cloning/Illumination_Change/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
|
||||
illuminationChange(source, mask, result, 0.2f, 0.4f);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else if(num == 6)
|
||||
{
|
||||
string folder = "cloning/Texture_Flattening/";
|
||||
string original_path1 = samples::findFile(folder + "source1.png");
|
||||
string original_path2 = samples::findFile(folder + "mask.png");
|
||||
|
||||
Mat source = imread(original_path1, IMREAD_COLOR);
|
||||
Mat mask = imread(original_path2, IMREAD_COLOR);
|
||||
|
||||
if(source.empty())
|
||||
{
|
||||
cout << "Could not load source image " << original_path1 << endl;
|
||||
exit(0);
|
||||
}
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "Could not load mask image " << original_path2 << endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
Mat result;
|
||||
|
||||
textureFlattening(source, mask, result, 30, 45, 3);
|
||||
|
||||
imshow("Output",result);
|
||||
imwrite("cloned.png", result);
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Invalid selection: " << num << endl;
|
||||
exit(1);
|
||||
}
|
||||
waitKey(0);
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* create_mask.cpp
|
||||
*
|
||||
* Author:
|
||||
* Siddharth Kherada <siddharthkherada27[at]gmail[dot]com>
|
||||
*
|
||||
* This tutorial demonstrates how to make mask image (black and white).
|
||||
* The program takes as input a source image and outputs its corresponding
|
||||
* mask image.
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
Mat src, img1, mask, final;
|
||||
|
||||
Point point;
|
||||
vector<Point> pts;
|
||||
int drag = 0;
|
||||
int var = 0;
|
||||
int flag = 0;
|
||||
|
||||
void mouseHandler(int, int, int, int, void*);
|
||||
|
||||
void mouseHandler(int event, int x, int y, int, void*)
|
||||
{
|
||||
|
||||
if (event == EVENT_LBUTTONDOWN && !drag)
|
||||
{
|
||||
if (flag == 0)
|
||||
{
|
||||
if (var == 0)
|
||||
img1 = src.clone();
|
||||
point = Point(x, y);
|
||||
circle(img1, point, 2, Scalar(0, 0, 255), -1, 8, 0);
|
||||
pts.push_back(point);
|
||||
var++;
|
||||
drag = 1;
|
||||
|
||||
if (var > 1)
|
||||
line(img1,pts[var-2], point, Scalar(0, 0, 255), 2, 8, 0);
|
||||
|
||||
imshow("Source", img1);
|
||||
}
|
||||
}
|
||||
|
||||
if (event == EVENT_LBUTTONUP && drag)
|
||||
{
|
||||
imshow("Source", img1);
|
||||
drag = 0;
|
||||
}
|
||||
|
||||
if (event == EVENT_RBUTTONDOWN)
|
||||
{
|
||||
flag = 1;
|
||||
img1 = src.clone();
|
||||
|
||||
if (var != 0)
|
||||
{
|
||||
polylines( img1, pts, 1, Scalar(0,0,0), 2, 8, 0);
|
||||
}
|
||||
|
||||
imshow("Source", img1);
|
||||
}
|
||||
|
||||
if (event == EVENT_RBUTTONUP)
|
||||
{
|
||||
flag = var;
|
||||
final = Mat::zeros(src.size(), CV_8UC3);
|
||||
mask = Mat::zeros(src.size(), CV_8UC1);
|
||||
|
||||
fillPoly(mask, pts, Scalar(255, 255, 255), 8, 0);
|
||||
bitwise_and(src, src, final, mask);
|
||||
imshow("Mask", mask);
|
||||
imshow("Result", final);
|
||||
imshow("Source", img1);
|
||||
}
|
||||
|
||||
if (event == EVENT_MBUTTONDOWN)
|
||||
{
|
||||
pts.clear();
|
||||
var = 0;
|
||||
drag = 0;
|
||||
flag = 0;
|
||||
imshow("Source", src);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommandLineParser parser(argc, argv, "{@input | lena.jpg | input image}");
|
||||
parser.about("This program demonstrates using mouse events\n");
|
||||
parser.printMessage();
|
||||
cout << "\n\tleft mouse button - set a point to create mask shape\n"
|
||||
"\tright mouse button - create mask from points\n"
|
||||
"\tmiddle mouse button - reset\n";
|
||||
String input_image = parser.get<String>("@input");
|
||||
|
||||
src = imread(samples::findFile(input_image));
|
||||
|
||||
if (src.empty())
|
||||
{
|
||||
printf("Error opening image: %s\n", input_image.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
namedWindow("Source", WINDOW_AUTOSIZE);
|
||||
setMouseCallback("Source", mouseHandler, NULL);
|
||||
imshow("Source", src);
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\n This program demonstrates how to use BLOB to detect and filter region \n"
|
||||
<< "Usage: \n"
|
||||
<< argv[0]
|
||||
<< " <image1(detect_blob.png as default)>\n"
|
||||
<< "Press a key when image window is active to change descriptor";
|
||||
}
|
||||
|
||||
|
||||
static String Legende(SimpleBlobDetector::Params &pAct)
|
||||
{
|
||||
String s = "";
|
||||
if (pAct.filterByArea)
|
||||
{
|
||||
String inf = static_cast<const ostringstream&>(ostringstream() << pAct.minArea).str();
|
||||
String sup = static_cast<const ostringstream&>(ostringstream() << pAct.maxArea).str();
|
||||
s = " Area range [" + inf + " to " + sup + "]";
|
||||
}
|
||||
if (pAct.filterByCircularity)
|
||||
{
|
||||
String inf = static_cast<const ostringstream&>(ostringstream() << pAct.minCircularity).str();
|
||||
String sup = static_cast<const ostringstream&>(ostringstream() << pAct.maxCircularity).str();
|
||||
if (s.length() == 0)
|
||||
s = " Circularity range [" + inf + " to " + sup + "]";
|
||||
else
|
||||
s += " AND Circularity range [" + inf + " to " + sup + "]";
|
||||
}
|
||||
if (pAct.filterByColor)
|
||||
{
|
||||
String inf = static_cast<const ostringstream&>(ostringstream() << (int)pAct.blobColor).str();
|
||||
if (s.length() == 0)
|
||||
s = " Blob color " + inf;
|
||||
else
|
||||
s += " AND Blob color " + inf;
|
||||
}
|
||||
if (pAct.filterByConvexity)
|
||||
{
|
||||
String inf = static_cast<const ostringstream&>(ostringstream() << pAct.minConvexity).str();
|
||||
String sup = static_cast<const ostringstream&>(ostringstream() << pAct.maxConvexity).str();
|
||||
if (s.length() == 0)
|
||||
s = " Convexity range[" + inf + " to " + sup + "]";
|
||||
else
|
||||
s += " AND Convexity range[" + inf + " to " + sup + "]";
|
||||
}
|
||||
if (pAct.filterByInertia)
|
||||
{
|
||||
String inf = static_cast<const ostringstream&>(ostringstream() << pAct.minInertiaRatio).str();
|
||||
String sup = static_cast<const ostringstream&>(ostringstream() << pAct.maxInertiaRatio).str();
|
||||
if (s.length() == 0)
|
||||
s = " Inertia ratio range [" + inf + " to " + sup + "]";
|
||||
else
|
||||
s += " AND Inertia ratio range [" + inf + " to " + sup + "]";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
String fileName;
|
||||
cv::CommandLineParser parser(argc, argv, "{@input |detect_blob.png| }{h help | | }");
|
||||
if (parser.has("h"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
fileName = parser.get<string>("@input");
|
||||
Mat img = imread(samples::findFile(fileName), IMREAD_COLOR);
|
||||
if (img.empty())
|
||||
{
|
||||
cout << "Image " << fileName << " is empty or cannot be found\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
SimpleBlobDetector::Params pDefaultBLOB;
|
||||
// This is default parameters for SimpleBlobDetector
|
||||
pDefaultBLOB.thresholdStep = 10;
|
||||
pDefaultBLOB.minThreshold = 10;
|
||||
pDefaultBLOB.maxThreshold = 220;
|
||||
pDefaultBLOB.minRepeatability = 2;
|
||||
pDefaultBLOB.minDistBetweenBlobs = 10;
|
||||
pDefaultBLOB.filterByColor = false;
|
||||
pDefaultBLOB.blobColor = 0;
|
||||
pDefaultBLOB.filterByArea = false;
|
||||
pDefaultBLOB.minArea = 25;
|
||||
pDefaultBLOB.maxArea = 5000;
|
||||
pDefaultBLOB.filterByCircularity = false;
|
||||
pDefaultBLOB.minCircularity = 0.9f;
|
||||
pDefaultBLOB.maxCircularity = (float)1e37;
|
||||
pDefaultBLOB.filterByInertia = false;
|
||||
pDefaultBLOB.minInertiaRatio = 0.1f;
|
||||
pDefaultBLOB.maxInertiaRatio = (float)1e37;
|
||||
pDefaultBLOB.filterByConvexity = false;
|
||||
pDefaultBLOB.minConvexity = 0.95f;
|
||||
pDefaultBLOB.maxConvexity = (float)1e37;
|
||||
// Descriptor array for BLOB
|
||||
vector<String> typeDesc;
|
||||
// Param array for BLOB
|
||||
vector<SimpleBlobDetector::Params> pBLOB;
|
||||
vector<SimpleBlobDetector::Params>::iterator itBLOB;
|
||||
// Color palette
|
||||
vector< Vec3b > palette;
|
||||
for (int i = 0; i<65536; i++)
|
||||
{
|
||||
uchar c1 = (uchar)rand();
|
||||
uchar c2 = (uchar)rand();
|
||||
uchar c3 = (uchar)rand();
|
||||
palette.push_back(Vec3b(c1, c2, c3));
|
||||
}
|
||||
help(argv);
|
||||
|
||||
|
||||
// These descriptors are going to be detecting and computing BLOBS with 6 different params
|
||||
// Param for first BLOB detector we want all
|
||||
typeDesc.push_back("BLOB"); // see http://docs.opencv.org/5.x/d0/d7a/classcv_1_1SimpleBlobDetector.html
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByArea = true;
|
||||
pBLOB.back().minArea = 1;
|
||||
pBLOB.back().maxArea = float(img.rows*img.cols);
|
||||
// Param for second BLOB detector we want area between 500 and 2900 pixels
|
||||
typeDesc.push_back("BLOB");
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByArea = true;
|
||||
pBLOB.back().minArea = 500;
|
||||
pBLOB.back().maxArea = 2900;
|
||||
// Param for third BLOB detector we want only circular object
|
||||
typeDesc.push_back("BLOB");
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByCircularity = true;
|
||||
// Param for Fourth BLOB detector we want ratio inertia
|
||||
typeDesc.push_back("BLOB");
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByInertia = true;
|
||||
pBLOB.back().minInertiaRatio = 0;
|
||||
pBLOB.back().maxInertiaRatio = (float)0.2;
|
||||
// Param for fifth BLOB detector we want ratio inertia
|
||||
typeDesc.push_back("BLOB");
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByConvexity = true;
|
||||
pBLOB.back().minConvexity = 0.;
|
||||
pBLOB.back().maxConvexity = (float)0.9;
|
||||
// Param for six BLOB detector we want blob with gravity center color equal to 0
|
||||
typeDesc.push_back("BLOB");
|
||||
pBLOB.push_back(pDefaultBLOB);
|
||||
pBLOB.back().filterByColor = true;
|
||||
pBLOB.back().blobColor = 0;
|
||||
|
||||
itBLOB = pBLOB.begin();
|
||||
vector<double> desMethCmp;
|
||||
Ptr<Feature2D> b;
|
||||
String label;
|
||||
// Descriptor loop
|
||||
vector<String>::iterator itDesc;
|
||||
for (itDesc = typeDesc.begin(); itDesc != typeDesc.end(); ++itDesc)
|
||||
{
|
||||
vector<KeyPoint> keyImg1;
|
||||
if (*itDesc == "BLOB")
|
||||
{
|
||||
b = SimpleBlobDetector::create(*itBLOB);
|
||||
label = Legende(*itBLOB);
|
||||
++itBLOB;
|
||||
}
|
||||
try
|
||||
{
|
||||
// We can detect keypoint with detect method
|
||||
vector<KeyPoint> keyImg;
|
||||
vector<Rect> zone;
|
||||
vector<vector <Point> > region;
|
||||
Mat desc, result(img.rows, img.cols, CV_8UC3);
|
||||
if (b.dynamicCast<SimpleBlobDetector>().get())
|
||||
{
|
||||
Ptr<SimpleBlobDetector> sbd = b.dynamicCast<SimpleBlobDetector>();
|
||||
sbd->detect(img, keyImg, Mat());
|
||||
drawKeypoints(img, keyImg, result);
|
||||
int i = 0;
|
||||
for (vector<KeyPoint>::iterator k = keyImg.begin(); k != keyImg.end(); ++k, ++i)
|
||||
circle(result, k->pt, (int)k->size, palette[i % 65536]);
|
||||
}
|
||||
namedWindow(*itDesc + label, WINDOW_AUTOSIZE);
|
||||
imshow(*itDesc + label, result);
|
||||
imshow("Original", img);
|
||||
waitKey();
|
||||
}
|
||||
catch (const Exception& e)
|
||||
{
|
||||
cout << "Feature : " << *itDesc << "\n";
|
||||
cout << e.msg << endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void convolveDFT(InputArray A, InputArray B, OutputArray C) {
|
||||
// Calculate the size of the output array
|
||||
int outputRows = A.rows() + B.rows() - 1;
|
||||
int outputCols = A.cols() + B.cols() - 1;
|
||||
|
||||
// Reallocate the output array if needed
|
||||
C.create(outputRows, outputCols, A.type());
|
||||
|
||||
Size dftSize;
|
||||
// Calculate the size of DFT transform
|
||||
dftSize.width = getOptimalDFTSize(A.cols() + B.cols() - 1);
|
||||
dftSize.height = getOptimalDFTSize(A.rows() + B.rows() - 1);
|
||||
|
||||
// Allocate temporary buffers and initialize them with 0's
|
||||
Mat tempA(dftSize, A.type(), Scalar::all(0));
|
||||
Mat tempB(dftSize, B.type(), Scalar::all(0));
|
||||
|
||||
// Copy A and B to the top-left corners of tempA and tempB, respectively
|
||||
Mat roiA(tempA, Rect(0, 0, A.cols(), A.rows()));
|
||||
A.copyTo(roiA);
|
||||
Mat roiB(tempB, Rect(0, 0, B.cols(), B.rows()));
|
||||
B.copyTo(roiB);
|
||||
|
||||
// Now transform the padded A & B in-place;
|
||||
// use "nonzeroRows" hint for faster processing
|
||||
dft(tempA, tempA, 0, A.rows());
|
||||
dft(tempB, tempB, 0, B.rows());
|
||||
|
||||
// Multiply the spectrums;
|
||||
// the function handles packed spectrum representations well
|
||||
mulSpectrums(tempA, tempB, tempA, 0);
|
||||
|
||||
// Transform the product back from the frequency domain.
|
||||
// Even though all the result rows will be non-zero,
|
||||
// you need only the first C.rows of them, and thus you
|
||||
// pass nonzeroRows == C.rows
|
||||
dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows());
|
||||
|
||||
// Now copy the result back to C.
|
||||
tempA(Rect(0, 0, C.cols(), C.rows())).copyTo(C);
|
||||
|
||||
// All the temporary buffers will be deallocated automatically
|
||||
}
|
||||
static void help(const char ** argv)
|
||||
{
|
||||
printf("\nThis program demonstrates the use of convolution using discrete Fourier transform (DFT)\n"
|
||||
"An image is convolved with kernel filter using DFT.\n"
|
||||
"Usage:\n %s [input -- default lena.jpg]\n", argv[0]);
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h||}{@input|lena.jpg|input image file}"
|
||||
};
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
// Load the image in grayscale
|
||||
help(argv);
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
string filename = parser.get<string>(0);
|
||||
Mat img = imread(samples::findFile(filename), IMREAD_GRAYSCALE);
|
||||
|
||||
// Check if the image is loaded successfully
|
||||
if (img.empty()) {
|
||||
std::cerr << "Error: Image not loaded!" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert the image to CV_32F
|
||||
Mat img_32f;
|
||||
img.convertTo(img_32f, CV_32F);
|
||||
|
||||
float kernelData[9] = { 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9, 1.0f/9 }; // example of blur filter, can be changed to other filter as well.
|
||||
Mat kernel(3, 3, CV_32F, kernelData);
|
||||
|
||||
// Perform convolution of the image with the sharpening kernel
|
||||
Mat result;
|
||||
convolveDFT(img_32f, kernel, result);
|
||||
|
||||
// Normalize the result for better visualization
|
||||
normalize(result, result, 0, 255, NORM_MINMAX);
|
||||
|
||||
// Convert result back to 8-bit for display
|
||||
Mat result_8u;
|
||||
result.convertTo(result_8u, CV_8U);
|
||||
|
||||
// Display the images
|
||||
imshow("Original Image", img);
|
||||
imshow("Output Image", result_8u);
|
||||
|
||||
waitKey(0); // Wait for a key press to close the windows
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/video.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
CommandLineParser parser(argc, argv, "{ @video | vtest.avi | use video as input }");
|
||||
string filename = samples::findFileOrKeep(parser.get<string>("@video"));
|
||||
|
||||
VideoCapture cap;
|
||||
cap.open(filename);
|
||||
|
||||
if(!cap.isOpened())
|
||||
{
|
||||
printf("ERROR: Cannot open file %s\n", filename.c_str());
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat prevgray, gray, rgb, frame;
|
||||
Mat flow, flow_uv[2];
|
||||
Mat mag, ang;
|
||||
Mat hsv_split[3], hsv;
|
||||
char ret;
|
||||
|
||||
Ptr<DenseOpticalFlow> algorithm = DISOpticalFlow::create(DISOpticalFlow::PRESET_MEDIUM);
|
||||
|
||||
while(true)
|
||||
{
|
||||
cap >> frame;
|
||||
if (frame.empty())
|
||||
break;
|
||||
|
||||
cvtColor(frame, gray, COLOR_BGR2GRAY);
|
||||
|
||||
if (!prevgray.empty())
|
||||
{
|
||||
algorithm->calc(prevgray, gray, flow);
|
||||
split(flow, flow_uv);
|
||||
multiply(flow_uv[1], -1, flow_uv[1]);
|
||||
cartToPolar(flow_uv[0], flow_uv[1], mag, ang, true);
|
||||
normalize(mag, mag, 0, 1, NORM_MINMAX);
|
||||
hsv_split[0] = ang;
|
||||
hsv_split[1] = mag;
|
||||
hsv_split[2] = Mat::ones(ang.size(), ang.type());
|
||||
merge(hsv_split, 3, hsv);
|
||||
cvtColor(hsv, rgb, COLOR_HSV2BGR);
|
||||
imshow("flow", rgb);
|
||||
imshow("orig", frame);
|
||||
}
|
||||
|
||||
if ((ret = (char)waitKey(20)) > 0)
|
||||
break;
|
||||
std::swap(prevgray, gray);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int maskSize0 = DIST_MASK_5;
|
||||
int voronoiType = -1;
|
||||
int edgeThresh = 100;
|
||||
int distType0 = DIST_L1;
|
||||
|
||||
// The output and temporary images
|
||||
Mat gray;
|
||||
|
||||
// threshold trackbar callback
|
||||
static void onTrackbar( int, void* )
|
||||
{
|
||||
static const Scalar colors[] =
|
||||
{
|
||||
Scalar(0,0,0),
|
||||
Scalar(255,0,0),
|
||||
Scalar(255,128,0),
|
||||
Scalar(255,255,0),
|
||||
Scalar(0,255,0),
|
||||
Scalar(0,128,255),
|
||||
Scalar(0,255,255),
|
||||
Scalar(0,0,255),
|
||||
Scalar(255,0,255)
|
||||
};
|
||||
|
||||
int maskSize = voronoiType >= 0 ? DIST_MASK_5 : maskSize0;
|
||||
int distType = voronoiType >= 0 ? DIST_L2 : distType0;
|
||||
|
||||
Mat edge = gray >= edgeThresh, dist, labels, dist8u;
|
||||
|
||||
if( voronoiType < 0 )
|
||||
distanceTransform( edge, dist, distType, maskSize );
|
||||
else
|
||||
distanceTransform( edge, dist, labels, distType, maskSize, voronoiType );
|
||||
|
||||
if( voronoiType < 0 )
|
||||
{
|
||||
// begin "painting" the distance transform result
|
||||
dist *= 5000;
|
||||
pow(dist, 0.5, dist);
|
||||
|
||||
Mat dist32s, dist8u1, dist8u2;
|
||||
|
||||
dist.convertTo(dist32s, CV_32S, 1, 0.5);
|
||||
dist32s &= Scalar::all(255);
|
||||
|
||||
dist32s.convertTo(dist8u1, CV_8U, 1, 0);
|
||||
dist32s *= -1;
|
||||
|
||||
dist32s += Scalar::all(255);
|
||||
dist32s.convertTo(dist8u2, CV_8U);
|
||||
|
||||
Mat planes[] = {dist8u1, dist8u2, dist8u2};
|
||||
merge(planes, 3, dist8u);
|
||||
}
|
||||
else
|
||||
{
|
||||
dist8u.create(labels.size(), CV_8UC3);
|
||||
for( int i = 0; i < labels.rows; i++ )
|
||||
{
|
||||
const int* ll = (const int*)labels.ptr(i);
|
||||
const float* dd = (const float*)dist.ptr(i);
|
||||
uchar* d = (uchar*)dist8u.ptr(i);
|
||||
for( int j = 0; j < labels.cols; j++ )
|
||||
{
|
||||
int idx = ll[j] == 0 || dd[j] == 0 ? 0 : (ll[j]-1)%8 + 1;
|
||||
float scale = 1.f/(1 + dd[j]*dd[j]*0.0004f);
|
||||
int b = cvRound(colors[idx][0]*scale);
|
||||
int g = cvRound(colors[idx][1]*scale);
|
||||
int r = cvRound(colors[idx][2]*scale);
|
||||
d[j*3] = (uchar)b;
|
||||
d[j*3+1] = (uchar)g;
|
||||
d[j*3+2] = (uchar)r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imshow("Distance Map", dist8u );
|
||||
}
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
printf("\nProgram to demonstrate the use of the distance transform function between edge images.\n"
|
||||
"Usage:\n"
|
||||
"%s [image_name -- default image is stuff.jpg]\n"
|
||||
"\nHot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tC - use C/Inf metric\n"
|
||||
"\tL1 - use L1 metric\n"
|
||||
"\tL2 - use L2 metric\n"
|
||||
"\t3 - use 3x3 mask\n"
|
||||
"\t5 - use 5x5 mask\n"
|
||||
"\t0 - use precise distance transform\n"
|
||||
"\tv - switch to Voronoi diagram mode\n"
|
||||
"\tp - switch to pixel-based Voronoi diagram mode\n"
|
||||
"\tSPACE - loop through all the modes\n\n", argv[0]);
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h||}{@image |stuff.jpg|input image file}"
|
||||
};
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
help(argv);
|
||||
if (parser.has("help"))
|
||||
return 0;
|
||||
string filename = parser.get<string>(0);
|
||||
gray = imread(samples::findFile(filename), 0);
|
||||
if(gray.empty())
|
||||
{
|
||||
printf("Cannot read image file: %s\n", filename.c_str());
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
namedWindow("Distance Map", 1);
|
||||
createTrackbar("Brightness Threshold", "Distance Map", &edgeThresh, 255, onTrackbar, 0);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Call to update the view
|
||||
onTrackbar(0, 0);
|
||||
|
||||
char c = (char)waitKey(0);
|
||||
|
||||
if( c == 27 )
|
||||
break;
|
||||
|
||||
if( c == 'c' || c == 'C' || c == '1' || c == '2' ||
|
||||
c == '3' || c == '5' || c == '0' )
|
||||
voronoiType = -1;
|
||||
|
||||
if( c == 'c' || c == 'C' )
|
||||
distType0 = DIST_C;
|
||||
else if( c == '1' )
|
||||
distType0 = DIST_L1;
|
||||
else if( c == '2' )
|
||||
distType0 = DIST_L2;
|
||||
else if( c == '3' )
|
||||
maskSize0 = DIST_MASK_3;
|
||||
else if( c == '5' )
|
||||
maskSize0 = DIST_MASK_5;
|
||||
else if( c == '0' )
|
||||
maskSize0 = DIST_MASK_PRECISE;
|
||||
else if( c == 'v' )
|
||||
voronoiType = 0;
|
||||
else if( c == 'p' )
|
||||
voronoiType = 1;
|
||||
else if( c == ' ' )
|
||||
{
|
||||
if( voronoiType == 0 )
|
||||
voronoiType = 1;
|
||||
else if( voronoiType == 1 )
|
||||
{
|
||||
voronoiType = -1;
|
||||
maskSize0 = DIST_MASK_3;
|
||||
distType0 = DIST_C;
|
||||
}
|
||||
else if( distType0 == DIST_C )
|
||||
distType0 = DIST_L1;
|
||||
else if( distType0 == DIST_L1 )
|
||||
distType0 = DIST_L2;
|
||||
else if( maskSize0 == DIST_MASK_3 )
|
||||
maskSize0 = DIST_MASK_5;
|
||||
else if( maskSize0 == DIST_MASK_5 )
|
||||
maskSize0 = DIST_MASK_PRECISE;
|
||||
else if( maskSize0 == DIST_MASK_PRECISE )
|
||||
voronoiType = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "opencv2/core/utility.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
int edgeThresh = 1;
|
||||
int edgeThreshScharr=1;
|
||||
|
||||
Mat image, gray, blurImage, edge1, edge2, cedge;
|
||||
|
||||
const char* window_name1 = "Edge map : Canny default (Sobel gradient)";
|
||||
const char* window_name2 = "Edge map : Canny with custom gradient (Scharr)";
|
||||
|
||||
// define a trackbar callback
|
||||
static void onTrackbar(int, void*)
|
||||
{
|
||||
blur(gray, blurImage, Size(3,3));
|
||||
|
||||
// Run the edge detector on grayscale
|
||||
Canny(blurImage, edge1, edgeThresh, edgeThresh*3, 3);
|
||||
cedge = Scalar::all(0);
|
||||
|
||||
image.copyTo(cedge, edge1);
|
||||
imshow(window_name1, cedge);
|
||||
|
||||
/// Canny detector with scharr
|
||||
Mat dx,dy;
|
||||
Scharr(blurImage,dx,CV_16S,1,0);
|
||||
Scharr(blurImage,dy,CV_16S,0,1);
|
||||
Canny( dx,dy, edge2, edgeThreshScharr, edgeThreshScharr*3 );
|
||||
/// Using Canny's output as a mask, we display our result
|
||||
cedge = Scalar::all(0);
|
||||
image.copyTo(cedge, edge2);
|
||||
imshow(window_name2, cedge);
|
||||
}
|
||||
|
||||
static void help(const char** argv)
|
||||
{
|
||||
printf("\nThis sample demonstrates Canny edge detection\n"
|
||||
"Call:\n"
|
||||
" %s [image_name -- Default is fruits.jpg]\n\n", argv[0]);
|
||||
}
|
||||
|
||||
const char* keys =
|
||||
{
|
||||
"{help h||}{@image |fruits.jpg|input image name}"
|
||||
};
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
help(argv);
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
string filename = parser.get<string>(0);
|
||||
|
||||
image = imread(samples::findFile(filename), IMREAD_COLOR);
|
||||
if(image.empty())
|
||||
{
|
||||
printf("Cannot read image file: %s\n", filename.c_str());
|
||||
help(argv);
|
||||
return -1;
|
||||
}
|
||||
cedge.create(image.size(), image.type());
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
|
||||
// Create a window
|
||||
namedWindow(window_name1, 1);
|
||||
namedWindow(window_name2, 1);
|
||||
|
||||
// create a toolbar
|
||||
createTrackbar("Canny threshold default", window_name1, &edgeThresh, 100, onTrackbar);
|
||||
createTrackbar("Canny threshold Scharr", window_name2, &edgeThreshScharr, 400, onTrackbar);
|
||||
|
||||
// Show the image
|
||||
onTrackbar(0, 0);
|
||||
|
||||
// Wait for a key stroke; the same function arranges events processing
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main(int args, char** argv) {
|
||||
std::string img_name1, img_name2;
|
||||
if (args < 3) {
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Path to two images \nFor example: "
|
||||
"./epipolar_lines img1.jpg img2.jpg");
|
||||
} else {
|
||||
img_name1 = argv[1];
|
||||
img_name2 = argv[2];
|
||||
}
|
||||
|
||||
Mat image1 = imread(img_name1);
|
||||
Mat image2 = imread(img_name2);
|
||||
Mat descriptors1, descriptors2;
|
||||
std::vector<KeyPoint> keypoints1, keypoints2;
|
||||
|
||||
Ptr<SIFT> detector = SIFT::create();
|
||||
detector->detect(image1, keypoints1);
|
||||
detector->detect(image2, keypoints2);
|
||||
detector->compute(image1, keypoints1, descriptors1);
|
||||
detector->compute(image2, keypoints2, descriptors2);
|
||||
|
||||
FlannBasedMatcher matcher(makePtr<flann::KDTreeIndexParams>(5), makePtr<flann::SearchParams>(32));
|
||||
|
||||
// get k=2 best match that we can apply ratio test explained by D.Lowe
|
||||
std::vector<std::vector<DMatch>> matches_vector;
|
||||
matcher.knnMatch(descriptors1, descriptors2, matches_vector, 2);
|
||||
|
||||
std::vector<Point2d> pts1, pts2;
|
||||
pts1.reserve(matches_vector.size()); pts2.reserve(matches_vector.size());
|
||||
for (const auto &m : matches_vector) {
|
||||
// compare best and second match using Lowe ratio test
|
||||
if (m[0].distance / m[1].distance < 0.75) {
|
||||
pts1.emplace_back(keypoints1[m[0].queryIdx].pt);
|
||||
pts2.emplace_back(keypoints2[m[0].trainIdx].pt);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Number of points " << pts1.size() << '\n';
|
||||
|
||||
Mat inliers;
|
||||
const auto begin_time = std::chrono::steady_clock::now();
|
||||
const Mat F = findFundamentalMat(pts1, pts2, RANSAC, 1., 0.99, 2000, inliers);
|
||||
std::cout << "RANSAC fundamental matrix time " << static_cast<int>(std::chrono::duration_cast<std::chrono::microseconds>
|
||||
(std::chrono::steady_clock::now() - begin_time).count()) << "\n";
|
||||
|
||||
Mat points1 = Mat((int)pts1.size(), 2, CV_64F, pts1.data());
|
||||
Mat points2 = Mat((int)pts2.size(), 2, CV_64F, pts2.data());
|
||||
vconcat(points1.t(), Mat::ones(1, points1.rows, points1.type()), points1);
|
||||
vconcat(points2.t(), Mat::ones(1, points2.rows, points2.type()), points2);
|
||||
|
||||
RNG rng;
|
||||
const int circle_sz = 3, line_sz = 1, max_lines = 300;
|
||||
std::vector<int> pts_shuffle (points1.cols);
|
||||
for (int i = 0; i < points1.cols; i++)
|
||||
pts_shuffle[i] = i;
|
||||
randShuffle(pts_shuffle);
|
||||
int plot_lines = 0, num_inliers = 0;
|
||||
double mean_err = 0;
|
||||
for (int pt : pts_shuffle) {
|
||||
if (inliers.at<uchar>(pt)) {
|
||||
const Scalar col (rng.uniform(0,256), rng.uniform(0,256), rng.uniform(0,256));
|
||||
const Mat l2 = F * points1.col(pt);
|
||||
const Mat l1 = F.t() * points2.col(pt);
|
||||
double a1 = l1.at<double>(0), b1 = l1.at<double>(1), c1 = l1.at<double>(2);
|
||||
double a2 = l2.at<double>(0), b2 = l2.at<double>(1), c2 = l2.at<double>(2);
|
||||
const double mag1 = sqrt(a1*a1 + b1*b1), mag2 = (a2*a2 + b2*b2);
|
||||
a1 /= mag1; b1 /= mag1; c1 /= mag1; a2 /= mag2; b2 /= mag2; c2 /= mag2;
|
||||
if (plot_lines++ < max_lines) {
|
||||
line(image1, Point2d(0, -c1/b1),
|
||||
Point2d((double)image1.cols, -(a1*image1.cols+c1)/b1), col, line_sz);
|
||||
line(image2, Point2d(0, -c2/b2),
|
||||
Point2d((double)image2.cols, -(a2*image2.cols+c2)/b2), col, line_sz);
|
||||
}
|
||||
circle (image1, pts1[pt], circle_sz, col, -1);
|
||||
circle (image2, pts2[pt], circle_sz, col, -1);
|
||||
mean_err += (fabs(points1.col(pt).dot(l2)) / mag2 + fabs(points2.col(pt).dot(l1) / mag1)) / 2;
|
||||
num_inliers++;
|
||||
}
|
||||
}
|
||||
std::cout << "Mean distance from tentative inliers to epipolar lines " << mean_err/num_inliers
|
||||
<< " number of inliers " << num_inliers << "\n";
|
||||
// concatenate two images
|
||||
hconcat(image1, image2, image1);
|
||||
const int new_img_size = 1200 * 800; // for example
|
||||
// resize with the same aspect ratio
|
||||
resize(image1, image1, Size((int) sqrt ((double) image1.cols * new_img_size / image1.rows),
|
||||
(int)sqrt ((double) image1.rows * new_img_size / image1.cols)));
|
||||
|
||||
imshow("epipolar lines, image 1, 2", image1);
|
||||
imwrite("epipolar_lines.png", image1);
|
||||
waitKey(0);
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
enum MyShape{MyCIRCLE=0,MyRECTANGLE,MyELLIPSE};
|
||||
|
||||
struct ParamColorMap {
|
||||
int iColormap;
|
||||
Mat img;
|
||||
};
|
||||
|
||||
String winName="False color";
|
||||
static const String ColorMaps[] = { "Autumn", "Bone", "Jet", "Winter", "Rainbow", "Ocean", "Summer", "Spring",
|
||||
"Cool", "HSV", "Pink", "Hot", "Parula", "Magma", "Inferno", "Plasma", "Viridis",
|
||||
"Cividis", "Twilight", "Twilight Shifted", "Turbo", "Deep Green", "User defined (random)" };
|
||||
|
||||
static void TrackColorMap(int x, void *r)
|
||||
{
|
||||
ParamColorMap *p = (ParamColorMap*)r;
|
||||
Mat dst;
|
||||
p->iColormap= x;
|
||||
if (x == COLORMAP_DEEPGREEN + 1)
|
||||
{
|
||||
Mat lutRND(256, 1, CV_8UC3);
|
||||
randu(lutRND, Scalar(0, 0, 0), Scalar(255, 255, 255));
|
||||
applyColorMap(p->img, dst, lutRND);
|
||||
}
|
||||
else
|
||||
applyColorMap(p->img,dst,p->iColormap);
|
||||
|
||||
putText(dst, "Colormap : "+ColorMaps[p->iColormap], Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(255, 255, 255),2);
|
||||
imshow(winName, dst);
|
||||
}
|
||||
|
||||
|
||||
static Mat DrawMyImage(int thickness,int nbShape)
|
||||
{
|
||||
Mat img=Mat::zeros(500,256*thickness+100,CV_8UC1);
|
||||
int offsetx = 50, offsety = 25;
|
||||
int lineLength = 50;
|
||||
|
||||
for (int i=0;i<256;i++)
|
||||
line(img,Point(thickness*i+ offsetx, offsety),Point(thickness*i+ offsetx, offsety+ lineLength),Scalar(i), thickness);
|
||||
RNG r;
|
||||
Point center;
|
||||
int radius;
|
||||
int width,height;
|
||||
int angle;
|
||||
Rect rc;
|
||||
|
||||
for (int i=1;i<=nbShape;i++)
|
||||
{
|
||||
int typeShape = r.uniform(MyCIRCLE, MyELLIPSE+1);
|
||||
switch (typeShape) {
|
||||
case MyCIRCLE:
|
||||
center = Point(r.uniform(offsetx,img.cols- offsetx), r.uniform(offsety + lineLength, img.rows - offsety));
|
||||
radius = r.uniform(1, min(offsetx, offsety));
|
||||
circle(img,center,radius,Scalar(i),-1);
|
||||
break;
|
||||
case MyRECTANGLE:
|
||||
center = Point(r.uniform(offsetx, img.cols - offsetx), r.uniform(offsety + lineLength, img.rows - offsety));
|
||||
width = r.uniform(1, min(offsetx, offsety));
|
||||
height = r.uniform(1, min(offsetx, offsety));
|
||||
rc = Rect(center-Point(width ,height )/2, center + Point(width , height )/2);
|
||||
rectangle(img,rc, Scalar(i), -1);
|
||||
break;
|
||||
case MyELLIPSE:
|
||||
center = Point(r.uniform(offsetx, img.cols - offsetx), r.uniform(offsety + lineLength, img.rows - offsety));
|
||||
width = r.uniform(1, min(offsetx, offsety));
|
||||
height = r.uniform(1, min(offsetx, offsety));
|
||||
angle = r.uniform(0, 180);
|
||||
ellipse(img, center,Size(width/2,height/2),angle,0,360, Scalar(i), -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cout << "This program demonstrates the use of applyColorMap function.\n\n";
|
||||
|
||||
ParamColorMap p;
|
||||
Mat img;
|
||||
|
||||
if (argc > 1)
|
||||
img = imread(samples::findFile(argv[1]), IMREAD_GRAYSCALE);
|
||||
else
|
||||
img = DrawMyImage(2,256);
|
||||
|
||||
p.img=img;
|
||||
p.iColormap=0;
|
||||
|
||||
imshow("Gray image",img);
|
||||
namedWindow(winName);
|
||||
createTrackbar("colormap", winName, NULL, COLORMAP_DEEPGREEN + 1, TrackColorMap, (void*)&p);
|
||||
setTrackbarMin("colormap", winName, COLORMAP_AUTUMN);
|
||||
setTrackbarMax("colormap", winName, COLORMAP_DEEPGREEN + 1);
|
||||
setTrackbarPos("colormap", winName, COLORMAP_AUTUMN);
|
||||
|
||||
TrackColorMap(0, (void*)&p);
|
||||
|
||||
cout << "Press a key to exit" << endl;
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Author: Steve Nicholson
|
||||
*
|
||||
* A program that illustrates intersectConvexConvex in various scenarios
|
||||
*/
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
// Create a vector of points describing a rectangle with the given corners
|
||||
static vector<Point> makeRectangle(Point topLeft, Point bottomRight)
|
||||
{
|
||||
vector<Point> rectangle;
|
||||
rectangle.push_back(topLeft);
|
||||
rectangle.push_back(Point(bottomRight.x, topLeft.y));
|
||||
rectangle.push_back(bottomRight);
|
||||
rectangle.push_back(Point(topLeft.x, bottomRight.y));
|
||||
return rectangle;
|
||||
}
|
||||
|
||||
static vector<Point> makeTriangle(Point point1, Point point2, Point point3)
|
||||
{
|
||||
vector<Point> triangle;
|
||||
triangle.push_back(point1);
|
||||
triangle.push_back(point2);
|
||||
triangle.push_back(point3);
|
||||
return triangle;
|
||||
}
|
||||
|
||||
// Run intersectConvexConvex on two polygons then draw the polygons and their intersection (if there is one)
|
||||
// Return the area of the intersection
|
||||
static float drawIntersection(Mat &image, vector<Point> polygon1, vector<Point> polygon2, bool handleNested = true)
|
||||
{
|
||||
vector<Point> intersectionPolygon;
|
||||
|
||||
vector<vector<Point> > polygons;
|
||||
polygons.push_back(polygon1);
|
||||
polygons.push_back(polygon2);
|
||||
|
||||
float intersectArea = intersectConvexConvex(polygon1, polygon2, intersectionPolygon, handleNested);
|
||||
|
||||
if (intersectArea > 0)
|
||||
{
|
||||
Scalar fillColor(200, 200, 200);
|
||||
// If the input is invalid, draw the intersection in red
|
||||
if (!isContourConvex(polygon1) || !isContourConvex(polygon2))
|
||||
{
|
||||
fillColor = Scalar(0, 0, 255);
|
||||
}
|
||||
fillPoly(image, intersectionPolygon, fillColor);
|
||||
}
|
||||
polylines(image, polygons, true, Scalar(0, 0, 0));
|
||||
|
||||
return intersectArea;
|
||||
}
|
||||
|
||||
static void drawDescription(Mat &image, int intersectionArea, string description, Point origin)
|
||||
{
|
||||
const size_t bufSize=1024;
|
||||
char caption[bufSize];
|
||||
snprintf(caption, bufSize, "Intersection area: %d%s", intersectionArea, description.c_str());
|
||||
putText(image, caption, origin, FONT_HERSHEY_SIMPLEX, 0.6, Scalar(0, 0, 0));
|
||||
}
|
||||
|
||||
static void intersectConvexExample()
|
||||
{
|
||||
Mat image(610, 550, CV_8UC3, Scalar(255, 255, 255));
|
||||
float intersectionArea;
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 10), Point(50, 50)),
|
||||
makeRectangle(Point(20, 20), Point(60, 60)));
|
||||
|
||||
drawDescription(image, (int)intersectionArea, "", Point(70, 40));
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 70), Point(35, 95)),
|
||||
makeRectangle(Point(35, 95), Point(60, 120)));
|
||||
|
||||
drawDescription(image, (int)intersectionArea, "", Point(70, 100));
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 130), Point(60, 180)),
|
||||
makeRectangle(Point(20, 140), Point(50, 170)),
|
||||
true);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested true)", Point(70, 160));
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 190), Point(60, 240)),
|
||||
makeRectangle(Point(20, 200), Point(50, 230)),
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested false)", Point(70, 220));
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 250), Point(60, 300)),
|
||||
makeRectangle(Point(20, 250), Point(50, 290)),
|
||||
true);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested true)", Point(70, 280));
|
||||
|
||||
// These rectangles share an edge so handleNested can be false and an intersection is still found
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 310), Point(60, 360)),
|
||||
makeRectangle(Point(20, 310), Point(50, 350)),
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested false)", Point(70, 340));
|
||||
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 370), Point(60, 420)),
|
||||
makeRectangle(Point(20, 371), Point(50, 410)),
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested false)", Point(70, 400));
|
||||
|
||||
// A vertex of the triangle lies on an edge of the rectangle so handleNested can be false and an intersection is still found
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 430), Point(60, 480)),
|
||||
makeTriangle(Point(35, 430), Point(20, 470), Point(50, 470)),
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (handleNested false)", Point(70, 460));
|
||||
|
||||
// Show intersection of overlapping rectangle and triangle
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 490), Point(40, 540)),
|
||||
makeTriangle(Point(25, 500), Point(25, 530), Point(60, 515)),
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, "", Point(70, 520));
|
||||
|
||||
// This concave polygon is invalid input to intersectConvexConvex so it returns an invalid intersection
|
||||
vector<Point> notConvex;
|
||||
notConvex.push_back(Point(25, 560));
|
||||
notConvex.push_back(Point(25, 590));
|
||||
notConvex.push_back(Point(45, 580));
|
||||
notConvex.push_back(Point(60, 600));
|
||||
notConvex.push_back(Point(60, 550));
|
||||
notConvex.push_back(Point(45, 570));
|
||||
intersectionArea = drawIntersection(image,
|
||||
makeRectangle(Point(10, 550), Point(50, 600)),
|
||||
notConvex,
|
||||
false);
|
||||
|
||||
drawDescription(image, (int)intersectionArea, " (invalid input: not convex)", Point(70, 580));
|
||||
|
||||
imshow("Intersections", image);
|
||||
waitKey(0);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
intersectConvexExample();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "opencv2/video/tracking.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core/cvdef.h"
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static inline Point calcPoint(Point2f center, double R, double angle)
|
||||
{
|
||||
return center + Point2f((float)cos(angle), (float)-sin(angle))*(float)R;
|
||||
}
|
||||
|
||||
static void help()
|
||||
{
|
||||
printf( "\nExample of c calls to OpenCV's Kalman filter.\n"
|
||||
" Tracking of rotating point.\n"
|
||||
" Point moves in a circle and is characterized by a 1D state.\n"
|
||||
" state_k+1 = state_k + speed + process_noise N(0, 1e-5)\n"
|
||||
" The speed is constant.\n"
|
||||
" Both state and measurements vectors are 1D (a point angle),\n"
|
||||
" Measurement is the real state + gaussian noise N(0, 1e-1).\n"
|
||||
" The real and the measured points are connected with red line segment,\n"
|
||||
" the real and the estimated points are connected with yellow line segment,\n"
|
||||
" the real and the corrected estimated points are connected with green line segment.\n"
|
||||
" (if Kalman filter works correctly,\n"
|
||||
" the yellow segment should be shorter than the red one and\n"
|
||||
" the green segment should be shorter than the yellow one)."
|
||||
"\n"
|
||||
" Pressing any key (except ESC) will reset the tracking.\n"
|
||||
" Pressing ESC will stop the program.\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
help();
|
||||
Mat img(500, 500, CV_8UC3);
|
||||
KalmanFilter KF(2, 1, 0);
|
||||
Mat state(2, 1, CV_32F); /* (phi, delta_phi) */
|
||||
Mat processNoise(2, 1, CV_32F);
|
||||
Mat measurement = Mat::zeros(1, 1, CV_32F);
|
||||
char code = (char)-1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
img = Scalar::all(0);
|
||||
state.at<float>(0) = 0.0f;
|
||||
state.at<float>(1) = 2.f * (float)CV_PI / 6;
|
||||
KF.transitionMatrix = Mat_<float>({2, 2}, {1, 1, 0, 1});
|
||||
|
||||
setIdentity(KF.measurementMatrix);
|
||||
setIdentity(KF.processNoiseCov, Scalar::all(1e-5));
|
||||
setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1));
|
||||
setIdentity(KF.errorCovPost, Scalar::all(1));
|
||||
|
||||
randn(KF.statePost, Scalar::all(0), Scalar::all(0.1));
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Point2f center(img.cols*0.5f, img.rows*0.5f);
|
||||
float R = img.cols/3.f;
|
||||
double stateAngle = state.at<float>(0);
|
||||
Point statePt = calcPoint(center, R, stateAngle);
|
||||
|
||||
Mat prediction = KF.predict();
|
||||
double predictAngle = prediction.at<float>(0);
|
||||
Point predictPt = calcPoint(center, R, predictAngle);
|
||||
|
||||
// generate measurement
|
||||
randn( measurement, Scalar::all(0), Scalar::all(KF.measurementNoiseCov.at<float>(0)));
|
||||
measurement += KF.measurementMatrix*state;
|
||||
|
||||
double measAngle = measurement.at<float>(0);
|
||||
Point measPt = calcPoint(center, R, measAngle);
|
||||
|
||||
// correct the state estimates based on measurements
|
||||
// updates statePost & errorCovPost
|
||||
KF.correct(measurement);
|
||||
double improvedAngle = KF.statePost.at<float>(0);
|
||||
Point improvedPt = calcPoint(center, R, improvedAngle);
|
||||
|
||||
// plot points
|
||||
img = img * 0.2;
|
||||
drawMarker(img, measPt, Scalar(0, 0, 255), cv::MARKER_SQUARE, 5, 2);
|
||||
drawMarker(img, predictPt, Scalar(0, 255, 255), cv::MARKER_SQUARE, 5, 2);
|
||||
drawMarker(img, improvedPt, Scalar(0, 255, 0), cv::MARKER_SQUARE, 5, 2);
|
||||
drawMarker(img, statePt, Scalar(255, 255, 255), cv::MARKER_STAR, 10, 1);
|
||||
// forecast one step
|
||||
Mat test = Mat(KF.transitionMatrix*KF.statePost);
|
||||
drawMarker(img, calcPoint(center, R, Mat(KF.transitionMatrix*KF.statePost).at<float>(0)),
|
||||
Scalar(255, 255, 0), cv::MARKER_SQUARE, 12, 1);
|
||||
|
||||
line( img, statePt, measPt, Scalar(0,0,255), 1, LINE_AA, 0 );
|
||||
line( img, statePt, predictPt, Scalar(0,255,255), 1, LINE_AA, 0 );
|
||||
line( img, statePt, improvedPt, Scalar(0,255,0), 1, LINE_AA, 0 );
|
||||
|
||||
|
||||
randn( processNoise, Scalar(0), Scalar::all(sqrt(KF.processNoiseCov.at<float>(0, 0))));
|
||||
state = KF.transitionMatrix*state + processNoise;
|
||||
|
||||
imshow( "Kalman", img );
|
||||
code = (char)waitKey(1000);
|
||||
|
||||
if( code > 0 )
|
||||
break;
|
||||
}
|
||||
if( code == 27 || code == 'q' || code == 'Q' )
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
// static void help()
|
||||
// {
|
||||
// cout << "\nThis program demonstrates kmeans clustering.\n"
|
||||
// "It generates an image with random points, then assigns a random number of cluster\n"
|
||||
// "centers and uses kmeans to move those cluster centers to their representative location\n"
|
||||
// "Call\n"
|
||||
// "./kmeans\n" << endl;
|
||||
// }
|
||||
|
||||
int main( int /*argc*/, char** /*argv*/ )
|
||||
{
|
||||
const int MAX_CLUSTERS = 5;
|
||||
Scalar colorTab[] =
|
||||
{
|
||||
Scalar(0, 0, 255),
|
||||
Scalar(0,255,0),
|
||||
Scalar(255,100,100),
|
||||
Scalar(255,0,255),
|
||||
Scalar(0,255,255)
|
||||
};
|
||||
|
||||
Mat img(500, 500, CV_8UC3);
|
||||
RNG rng(12345);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
int k, clusterCount = rng.uniform(2, MAX_CLUSTERS+1);
|
||||
int i, sampleCount = rng.uniform(1, 1001);
|
||||
Mat points(sampleCount, 1, CV_32FC2), labels;
|
||||
|
||||
clusterCount = MIN(clusterCount, sampleCount);
|
||||
std::vector<Point2f> centers;
|
||||
|
||||
/* generate random sample from multigaussian distribution */
|
||||
for( k = 0; k < clusterCount; k++ )
|
||||
{
|
||||
Point center;
|
||||
center.x = rng.uniform(0, img.cols);
|
||||
center.y = rng.uniform(0, img.rows);
|
||||
Mat pointChunk = points.rowRange(k*sampleCount/clusterCount,
|
||||
k == clusterCount - 1 ? sampleCount :
|
||||
(k+1)*sampleCount/clusterCount);
|
||||
rng.fill(pointChunk, RNG::NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
|
||||
}
|
||||
|
||||
randShuffle(points, 1, &rng);
|
||||
|
||||
double compactness = kmeans(points, clusterCount, labels,
|
||||
TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 10, 1.0),
|
||||
3, KMEANS_PP_CENTERS, centers);
|
||||
|
||||
img = Scalar::all(0);
|
||||
|
||||
for( i = 0; i < sampleCount; i++ )
|
||||
{
|
||||
int clusterIdx = labels.at<int>(i);
|
||||
Point ipt = points.at<Point2f>(i);
|
||||
circle( img, ipt, 2, colorTab[clusterIdx], FILLED, LINE_AA );
|
||||
}
|
||||
for (i = 0; i < (int)centers.size(); ++i)
|
||||
{
|
||||
Point2f c = centers[i];
|
||||
circle( img, c, 40, colorTab[i], 1, LINE_AA );
|
||||
}
|
||||
cout << "Compactness: " << compactness << endl;
|
||||
|
||||
imshow("clusters", img);
|
||||
|
||||
char key = (char)waitKey();
|
||||
if( key == 27 || key == 'q' || key == 'Q' ) // 'ESC'
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout <<
|
||||
"\nThis program demonstrates Laplace point/edge detection using OpenCV function Laplacian()\n"
|
||||
"It captures from the camera of your choice: 0, 1, ... default 0\n"
|
||||
"Call:\n"
|
||||
<< argv[0] << " -c=<camera #, default 0> -p=<index of the frame to be decoded/captured next>\n" << endl;
|
||||
}
|
||||
|
||||
enum {GAUSSIAN, BLUR, MEDIAN};
|
||||
|
||||
int sigma = 3;
|
||||
int smoothType = GAUSSIAN;
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{ c | 0 | }{ p | | }");
|
||||
help(argv);
|
||||
|
||||
VideoCapture cap;
|
||||
string camera = parser.get<string>("c");
|
||||
if (camera.size() == 1 && isdigit(camera[0]))
|
||||
cap.open(parser.get<int>("c"));
|
||||
else
|
||||
cap.open(samples::findFileOrKeep(camera));
|
||||
if (!cap.isOpened())
|
||||
{
|
||||
cerr << "Can't open camera/video stream: " << camera << endl;
|
||||
return 1;
|
||||
}
|
||||
cout << "Video " << parser.get<string>("c") <<
|
||||
": width=" << cap.get(CAP_PROP_FRAME_WIDTH) <<
|
||||
", height=" << cap.get(CAP_PROP_FRAME_HEIGHT) <<
|
||||
", nframes=" << cap.get(CAP_PROP_FRAME_COUNT) << endl;
|
||||
int pos = 0;
|
||||
if (parser.has("p"))
|
||||
{
|
||||
pos = parser.get<int>("p");
|
||||
}
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos != 0)
|
||||
{
|
||||
cout << "seeking to frame #" << pos << endl;
|
||||
if (!cap.set(CAP_PROP_POS_FRAMES, pos))
|
||||
{
|
||||
cerr << "ERROR: seekeing is not supported" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
namedWindow("Laplacian", WINDOW_AUTOSIZE);
|
||||
createTrackbar("Sigma", "Laplacian", &sigma, 15, 0);
|
||||
|
||||
Mat smoothed, laplace, result;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
Mat frame;
|
||||
cap >> frame;
|
||||
if( frame.empty() )
|
||||
break;
|
||||
|
||||
int ksize = (sigma*5)|1;
|
||||
if(smoothType == GAUSSIAN)
|
||||
GaussianBlur(frame, smoothed, Size(ksize, ksize), sigma, sigma);
|
||||
else if(smoothType == BLUR)
|
||||
blur(frame, smoothed, Size(ksize, ksize));
|
||||
else
|
||||
medianBlur(frame, smoothed, ksize);
|
||||
|
||||
Laplacian(smoothed, laplace, CV_16S, 5);
|
||||
convertScaleAbs(laplace, result, (sigma+1)*0.25);
|
||||
imshow("Laplacian", result);
|
||||
|
||||
char c = (char)waitKey(30);
|
||||
if( c == ' ' )
|
||||
smoothType = smoothType == GAUSSIAN ? BLUR : smoothType == BLUR ? MEDIAN : GAUSSIAN;
|
||||
if( c == 'q' || c == 'Q' || c == 27 )
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "opencv2/geometry/2d.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{input i|building.jpg|input image}"
|
||||
"{refine r|false|if true use LSD_REFINE_STD method, if false use LSD_REFINE_NONE method}"
|
||||
"{canny c|false|use Canny edge detector}"
|
||||
"{overlay o|false|show result on input image}"
|
||||
"{help h|false|show help message}");
|
||||
|
||||
if (parser.get<bool>("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
parser.printMessage();
|
||||
|
||||
String filename = samples::findFile(parser.get<String>("input"));
|
||||
bool useRefine = parser.get<bool>("refine");
|
||||
bool useCanny = parser.get<bool>("canny");
|
||||
bool overlay = parser.get<bool>("overlay");
|
||||
|
||||
Mat image = imread(filename, IMREAD_GRAYSCALE);
|
||||
|
||||
if( image.empty() )
|
||||
{
|
||||
cout << "Unable to load " << filename;
|
||||
return 1;
|
||||
}
|
||||
|
||||
imshow("Source Image", image);
|
||||
|
||||
if (useCanny)
|
||||
{
|
||||
Canny(image, image, 50, 200, 3); // Apply Canny edge detector
|
||||
}
|
||||
|
||||
// Create and LSD detector with standard or no refinement.
|
||||
Ptr<LineSegmentDetector> ls = useRefine ? createLineSegmentDetector(LSD_REFINE_STD) : createLineSegmentDetector(LSD_REFINE_NONE);
|
||||
|
||||
double start = double(getTickCount());
|
||||
vector<Vec4f> lines_std;
|
||||
|
||||
// Detect the lines
|
||||
ls->detect(image, lines_std);
|
||||
|
||||
double duration_ms = (double(getTickCount()) - start) * 1000 / getTickFrequency();
|
||||
std::cout << "It took " << duration_ms << " ms." << std::endl;
|
||||
|
||||
// Show found lines
|
||||
if (!overlay || useCanny)
|
||||
{
|
||||
image = Scalar(0, 0, 0);
|
||||
}
|
||||
|
||||
ls->drawSegments(image, lines_std);
|
||||
|
||||
String window_name = useRefine ? "Result - standard refinement" : "Result - no refinement";
|
||||
window_name += useCanny ? " - Canny edge detector used" : "";
|
||||
|
||||
imshow(window_name, image);
|
||||
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, const char** argv )
|
||||
{
|
||||
CommandLineParser parser(argc, argv,
|
||||
"{ i | lena_tmpl.jpg |image name }"
|
||||
"{ t | tmpl.png |template name }"
|
||||
"{ m | mask.png |mask name }"
|
||||
"{ cm| 3 |comparison method }");
|
||||
|
||||
cout << "This program demonstrates the use of template matching with mask." << endl
|
||||
<< endl
|
||||
<< "Available methods: https://docs.opencv.org/5.x/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d" << endl
|
||||
<< " TM_SQDIFF = " << (int)TM_SQDIFF << endl
|
||||
<< " TM_SQDIFF_NORMED = " << (int)TM_SQDIFF_NORMED << endl
|
||||
<< " TM_CCORR = " << (int)TM_CCORR << endl
|
||||
<< " TM_CCORR_NORMED = " << (int)TM_CCORR_NORMED << endl
|
||||
<< " TM_CCOEFF = " << (int)TM_CCOEFF << endl
|
||||
<< " TM_CCOEFF_NORMED = " << (int)TM_CCOEFF_NORMED << endl
|
||||
<< endl;
|
||||
|
||||
parser.printMessage();
|
||||
|
||||
string filename = samples::findFile(parser.get<string>("i"));
|
||||
string tmplname = samples::findFile(parser.get<string>("t"));
|
||||
string maskname = samples::findFile(parser.get<string>("m"));
|
||||
Mat img = imread(filename);
|
||||
Mat tmpl = imread(tmplname);
|
||||
Mat mask = imread(maskname);
|
||||
Mat res;
|
||||
|
||||
if(img.empty())
|
||||
{
|
||||
cout << "can not open " << filename << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(tmpl.empty())
|
||||
{
|
||||
cout << "can not open " << tmplname << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(mask.empty())
|
||||
{
|
||||
cout << "can not open " << maskname << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int method = parser.get<int>("cm"); // default 3 (cv::TM_CCORR_NORMED)
|
||||
matchTemplate(img, tmpl, res, method, mask);
|
||||
|
||||
double minVal, maxVal;
|
||||
Point minLoc, maxLoc;
|
||||
Rect rect;
|
||||
minMaxLoc(res, &minVal, &maxVal, &minLoc, &maxLoc);
|
||||
|
||||
if(method == TM_SQDIFF || method == TM_SQDIFF_NORMED)
|
||||
rect = Rect(minLoc, tmpl.size());
|
||||
else
|
||||
rect = Rect(maxLoc, tmpl.size());
|
||||
|
||||
rectangle(img, rect, Scalar(0, 255, 0), 2);
|
||||
|
||||
imshow("detected template", img);
|
||||
waitKey();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main(int, char* [])
|
||||
{
|
||||
VideoCapture video(0);
|
||||
Mat frame, curr, prev, curr64f, prev64f, hann;
|
||||
char key;
|
||||
|
||||
do
|
||||
{
|
||||
video >> frame;
|
||||
cvtColor(frame, curr, COLOR_RGB2GRAY);
|
||||
|
||||
if(prev.empty())
|
||||
{
|
||||
prev = curr.clone();
|
||||
createHanningWindow(hann, curr.size(), CV_64F);
|
||||
}
|
||||
|
||||
prev.convertTo(prev64f, CV_64F);
|
||||
curr.convertTo(curr64f, CV_64F);
|
||||
|
||||
Point2d shift = phaseCorrelate(prev64f, curr64f, hann);
|
||||
double radius = std::sqrt(shift.x*shift.x + shift.y*shift.y);
|
||||
|
||||
if(radius > 5)
|
||||
{
|
||||
// draw a circle and line indicating the shift direction...
|
||||
Point center(curr.cols >> 1, curr.rows >> 1);
|
||||
circle(frame, center, (int)radius, Scalar(0, 255, 0), 3, LINE_AA);
|
||||
line(frame, center, Point(center.x + (int)shift.x, center.y + (int)shift.y), Scalar(0, 255, 0), 3, LINE_AA);
|
||||
}
|
||||
|
||||
imshow("phase shift", frame);
|
||||
key = (char)waitKey(2);
|
||||
|
||||
prev = curr.clone();
|
||||
} while(key != 27); // Esc to exit...
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
VideoCapture capture;
|
||||
Mat log_polar_img, lin_polar_img, recovered_log_polar, recovered_lin_polar_img;
|
||||
|
||||
CommandLineParser parser(argc, argv, "{@input|0| camera device number or video file path}");
|
||||
parser.about("\nThis program illustrates usage of warpPolar for both linear and log-polar transforms.\n");
|
||||
parser.printMessage();
|
||||
std::string arg = parser.get<std::string>("@input");
|
||||
|
||||
if( arg.size() == 1 && isdigit(arg[0]) )
|
||||
capture.open( arg[0] - '0' );
|
||||
else
|
||||
capture.open(samples::findFileOrKeep(arg));
|
||||
|
||||
if( !capture.isOpened() )
|
||||
{
|
||||
fprintf(stderr,"Could not initialize capturing...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
namedWindow( "Linear-Polar", WINDOW_AUTOSIZE );
|
||||
namedWindow( "Log-Polar", WINDOW_AUTOSIZE);
|
||||
namedWindow( "Recovered Linear-Polar", WINDOW_AUTOSIZE);
|
||||
namedWindow( "Recovered Log-Polar", WINDOW_AUTOSIZE);
|
||||
|
||||
moveWindow( "Linear-Polar", 20,20 );
|
||||
moveWindow( "Log-Polar", 700,20 );
|
||||
moveWindow( "Recovered Linear-Polar", 20, 350 );
|
||||
moveWindow( "Recovered Log-Polar", 700, 350 );
|
||||
int flags = INTER_LINEAR + WARP_FILL_OUTLIERS;
|
||||
Mat src;
|
||||
for(;;)
|
||||
{
|
||||
capture >> src;
|
||||
|
||||
if(src.empty() )
|
||||
break;
|
||||
|
||||
Point2f center( (float)src.cols / 2, (float)src.rows / 2 );
|
||||
double maxRadius = 0.7*min(center.y, center.x);
|
||||
|
||||
//! [InverseMap]
|
||||
// direct transform using warpPolar (replacing deprecated functions)
|
||||
warpPolar(src, lin_polar_img, Size(),center, maxRadius, flags); // Linear-polar transform
|
||||
warpPolar(src, log_polar_img, Size(),center, maxRadius, flags + WARP_POLAR_LOG); // Log-polar transform
|
||||
// inverse transform
|
||||
warpPolar(lin_polar_img, recovered_lin_polar_img, src.size(), center, maxRadius, flags + WARP_INVERSE_MAP);
|
||||
warpPolar(log_polar_img, recovered_log_polar, src.size(), center, maxRadius, flags + WARP_POLAR_LOG + WARP_INVERSE_MAP);
|
||||
//! [InverseMap]
|
||||
|
||||
// Below is the reverse transformation for (rho, phi)->(x, y) :
|
||||
Mat dst;
|
||||
if (flags & WARP_POLAR_LOG)
|
||||
dst = log_polar_img;
|
||||
else
|
||||
dst = lin_polar_img;
|
||||
// get a point from the polar image
|
||||
int rho = cvRound(dst.cols * 0.75);
|
||||
int phi = cvRound(dst.rows / 2.0);
|
||||
|
||||
//! [InverseCoordinate]
|
||||
double angleRad, magnitude;
|
||||
double Kangle = dst.rows / CV_2PI;
|
||||
angleRad = phi / Kangle;
|
||||
if (flags & WARP_POLAR_LOG)
|
||||
{
|
||||
double Klog = dst.cols / std::log(maxRadius);
|
||||
magnitude = std::exp(rho / Klog);
|
||||
}
|
||||
else
|
||||
{
|
||||
double Klin = dst.cols / maxRadius;
|
||||
magnitude = rho / Klin;
|
||||
}
|
||||
int x = cvRound(center.x + magnitude * cos(angleRad));
|
||||
int y = cvRound(center.y + magnitude * sin(angleRad));
|
||||
//! [InverseCoordinate]
|
||||
drawMarker(src, Point(x, y), Scalar(0, 255, 0));
|
||||
drawMarker(dst, Point(rho, phi), Scalar(0, 255, 0));
|
||||
|
||||
imshow("Src frame", src);
|
||||
imshow("Log-Polar", log_polar_img);
|
||||
imshow("Linear-Polar", lin_polar_img);
|
||||
imshow("Recovered Linear-Polar", recovered_lin_polar_img );
|
||||
imshow("Recovered Log-Polar", recovered_log_polar );
|
||||
|
||||
if( waitKey(10) >= 0 )
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/video/background_segm.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
printf("\n"
|
||||
"This program demonstrated a simple method of connected components clean up of background subtraction\n"
|
||||
"When the program starts, it begins learning the background.\n"
|
||||
"You can toggle background learning on and off by hitting the space bar.\n"
|
||||
"Call\n"
|
||||
"%s [video file, else it reads camera 0]\n\n", argv[0]);
|
||||
}
|
||||
|
||||
static void refineSegments(const Mat& img, Mat& mask, Mat& dst)
|
||||
{
|
||||
int niters = 3;
|
||||
|
||||
vector<vector<Point> > contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
|
||||
Mat temp;
|
||||
|
||||
dilate(mask, temp, Mat(), Point(-1,-1), niters);
|
||||
erode(temp, temp, Mat(), Point(-1,-1), niters*2);
|
||||
dilate(temp, temp, Mat(), Point(-1,-1), niters);
|
||||
|
||||
findContours( temp, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE );
|
||||
|
||||
dst = Mat::zeros(img.size(), CV_8UC3);
|
||||
|
||||
if( contours.size() == 0 )
|
||||
return;
|
||||
|
||||
// iterate through all the top-level contours,
|
||||
// draw each connected component with its own random color
|
||||
int idx = 0, largestComp = 0;
|
||||
double maxArea = 0;
|
||||
|
||||
for( ; idx >= 0; idx = hierarchy[idx][0] )
|
||||
{
|
||||
const vector<Point>& c = contours[idx];
|
||||
double area = fabs(contourArea(Mat(c)));
|
||||
if( area > maxArea )
|
||||
{
|
||||
maxArea = area;
|
||||
largestComp = idx;
|
||||
}
|
||||
}
|
||||
Scalar color( 0, 0, 255 );
|
||||
drawContours( dst, contours, largestComp, color, FILLED, LINE_8, hierarchy );
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
VideoCapture cap;
|
||||
bool update_bg_model = true;
|
||||
|
||||
CommandLineParser parser(argc, argv, "{help h||}{@input||}");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
string input = parser.get<std::string>("@input");
|
||||
if (input.empty())
|
||||
cap.open(0);
|
||||
else
|
||||
cap.open(samples::findFileOrKeep(input));
|
||||
|
||||
if( !cap.isOpened() )
|
||||
{
|
||||
printf("\nCan not open camera or video file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat tmp_frame, bgmask, out_frame;
|
||||
|
||||
cap >> tmp_frame;
|
||||
if(tmp_frame.empty())
|
||||
{
|
||||
printf("can not read data from the video source\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
namedWindow("video", 1);
|
||||
namedWindow("segmented", 1);
|
||||
|
||||
Ptr<BackgroundSubtractorMOG2> bgsubtractor=createBackgroundSubtractorMOG2();
|
||||
bgsubtractor->setVarThreshold(10);
|
||||
|
||||
for(;;)
|
||||
{
|
||||
cap >> tmp_frame;
|
||||
if( tmp_frame.empty() )
|
||||
break;
|
||||
bgsubtractor->apply(tmp_frame, bgmask, update_bg_model ? -1 : 0);
|
||||
refineSegments(tmp_frame, bgmask, out_frame);
|
||||
imshow("video", tmp_frame);
|
||||
imshow("segmented", out_frame);
|
||||
char keycode = (char)waitKey(30);
|
||||
if( keycode == 27 )
|
||||
break;
|
||||
if( keycode == ' ' )
|
||||
{
|
||||
update_bg_model = !update_bg_model;
|
||||
printf("Learn background is in state = %d\n",update_bg_model);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
|
||||
// The "Square Detector" program.
|
||||
// It loads several images sequentially and tries to find squares in
|
||||
// each image
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(const char* programName)
|
||||
{
|
||||
cout <<
|
||||
"\nA program using pyramid scaling, Canny, contours and contour simplification\n"
|
||||
"to find squares in a list of images (pic1-6.png)\n"
|
||||
"Returns sequence of squares detected on the image.\n"
|
||||
"Call:\n"
|
||||
"./" << programName << " [file_name (optional)]\n"
|
||||
"Using OpenCV version " << CV_VERSION << "\n" << endl;
|
||||
}
|
||||
|
||||
|
||||
int thresh = 50, N = 11;
|
||||
const char* wndname = "Square Detection Demo";
|
||||
|
||||
// helper function:
|
||||
// finds a cosine of angle between vectors
|
||||
// from pt0->pt1 and from pt0->pt2
|
||||
static double angle( Point pt1, Point pt2, Point pt0 )
|
||||
{
|
||||
double dx1 = pt1.x - pt0.x;
|
||||
double dy1 = pt1.y - pt0.y;
|
||||
double dx2 = pt2.x - pt0.x;
|
||||
double dy2 = pt2.y - pt0.y;
|
||||
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
|
||||
}
|
||||
|
||||
// returns sequence of squares detected on the image.
|
||||
static void findSquares( const Mat& image, vector<vector<Point> >& squares )
|
||||
{
|
||||
squares.clear();
|
||||
|
||||
Mat pyr, timg, gray0(image.size(), CV_8U), gray;
|
||||
|
||||
// down-scale and upscale the image to filter out the noise
|
||||
pyrDown(image, pyr, Size(image.cols/2, image.rows/2));
|
||||
pyrUp(pyr, timg, image.size());
|
||||
vector<vector<Point> > contours;
|
||||
|
||||
// find squares in every color plane of the image
|
||||
for( int c = 0; c < 3; c++ )
|
||||
{
|
||||
int ch[] = {c, 0};
|
||||
mixChannels(&timg, 1, &gray0, 1, ch, 1);
|
||||
|
||||
// try several threshold levels
|
||||
for( int l = 0; l < N; l++ )
|
||||
{
|
||||
// hack: use Canny instead of zero threshold level.
|
||||
// Canny helps to catch squares with gradient shading
|
||||
if( l == 0 )
|
||||
{
|
||||
// apply Canny. Take the upper threshold from slider
|
||||
// and set the lower to 0 (which forces edges merging)
|
||||
Canny(gray0, gray, 0, thresh, 5);
|
||||
// dilate canny output to remove potential
|
||||
// holes between edge segments
|
||||
dilate(gray, gray, Mat(), Point(-1,-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply threshold if l!=0:
|
||||
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
|
||||
gray = gray0 >= (l+1)*255/N;
|
||||
}
|
||||
|
||||
// find contours and store them all as a list
|
||||
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
|
||||
|
||||
vector<Point> approx;
|
||||
|
||||
// test each contour
|
||||
for( size_t i = 0; i < contours.size(); i++ )
|
||||
{
|
||||
// approximate contour with accuracy proportional
|
||||
// to the contour perimeter
|
||||
approxPolyDP(contours[i], approx, arcLength(contours[i], true)*0.02, true);
|
||||
|
||||
// square contours should have 4 vertices after approximation
|
||||
// relatively large area (to filter out noisy contours)
|
||||
// and be convex.
|
||||
// Note: absolute value of an area is used because
|
||||
// area may be positive or negative - in accordance with the
|
||||
// contour orientation
|
||||
if( approx.size() == 4 &&
|
||||
fabs(contourArea(approx)) > 1000 &&
|
||||
isContourConvex(approx) )
|
||||
{
|
||||
double maxCosine = 0;
|
||||
|
||||
for( int j = 2; j < 5; j++ )
|
||||
{
|
||||
// find the maximum cosine of the angle between joint edges
|
||||
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
|
||||
maxCosine = MAX(maxCosine, cosine);
|
||||
}
|
||||
|
||||
// if cosines of all angles are small
|
||||
// (all angles are ~90 degree) then write quandrange
|
||||
// vertices to resultant sequence
|
||||
if( maxCosine < 0.3 )
|
||||
squares.push_back(approx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
const char* names[] = { "pic1.png", "pic2.png", "pic3.png",
|
||||
"pic4.png", "pic5.png", "pic6.png", 0 };
|
||||
help(argv[0]);
|
||||
|
||||
if( argc > 1)
|
||||
{
|
||||
names[0] = argv[1];
|
||||
names[1] = 0;
|
||||
}
|
||||
|
||||
for( int i = 0; names[i] != 0; i++ )
|
||||
{
|
||||
string filename = samples::findFile(names[i]);
|
||||
Mat image = imread(filename, IMREAD_COLOR);
|
||||
if( image.empty() )
|
||||
{
|
||||
cout << "Couldn't load " << filename << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
vector<vector<Point> > squares;
|
||||
findSquares(image, squares);
|
||||
|
||||
polylines(image, squares, true, Scalar(0, 255, 0), 3, LINE_AA);
|
||||
imshow(wndname, image);
|
||||
|
||||
int c = waitKey();
|
||||
if( c == 27 )
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/stitching.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
bool divide_images = false;
|
||||
Stitcher::Mode mode = Stitcher::PANORAMA;
|
||||
vector<Mat> imgs;
|
||||
string result_name = "result.jpg";
|
||||
|
||||
void printUsage(char** argv);
|
||||
int parseCmdArgs(int argc, char** argv);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int retval = parseCmdArgs(argc, argv);
|
||||
if (retval) return EXIT_FAILURE;
|
||||
|
||||
//![stitching]
|
||||
Mat pano;
|
||||
Ptr<Stitcher> stitcher = Stitcher::create(mode);
|
||||
Stitcher::Status status = stitcher->stitch(imgs, pano);
|
||||
|
||||
if (status != Stitcher::OK)
|
||||
{
|
||||
cout << "Can't stitch images, error code = " << int(status) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//![stitching]
|
||||
|
||||
imwrite(result_name, pano);
|
||||
cout << "stitching completed successfully\n" << result_name << " saved!";
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void printUsage(char** argv)
|
||||
{
|
||||
cout <<
|
||||
"Images stitcher.\n\n" << "Usage :\n" << argv[0] <<" [Flags] img1 img2 [...imgN]\n\n"
|
||||
"Flags:\n"
|
||||
" --d3\n"
|
||||
" internally creates three chunks of each image to increase stitching success\n"
|
||||
" --mode (panorama|scans)\n"
|
||||
" Determines configuration of stitcher. The default is 'panorama',\n"
|
||||
" mode suitable for creating photo panoramas. Option 'scans' is suitable\n"
|
||||
" for stitching materials under affine transformation, such as scans.\n"
|
||||
" --output <result_img>\n"
|
||||
" The default is 'result.jpg'.\n\n"
|
||||
"Example usage :\n" << argv[0] << " --d3 --mode scans img1.jpg img2.jpg\n";
|
||||
}
|
||||
|
||||
|
||||
int parseCmdArgs(int argc, char** argv)
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printUsage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
if (string(argv[i]) == "--help" || string(argv[i]) == "/?")
|
||||
{
|
||||
printUsage(argv);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if (string(argv[i]) == "--d3")
|
||||
{
|
||||
divide_images = true;
|
||||
}
|
||||
else if (string(argv[i]) == "--output")
|
||||
{
|
||||
result_name = argv[i + 1];
|
||||
i++;
|
||||
}
|
||||
else if (string(argv[i]) == "--mode")
|
||||
{
|
||||
if (string(argv[i + 1]) == "panorama")
|
||||
mode = Stitcher::PANORAMA;
|
||||
else if (string(argv[i + 1]) == "scans")
|
||||
mode = Stitcher::SCANS;
|
||||
else
|
||||
{
|
||||
cout << "Bad --mode flag value\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat img = imread(samples::findFile(argv[i]));
|
||||
if (img.empty())
|
||||
{
|
||||
cout << "Can't read image '" << argv[i] << "'\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (divide_images)
|
||||
{
|
||||
Rect rect(0, 0, img.cols / 2, img.rows);
|
||||
imgs.push_back(img(rect).clone());
|
||||
rect.x = img.cols / 3;
|
||||
imgs.push_back(img(rect).clone());
|
||||
rect.x = img.cols / 2;
|
||||
imgs.push_back(img(rect).clone());
|
||||
}
|
||||
else
|
||||
imgs.push_back(img);
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
@file videocapture_basic.cpp
|
||||
@brief A very basic sample for using VideoCapture and VideoWriter
|
||||
@author PkLab.net
|
||||
@date Aug 24, 2016
|
||||
*/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
Mat frame;
|
||||
//--- INITIALIZE VIDEOCAPTURE
|
||||
VideoCapture cap;
|
||||
// open the default camera using default API
|
||||
// cap.open(0);
|
||||
// OR advance usage: select any API backend
|
||||
int deviceID = 0; // 0 = open default camera
|
||||
int apiID = cv::CAP_ANY; // 0 = autodetect default API
|
||||
// open selected camera using selected API
|
||||
cap.open(deviceID, apiID);
|
||||
// check if we succeeded
|
||||
if (!cap.isOpened()) {
|
||||
cerr << "ERROR! Unable to open camera\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
//--- GRAB AND WRITE LOOP
|
||||
cout << "Start grabbing" << endl
|
||||
<< "Press any key to terminate" << endl;
|
||||
for (;;)
|
||||
{
|
||||
// wait for a new frame from camera and store it into 'frame'
|
||||
cap.read(frame);
|
||||
// check if we succeeded
|
||||
if (frame.empty()) {
|
||||
cerr << "ERROR! blank frame grabbed\n";
|
||||
break;
|
||||
}
|
||||
// show live and wait for a key with timeout long enough to show images
|
||||
imshow("Live", frame);
|
||||
if (waitKey(5) >= 0)
|
||||
break;
|
||||
}
|
||||
// the camera will be deinitialized automatically in VideoCapture destructor
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
@file warpPerspective_demo.cpp
|
||||
@brief a demo program shows how perspective transformation applied on an image
|
||||
@based on a sample code http://study.marearts.com/2015/03/image-warping-using-opencv.html
|
||||
@modified by Suleyman TURKMEN
|
||||
*/
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
// print a welcome message, and the OpenCV version
|
||||
cout << "\nThis is a demo program shows how perspective transformation applied on an image, \n"
|
||||
"Using OpenCV version " << CV_VERSION << endl;
|
||||
|
||||
cout << "\nUsage:\n" << argv[0] << " [image_name -- Default right.jpg]\n" << endl;
|
||||
|
||||
cout << "\nHot keys: \n"
|
||||
"\tESC, q - quit the program\n"
|
||||
"\tr - change order of points to rotate transformation\n"
|
||||
"\tc - delete selected points\n"
|
||||
"\ti - change order of points to inverse transformation \n"
|
||||
"\nUse your mouse to select a point and move it to see transformation changes" << endl;
|
||||
}
|
||||
|
||||
static void onMouse(int event, int x, int y, int, void*);
|
||||
Mat warping(Mat image, Size warped_image_size, vector< Point2f> srcPoints, vector< Point2f> dstPoints);
|
||||
|
||||
String windowTitle = "Perspective Transformation Demo";
|
||||
String labels[4] = { "TL","TR","BR","BL" };
|
||||
vector< Point2f> roi_corners;
|
||||
vector< Point2f> midpoints(4);
|
||||
vector< Point2f> dst_corners(4);
|
||||
int roiIndex = 0;
|
||||
bool dragging;
|
||||
int selected_corner_index = 0;
|
||||
bool validation_needed = true;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
help(argv);
|
||||
CommandLineParser parser(argc, argv, "{@input| right.jpg |}");
|
||||
|
||||
string filename = samples::findFile(parser.get<string>("@input"));
|
||||
Mat original_image = imread( filename );
|
||||
Mat image;
|
||||
|
||||
float original_image_cols = (float)original_image.cols;
|
||||
float original_image_rows = (float)original_image.rows;
|
||||
roi_corners.push_back(Point2f( (float)(original_image_cols / 1.70), (float)(original_image_rows / 4.20) ));
|
||||
roi_corners.push_back(Point2f( (float)(original_image.cols / 1.15), (float)(original_image.rows / 3.32) ));
|
||||
roi_corners.push_back(Point2f( (float)(original_image.cols / 1.33), (float)(original_image.rows / 1.10) ));
|
||||
roi_corners.push_back(Point2f( (float)(original_image.cols / 1.93), (float)(original_image.rows / 1.36) ));
|
||||
|
||||
namedWindow(windowTitle, WINDOW_NORMAL);
|
||||
namedWindow("Warped Image", WINDOW_AUTOSIZE);
|
||||
moveWindow("Warped Image", 20, 20);
|
||||
moveWindow(windowTitle, 330, 20);
|
||||
|
||||
setMouseCallback(windowTitle, onMouse, 0);
|
||||
|
||||
bool endProgram = false;
|
||||
while (!endProgram)
|
||||
{
|
||||
if ( validation_needed & (roi_corners.size() < 4) )
|
||||
{
|
||||
validation_needed = false;
|
||||
image = original_image.clone();
|
||||
|
||||
for (size_t i = 0; i < roi_corners.size(); ++i)
|
||||
{
|
||||
circle( image, roi_corners[i], 5, Scalar(0, 255, 0), 3 );
|
||||
|
||||
if( i > 0 )
|
||||
{
|
||||
line(image, roi_corners[i-1], roi_corners[(i)], Scalar(0, 0, 255), 2);
|
||||
circle(image, roi_corners[i], 5, Scalar(0, 255, 0), 3);
|
||||
putText(image, labels[i].c_str(), roi_corners[i], FONT_HERSHEY_SIMPLEX, 0.8, Scalar(255, 0, 0), 2);
|
||||
}
|
||||
}
|
||||
imshow( windowTitle, image );
|
||||
}
|
||||
|
||||
if ( validation_needed & ( roi_corners.size() == 4 ))
|
||||
{
|
||||
image = original_image.clone();
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
line(image, roi_corners[i], roi_corners[(i + 1) % 4], Scalar(0, 0, 255), 2);
|
||||
circle(image, roi_corners[i], 5, Scalar(0, 255, 0), 3);
|
||||
putText(image, labels[i].c_str(), roi_corners[i], FONT_HERSHEY_SIMPLEX, 0.8, Scalar(255, 0, 0), 2);
|
||||
}
|
||||
|
||||
imshow( windowTitle, image );
|
||||
|
||||
midpoints[0] = (roi_corners[0] + roi_corners[1]) / 2;
|
||||
midpoints[1] = (roi_corners[1] + roi_corners[2]) / 2;
|
||||
midpoints[2] = (roi_corners[2] + roi_corners[3]) / 2;
|
||||
midpoints[3] = (roi_corners[3] + roi_corners[0]) / 2;
|
||||
|
||||
dst_corners[0].x = 0;
|
||||
dst_corners[0].y = 0;
|
||||
dst_corners[1].x = (float)norm(midpoints[1] - midpoints[3]);
|
||||
dst_corners[1].y = 0;
|
||||
dst_corners[2].x = dst_corners[1].x;
|
||||
dst_corners[2].y = (float)norm(midpoints[0] - midpoints[2]);
|
||||
dst_corners[3].x = 0;
|
||||
dst_corners[3].y = dst_corners[2].y;
|
||||
|
||||
Size warped_image_size = Size(cvRound(dst_corners[2].x), cvRound(dst_corners[2].y));
|
||||
|
||||
Mat M = getPerspectiveTransform(roi_corners, dst_corners);
|
||||
|
||||
Mat warped_image;
|
||||
warpPerspective(original_image, warped_image, M, warped_image_size); // do perspective transformation
|
||||
|
||||
imshow("Warped Image", warped_image);
|
||||
}
|
||||
|
||||
char c = (char)waitKey( 10 );
|
||||
|
||||
if ((c == 'q') | (c == 'Q') | (c == 27))
|
||||
{
|
||||
endProgram = true;
|
||||
}
|
||||
|
||||
if ((c == 'c') | (c == 'C'))
|
||||
{
|
||||
roi_corners.clear();
|
||||
}
|
||||
|
||||
if ((c == 'r') | (c == 'R'))
|
||||
{
|
||||
roi_corners.push_back(roi_corners[0]);
|
||||
roi_corners.erase(roi_corners.begin());
|
||||
}
|
||||
|
||||
if ((c == 'i') | (c == 'I'))
|
||||
{
|
||||
swap(roi_corners[0], roi_corners[1]);
|
||||
swap(roi_corners[2], roi_corners[3]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void onMouse(int event, int x, int y, int, void*)
|
||||
{
|
||||
// Action when left button is pressed
|
||||
if (roi_corners.size() == 4)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if ((event == EVENT_LBUTTONDOWN) && ((abs(roi_corners[i].x - x) < 10)) && (abs(roi_corners[i].y - y) < 10))
|
||||
{
|
||||
selected_corner_index = i;
|
||||
dragging = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( event == EVENT_LBUTTONDOWN )
|
||||
{
|
||||
roi_corners.push_back( Point2f( (float) x, (float) y ) );
|
||||
validation_needed = true;
|
||||
}
|
||||
|
||||
// Action when left button is released
|
||||
if (event == EVENT_LBUTTONUP)
|
||||
{
|
||||
dragging = false;
|
||||
}
|
||||
|
||||
// Action when left button is pressed and mouse has moved over the window
|
||||
if ((event == EVENT_MOUSEMOVE) && dragging)
|
||||
{
|
||||
roi_corners[selected_corner_index].x = (float) x;
|
||||
roi_corners[selected_corner_index].y = (float) y;
|
||||
validation_needed = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <opencv2/core/utility.hpp>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static void help(char** argv)
|
||||
{
|
||||
cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n"
|
||||
"Usage:\n" << argv[0] <<" [image_name -- default is fruits.jpg]\n" << endl;
|
||||
|
||||
|
||||
cout << "Hot keys: \n"
|
||||
"\tESC - quit the program\n"
|
||||
"\tr - restore the original image\n"
|
||||
"\tw or SPACE - run watershed segmentation algorithm\n"
|
||||
"\t\t(before running it, *roughly* mark the areas to segment on the image)\n"
|
||||
"\t (before that, roughly outline several markers on the image)\n";
|
||||
}
|
||||
Mat markerMask, img;
|
||||
Point prevPt(-1, -1);
|
||||
|
||||
static void onMouse( int event, int x, int y, int flags, void* )
|
||||
{
|
||||
if( x < 0 || x >= img.cols || y < 0 || y >= img.rows )
|
||||
return;
|
||||
if( event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON) )
|
||||
prevPt = Point(-1,-1);
|
||||
else if( event == EVENT_LBUTTONDOWN )
|
||||
prevPt = Point(x,y);
|
||||
else if( event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON) )
|
||||
{
|
||||
Point pt(x, y);
|
||||
if( prevPt.x < 0 )
|
||||
prevPt = pt;
|
||||
line( markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0 );
|
||||
line( img, prevPt, pt, Scalar::all(255), 5, 8, 0 );
|
||||
prevPt = pt;
|
||||
imshow("image", img);
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv, "{help h | | }{ @input | fruits.jpg | }");
|
||||
if (parser.has("help"))
|
||||
{
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
string filename = samples::findFile(parser.get<string>("@input"));
|
||||
Mat img0 = imread(filename, IMREAD_COLOR), imgGray;
|
||||
|
||||
if( img0.empty() )
|
||||
{
|
||||
cout << "Couldn't open image ";
|
||||
help(argv);
|
||||
return 0;
|
||||
}
|
||||
help(argv);
|
||||
namedWindow( "image", 1 );
|
||||
|
||||
img0.copyTo(img);
|
||||
cvtColor(img, markerMask, COLOR_BGR2GRAY);
|
||||
cvtColor(markerMask, imgGray, COLOR_GRAY2BGR);
|
||||
markerMask = Scalar::all(0);
|
||||
imshow( "image", img );
|
||||
setMouseCallback( "image", onMouse, 0 );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
char c = (char)waitKey(0);
|
||||
|
||||
if( c == 27 )
|
||||
break;
|
||||
|
||||
if( c == 'r' )
|
||||
{
|
||||
markerMask = Scalar::all(0);
|
||||
img0.copyTo(img);
|
||||
imshow( "image", img );
|
||||
}
|
||||
|
||||
if( c == 'w' || c == ' ' )
|
||||
{
|
||||
int i, j, compCount = 0;
|
||||
vector<vector<Point> > contours;
|
||||
vector<Vec4i> hierarchy;
|
||||
|
||||
findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
|
||||
|
||||
if( contours.empty() )
|
||||
continue;
|
||||
Mat markers(markerMask.size(), CV_32S);
|
||||
markers = Scalar::all(0);
|
||||
int idx = 0;
|
||||
for( ; idx >= 0; idx = hierarchy[idx][0], compCount++ )
|
||||
drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
|
||||
|
||||
if( compCount == 0 )
|
||||
continue;
|
||||
|
||||
vector<Vec3b> colorTab;
|
||||
for( i = 0; i < compCount; i++ )
|
||||
{
|
||||
int b = theRNG().uniform(0, 255);
|
||||
int g = theRNG().uniform(0, 255);
|
||||
int r = theRNG().uniform(0, 255);
|
||||
|
||||
colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
|
||||
}
|
||||
|
||||
double t = (double)getTickCount();
|
||||
watershed( img0, markers );
|
||||
t = (double)getTickCount() - t;
|
||||
printf( "execution time = %gms\n", t*1000./getTickFrequency() );
|
||||
|
||||
Mat wshed(markers.size(), CV_8UC3);
|
||||
|
||||
// paint the watershed image
|
||||
for( i = 0; i < markers.rows; i++ )
|
||||
for( j = 0; j < markers.cols; j++ )
|
||||
{
|
||||
int index = markers.at<int>(i,j);
|
||||
if( index == -1 )
|
||||
wshed.at<Vec3b>(i,j) = Vec3b(255,255,255);
|
||||
else if( index <= 0 || index > compCount )
|
||||
wshed.at<Vec3b>(i,j) = Vec3b(0,0,0);
|
||||
else
|
||||
wshed.at<Vec3b>(i,j) = colorTab[index - 1];
|
||||
}
|
||||
|
||||
wshed = wshed*0.5 + imgGray*0.5;
|
||||
imshow( "watershed transform", wshed );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
/* This is sample from the OpenCV book. The copyright notice is below */
|
||||
|
||||
/* *************** License:**************************
|
||||
Oct. 3, 2008
|
||||
Right to use this code in any way you want without warranty, support or any guarantee of it working.
|
||||
|
||||
BOOK: It would be nice if you cited it:
|
||||
Learning OpenCV: Computer Vision with the OpenCV Library
|
||||
by Gary Bradski and Adrian Kaehler
|
||||
Published by O'Reilly Media, October 3, 2008
|
||||
|
||||
AVAILABLE AT:
|
||||
http://www.amazon.com/Learning-OpenCV-Computer-Vision-Library/dp/0596516134
|
||||
Or: http://oreilly.com/catalog/9780596516130/
|
||||
ISBN-10: 0596516134 or: ISBN-13: 978-0596516130
|
||||
|
||||
OPENCV WEBSITES:
|
||||
Homepage: http://opencv.org
|
||||
Online docs: http://docs.opencv.org
|
||||
GitHub: https://github.com/opencv/opencv/
|
||||
************************************************** */
|
||||
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/stereo.hpp"
|
||||
#include "opencv2/calib.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/objdetect.hpp"
|
||||
#include "opencv2/objdetect/charuco_detector.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
static int print_help(char** argv)
|
||||
{
|
||||
cout <<
|
||||
" Given a list of chessboard or ChArUco images, the number of corners (nx, ny)\n"
|
||||
" on the chessboards and the number of squares (nx, ny) on ChArUco,\n"
|
||||
" and a flag: useCalibrated for \n"
|
||||
" calibrated (0) or\n"
|
||||
" uncalibrated \n"
|
||||
" (1: use stereoCalibrate(), 2: compute fundamental\n"
|
||||
" matrix separately) stereo. \n"
|
||||
" Calibrate the cameras and display the\n"
|
||||
" rectified results along with the computed disparity images. \n" << endl;
|
||||
cout << "Usage:\n " << argv[0] << " -w=<board_width default=9> -h=<board_height default=6>"
|
||||
<<" -t=<pattern type: chessboard or charucoboard default=chessboard> -s=<square_size default=1.0> -ms=<marker size default=0.5>"
|
||||
<<" -ad=<predefined aruco dictionary name default=DICT_4X4_50> -adf=<aruco dictionary file default=None>"
|
||||
<<" <image list XML/YML file default=stereo_calib.xml>\n" << endl;
|
||||
cout << "Available Aruco dictionaries: DICT_4X4_50, DICT_4X4_100, DICT_4X4_250, "
|
||||
<< "DICT_4X4_1000, DICT_5X5_50, DICT_5X5_100, DICT_5X5_250, DICT_5X5_1000, "
|
||||
<< "DICT_6X6_50, DICT_6X6_100, DICT_6X6_250, DICT_6X6_1000, DICT_7X7_50, "
|
||||
<< "DICT_7X7_100, DICT_7X7_250, DICT_7X7_1000, DICT_ARUCO_ORIGINAL, "
|
||||
<< "DICT_APRILTAG_16h5, DICT_APRILTAG_25h9, DICT_APRILTAG_36h10, DICT_APRILTAG_36h11, DICT_ARUCO_MIP_36h12\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
StereoCalib(const vector<string>& imagelist, Size inputBoardSize, string type, float squareSize, float markerSize, cv::aruco::PredefinedDictionaryType arucoDict, string arucoDictFile, bool displayCorners = false, bool useCalibrated=true, bool showRectified=true)
|
||||
{
|
||||
if( imagelist.size() % 2 != 0 )
|
||||
{
|
||||
cout << "Error: the image list contains odd (non-even) number of elements\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const int maxScale = 2;
|
||||
// ARRAY AND VECTOR STORAGE:
|
||||
|
||||
vector<vector<Point2f> > imagePoints[2];
|
||||
vector<vector<Point3f> > objectPoints;
|
||||
Size imageSize;
|
||||
|
||||
int i, j, k, nimages = (int)imagelist.size()/2;
|
||||
|
||||
imagePoints[0].resize(nimages);
|
||||
imagePoints[1].resize(nimages);
|
||||
vector<string> goodImageList;
|
||||
|
||||
Size boardSizeInnerCorners, boardSizeUnits;
|
||||
if (type == "chessboard") {
|
||||
//chess board pattern boardSize is given in inner corners
|
||||
boardSizeInnerCorners = inputBoardSize;
|
||||
boardSizeUnits.height = inputBoardSize.height+1;
|
||||
boardSizeUnits.width = inputBoardSize.width+1;
|
||||
}
|
||||
else if (type == "charucoboard") {
|
||||
//ChArUco board pattern boardSize is given in squares units
|
||||
boardSizeUnits = inputBoardSize;
|
||||
boardSizeInnerCorners.width = inputBoardSize.width - 1;
|
||||
boardSizeInnerCorners.height = inputBoardSize.height - 1;
|
||||
}
|
||||
else {
|
||||
std::cout << "unknown pattern type " << type << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
cv::aruco::Dictionary dictionary;
|
||||
if (arucoDictFile == "None") {
|
||||
dictionary = cv::aruco::getPredefinedDictionary(arucoDict);
|
||||
}
|
||||
else {
|
||||
cv::FileStorage dict_file(arucoDictFile, cv::FileStorage::Mode::READ);
|
||||
cv::FileNode fn(dict_file.root());
|
||||
dictionary.readDictionary(fn);
|
||||
}
|
||||
cv::aruco::CharucoBoard ch_board(boardSizeUnits, squareSize, markerSize, dictionary);
|
||||
cv::aruco::CharucoDetector ch_detector(ch_board);
|
||||
std::vector<int> markerIds;
|
||||
|
||||
for( i = j = 0; i < nimages; i++ )
|
||||
{
|
||||
for( k = 0; k < 2; k++ )
|
||||
{
|
||||
const string& filename = imagelist[i*2+k];
|
||||
Mat img = imread(filename, IMREAD_GRAYSCALE);
|
||||
if(img.empty())
|
||||
break;
|
||||
if( imageSize == Size() )
|
||||
imageSize = img.size();
|
||||
else if( img.size() != imageSize )
|
||||
{
|
||||
cout << "The image " << filename << " has the size different from the first image size. Skipping the pair\n";
|
||||
break;
|
||||
}
|
||||
bool found = false;
|
||||
vector<Point2f>& corners = imagePoints[k][j];
|
||||
for( int scale = 1; scale <= maxScale; scale++ )
|
||||
{
|
||||
Mat timg;
|
||||
if( scale == 1 )
|
||||
timg = img;
|
||||
else
|
||||
resize(img, timg, Size(), scale, scale, INTER_LINEAR_EXACT);
|
||||
|
||||
if (type == "chessboard") {
|
||||
found = findChessboardCorners(timg, boardSizeInnerCorners, corners,
|
||||
CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE);
|
||||
}
|
||||
else if (type == "charucoboard") {
|
||||
ch_detector.detectBoard(timg, corners, markerIds);
|
||||
found = corners.size() == (size_t) (boardSizeInnerCorners.height*boardSizeInnerCorners.width);
|
||||
}
|
||||
else {
|
||||
cout << "Error: unknown pattern " << type << "\n";
|
||||
return;
|
||||
}
|
||||
if( found )
|
||||
{
|
||||
if( scale > 1 )
|
||||
{
|
||||
Mat cornersMat(corners);
|
||||
cornersMat *= 1./scale;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( displayCorners )
|
||||
{
|
||||
cout << filename << endl;
|
||||
Mat cimg, cimg1;
|
||||
cvtColor(img, cimg, COLOR_GRAY2BGR);
|
||||
drawChessboardCorners(cimg, boardSizeInnerCorners, corners, found);
|
||||
double sf = 640./MAX(img.rows, img.cols);
|
||||
resize(cimg, cimg1, Size(), sf, sf, INTER_LINEAR_EXACT);
|
||||
imshow("corners", cimg1);
|
||||
char c = (char)waitKey(500);
|
||||
if( c == 27 || c == 'q' || c == 'Q' ) //Allow ESC to quit
|
||||
exit(-1);
|
||||
}
|
||||
else
|
||||
putchar('.');
|
||||
if( !found )
|
||||
break;
|
||||
if (type == "chessboard") {
|
||||
cornerSubPix(img, corners, Size(11, 11), Size(-1, -1),
|
||||
TermCriteria(TermCriteria::COUNT + TermCriteria::EPS,
|
||||
30, 0.01));
|
||||
}
|
||||
}
|
||||
if( k == 2 )
|
||||
{
|
||||
goodImageList.push_back(imagelist[i*2]);
|
||||
goodImageList.push_back(imagelist[i*2+1]);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
cout << j << " pairs have been successfully detected.\n";
|
||||
nimages = j;
|
||||
if( nimages < 2 )
|
||||
{
|
||||
cout << "Error: too little pairs to run the calibration\n";
|
||||
return;
|
||||
}
|
||||
|
||||
imagePoints[0].resize(nimages);
|
||||
imagePoints[1].resize(nimages);
|
||||
objectPoints.resize(nimages);
|
||||
|
||||
for( i = 0; i < nimages; i++ )
|
||||
{
|
||||
for( j = 0; j < boardSizeInnerCorners.height; j++ )
|
||||
for( k = 0; k < boardSizeInnerCorners.width; k++ )
|
||||
objectPoints[i].push_back(Point3f(k*squareSize, j*squareSize, 0));
|
||||
}
|
||||
|
||||
cout << "Running stereo calibration ...\n";
|
||||
|
||||
Mat cameraMatrix[2], distCoeffs[2];
|
||||
cameraMatrix[0] = initCameraMatrix2D(objectPoints,imagePoints[0],imageSize,0);
|
||||
cameraMatrix[1] = initCameraMatrix2D(objectPoints,imagePoints[1],imageSize,0);
|
||||
Mat R, T, E, F;
|
||||
|
||||
double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
|
||||
cameraMatrix[0], distCoeffs[0],
|
||||
cameraMatrix[1], distCoeffs[1],
|
||||
imageSize, R, T, E, F,
|
||||
CALIB_FIX_ASPECT_RATIO +
|
||||
CALIB_ZERO_TANGENT_DIST +
|
||||
CALIB_USE_INTRINSIC_GUESS +
|
||||
CALIB_SAME_FOCAL_LENGTH +
|
||||
CALIB_RATIONAL_MODEL +
|
||||
CALIB_FIX_K3 + CALIB_FIX_K4 + CALIB_FIX_K5,
|
||||
TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 100, 1e-5) );
|
||||
cout << "done with RMS error=" << rms << endl;
|
||||
|
||||
// CALIBRATION QUALITY CHECK
|
||||
// because the output fundamental matrix implicitly
|
||||
// includes all the output information,
|
||||
// we can check the quality of calibration using the
|
||||
// epipolar geometry constraint: m2^t*F*m1=0
|
||||
double err = 0;
|
||||
int npoints = 0;
|
||||
vector<Vec3f> lines[2];
|
||||
for( i = 0; i < nimages; i++ )
|
||||
{
|
||||
int npt = (int)imagePoints[0][i].size();
|
||||
Mat imgpt[2];
|
||||
for( k = 0; k < 2; k++ )
|
||||
{
|
||||
imgpt[k] = Mat(imagePoints[k][i]);
|
||||
undistortPoints(imgpt[k], imgpt[k], cameraMatrix[k], distCoeffs[k], Mat(), cameraMatrix[k]);
|
||||
computeCorrespondEpilines(imgpt[k], k+1, F, lines[k]);
|
||||
}
|
||||
for( j = 0; j < npt; j++ )
|
||||
{
|
||||
double errij = fabs(imagePoints[0][i][j].x*lines[1][j][0] +
|
||||
imagePoints[0][i][j].y*lines[1][j][1] + lines[1][j][2]) +
|
||||
fabs(imagePoints[1][i][j].x*lines[0][j][0] +
|
||||
imagePoints[1][i][j].y*lines[0][j][1] + lines[0][j][2]);
|
||||
err += errij;
|
||||
}
|
||||
npoints += npt;
|
||||
}
|
||||
cout << "average epipolar err = " << err/npoints << endl;
|
||||
|
||||
// save intrinsic parameters
|
||||
FileStorage fs("intrinsics.yml", FileStorage::WRITE);
|
||||
if( fs.isOpened() )
|
||||
{
|
||||
fs << "M1" << cameraMatrix[0] << "D1" << distCoeffs[0] <<
|
||||
"M2" << cameraMatrix[1] << "D2" << distCoeffs[1];
|
||||
fs.release();
|
||||
}
|
||||
else
|
||||
cout << "Error: can not save the intrinsic parameters\n";
|
||||
|
||||
Mat R1, R2, P1, P2, Q;
|
||||
Rect validRoi[2];
|
||||
|
||||
stereoRectify(cameraMatrix[0], distCoeffs[0],
|
||||
cameraMatrix[1], distCoeffs[1],
|
||||
imageSize, R, T, R1, R2, P1, P2, Q,
|
||||
CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]);
|
||||
|
||||
fs.open("extrinsics.yml", FileStorage::WRITE);
|
||||
if( fs.isOpened() )
|
||||
{
|
||||
fs << "R" << R << "T" << T << "R1" << R1 << "R2" << R2 << "P1" << P1 << "P2" << P2 << "Q" << Q;
|
||||
fs.release();
|
||||
}
|
||||
else
|
||||
cout << "Error: can not save the extrinsic parameters\n";
|
||||
|
||||
// OpenCV can handle left-right
|
||||
// or up-down camera arrangements
|
||||
bool isVerticalStereo = fabs(P2.at<double>(1, 3)) > fabs(P2.at<double>(0, 3));
|
||||
|
||||
// COMPUTE AND DISPLAY RECTIFICATION
|
||||
if( !showRectified )
|
||||
return;
|
||||
|
||||
Mat rmap[2][2];
|
||||
// IF BY CALIBRATED (BOUGUET'S METHOD)
|
||||
if( useCalibrated )
|
||||
{
|
||||
// we already computed everything
|
||||
}
|
||||
// OR ELSE HARTLEY'S METHOD
|
||||
else
|
||||
// use intrinsic parameters of each camera, but
|
||||
// compute the rectification transformation directly
|
||||
// from the fundamental matrix
|
||||
{
|
||||
vector<Point2f> allimgpt[2];
|
||||
for( k = 0; k < 2; k++ )
|
||||
{
|
||||
for( i = 0; i < nimages; i++ )
|
||||
std::copy(imagePoints[k][i].begin(), imagePoints[k][i].end(), back_inserter(allimgpt[k]));
|
||||
}
|
||||
F = findFundamentalMat(Mat(allimgpt[0]), Mat(allimgpt[1]), FM_8POINT, 0, 0);
|
||||
Mat H1, H2;
|
||||
stereoRectifyUncalibrated(Mat(allimgpt[0]), Mat(allimgpt[1]), F, imageSize, H1, H2, 3);
|
||||
|
||||
R1 = cameraMatrix[0].inv()*H1*cameraMatrix[0];
|
||||
R2 = cameraMatrix[1].inv()*H2*cameraMatrix[1];
|
||||
P1 = cameraMatrix[0];
|
||||
P2 = cameraMatrix[1];
|
||||
}
|
||||
|
||||
//Precompute maps for cv::remap()
|
||||
initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
|
||||
initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);
|
||||
|
||||
Mat canvas;
|
||||
double sf;
|
||||
int w, h;
|
||||
if( !isVerticalStereo )
|
||||
{
|
||||
sf = 600./MAX(imageSize.width, imageSize.height);
|
||||
w = cvRound(imageSize.width*sf);
|
||||
h = cvRound(imageSize.height*sf);
|
||||
canvas.create(h, w*2, CV_8UC3);
|
||||
}
|
||||
else
|
||||
{
|
||||
sf = 300./MAX(imageSize.width, imageSize.height);
|
||||
w = cvRound(imageSize.width*sf);
|
||||
h = cvRound(imageSize.height*sf);
|
||||
canvas.create(h*2, w, CV_8UC3);
|
||||
}
|
||||
|
||||
for( i = 0; i < nimages; i++ )
|
||||
{
|
||||
for( k = 0; k < 2; k++ )
|
||||
{
|
||||
Mat img = imread(goodImageList[i*2+k], IMREAD_GRAYSCALE), rimg, cimg;
|
||||
remap(img, rimg, rmap[k][0], rmap[k][1], INTER_LINEAR);
|
||||
cvtColor(rimg, cimg, COLOR_GRAY2BGR);
|
||||
Mat canvasPart = !isVerticalStereo ? canvas(Rect(w*k, 0, w, h)) : canvas(Rect(0, h*k, w, h));
|
||||
resize(cimg, canvasPart, canvasPart.size(), 0, 0, INTER_AREA);
|
||||
if( useCalibrated )
|
||||
{
|
||||
Rect vroi(cvRound(validRoi[k].x*sf), cvRound(validRoi[k].y*sf),
|
||||
cvRound(validRoi[k].width*sf), cvRound(validRoi[k].height*sf));
|
||||
rectangle(canvasPart, vroi, Scalar(0,0,255), 3, 8);
|
||||
}
|
||||
}
|
||||
|
||||
if( !isVerticalStereo )
|
||||
for( j = 0; j < canvas.rows; j += 16 )
|
||||
line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);
|
||||
else
|
||||
for( j = 0; j < canvas.cols; j += 16 )
|
||||
line(canvas, Point(j, 0), Point(j, canvas.rows), Scalar(0, 255, 0), 1, 8);
|
||||
imshow("rectified", canvas);
|
||||
char c = (char)waitKey();
|
||||
if( c == 27 || c == 'q' || c == 'Q' )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool readStringList( const string& filename, vector<string>& l )
|
||||
{
|
||||
l.resize(0);
|
||||
FileStorage fs(filename, FileStorage::READ);
|
||||
if( !fs.isOpened() )
|
||||
return false;
|
||||
FileNode n = fs.getFirstTopLevelNode();
|
||||
if( n.type() != FileNode::SEQ )
|
||||
return false;
|
||||
FileNodeIterator it = n.begin(), it_end = n.end();
|
||||
for( ; it != it_end; ++it )
|
||||
l.push_back((string)*it);
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Size inputBoardSize;
|
||||
string imagelistfn;
|
||||
bool showRectified;
|
||||
cv::CommandLineParser parser(argc, argv, "{w|9|}{h|6|}{t|chessboard|}{s|1.0|}{ms|0.5|}{ad|DICT_4X4_50|}{adf|None|}{nr||}{help||}{@input|stereo_calib.xml|}");
|
||||
if (parser.has("help"))
|
||||
return print_help(argv);
|
||||
showRectified = !parser.has("nr");
|
||||
imagelistfn = samples::findFile(parser.get<string>("@input"));
|
||||
inputBoardSize.width = parser.get<int>("w");
|
||||
inputBoardSize.height = parser.get<int>("h");
|
||||
string type = parser.get<string>("t");
|
||||
float squareSize = parser.get<float>("s");
|
||||
float markerSize = parser.get<float>("ms");
|
||||
string arucoDictName = parser.get<string>("ad");
|
||||
string arucoDictFile = parser.get<string>("adf");
|
||||
|
||||
cv::aruco::PredefinedDictionaryType arucoDict;
|
||||
if (arucoDictName == "DICT_4X4_50") { arucoDict = cv::aruco::DICT_4X4_50; }
|
||||
else if (arucoDictName == "DICT_4X4_100") { arucoDict = cv::aruco::DICT_4X4_100; }
|
||||
else if (arucoDictName == "DICT_4X4_250") { arucoDict = cv::aruco::DICT_4X4_250; }
|
||||
else if (arucoDictName == "DICT_4X4_1000") { arucoDict = cv::aruco::DICT_4X4_1000; }
|
||||
else if (arucoDictName == "DICT_5X5_50") { arucoDict = cv::aruco::DICT_5X5_50; }
|
||||
else if (arucoDictName == "DICT_5X5_100") { arucoDict = cv::aruco::DICT_5X5_100; }
|
||||
else if (arucoDictName == "DICT_5X5_250") { arucoDict = cv::aruco::DICT_5X5_250; }
|
||||
else if (arucoDictName == "DICT_5X5_1000") { arucoDict = cv::aruco::DICT_5X5_1000; }
|
||||
else if (arucoDictName == "DICT_6X6_50") { arucoDict = cv::aruco::DICT_6X6_50; }
|
||||
else if (arucoDictName == "DICT_6X6_100") { arucoDict = cv::aruco::DICT_6X6_100; }
|
||||
else if (arucoDictName == "DICT_6X6_250") { arucoDict = cv::aruco::DICT_6X6_250; }
|
||||
else if (arucoDictName == "DICT_6X6_1000") { arucoDict = cv::aruco::DICT_6X6_1000; }
|
||||
else if (arucoDictName == "DICT_7X7_50") { arucoDict = cv::aruco::DICT_7X7_50; }
|
||||
else if (arucoDictName == "DICT_7X7_100") { arucoDict = cv::aruco::DICT_7X7_100; }
|
||||
else if (arucoDictName == "DICT_7X7_250") { arucoDict = cv::aruco::DICT_7X7_250; }
|
||||
else if (arucoDictName == "DICT_7X7_1000") { arucoDict = cv::aruco::DICT_7X7_1000; }
|
||||
else if (arucoDictName == "DICT_ARUCO_ORIGINAL") { arucoDict = cv::aruco::DICT_ARUCO_ORIGINAL; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_16h5") { arucoDict = cv::aruco::DICT_APRILTAG_16h5; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_25h9") { arucoDict = cv::aruco::DICT_APRILTAG_25h9; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h10") { arucoDict = cv::aruco::DICT_APRILTAG_36h10; }
|
||||
else if (arucoDictName == "DICT_APRILTAG_36h11") { arucoDict = cv::aruco::DICT_APRILTAG_36h11; }
|
||||
else if (arucoDictName == "DICT_ARUCO_MIP_36h12") { arucoDict = cv::aruco::DICT_ARUCO_MIP_36h12; }
|
||||
else {
|
||||
cout << "incorrect name of aruco dictionary \n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
vector<string> imagelist;
|
||||
bool ok = readStringList(imagelistfn, imagelist);
|
||||
if(!ok || imagelist.empty())
|
||||
{
|
||||
cout << "can not open " << imagelistfn << " or the string list is empty" << endl;
|
||||
return print_help(argv);
|
||||
}
|
||||
|
||||
StereoCalib(imagelist, inputBoardSize, type, squareSize, markerSize, arucoDict, arucoDictFile, false, true, showRectified);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* stereo_match.cpp
|
||||
* calibration
|
||||
*
|
||||
* Created by Victor Eruhimov on 1/18/10.
|
||||
* Copyright 2010 Argus Corp. All rights reserved.
|
||||
*
|
||||
*/
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/stereo.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/core/utility.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
static void print_help(char** argv)
|
||||
{
|
||||
printf("\nDemo stereo matching converting L and R images into disparity and point clouds\n");
|
||||
printf("\nUsage: %s <left_image> <right_image> [--algorithm=bm|sgbm|hh|hh4|sgbm3way] [--blocksize=<block_size>]\n"
|
||||
"[--max-disparity=<max_disparity>] [--scale=scale_factor>] [-i=<intrinsic_filename>] [-e=<extrinsic_filename>]\n"
|
||||
"[--no-display] [--color] [-o=<disparity_image>] [-p=<point_cloud_file>]\n", argv[0]);
|
||||
}
|
||||
|
||||
static void saveXYZ(const char* filename, const Mat& mat)
|
||||
{
|
||||
const double max_z = 1.0e4;
|
||||
FILE* fp = fopen(filename, "wt");
|
||||
for(int y = 0; y < mat.rows; y++)
|
||||
{
|
||||
for(int x = 0; x < mat.cols; x++)
|
||||
{
|
||||
Vec3f point = mat.at<Vec3f>(y, x);
|
||||
if(fabs(point[2] - max_z) < FLT_EPSILON || fabs(point[2]) > max_z) continue;
|
||||
fprintf(fp, "%f %f %f\n", point[0], point[1], point[2]);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::string img1_filename = "";
|
||||
std::string img2_filename = "";
|
||||
std::string intrinsic_filename = "";
|
||||
std::string extrinsic_filename = "";
|
||||
std::string disparity_filename = "";
|
||||
std::string point_cloud_filename = "";
|
||||
|
||||
enum { STEREO_BM=0, STEREO_SGBM=1, STEREO_HH=2, STEREO_VAR=3, STEREO_3WAY=4, STEREO_HH4=5 };
|
||||
int alg = STEREO_SGBM;
|
||||
int SADWindowSize, numberOfDisparities;
|
||||
bool no_display;
|
||||
bool color_display;
|
||||
float scale;
|
||||
|
||||
Ptr<StereoBM> bm = StereoBM::create(16,9);
|
||||
Ptr<StereoSGBM> sgbm = StereoSGBM::create(0,16,3);
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{@arg1||}{@arg2||}{help h||}{algorithm||}{max-disparity|0|}{blocksize|0|}{no-display||}{color||}{scale|1|}{i||}{e||}{o||}{p||}");
|
||||
if(parser.has("help"))
|
||||
{
|
||||
print_help(argv);
|
||||
return 0;
|
||||
}
|
||||
img1_filename = samples::findFile(parser.get<std::string>(0));
|
||||
img2_filename = samples::findFile(parser.get<std::string>(1));
|
||||
if (parser.has("algorithm"))
|
||||
{
|
||||
std::string _alg = parser.get<std::string>("algorithm");
|
||||
alg = _alg == "bm" ? STEREO_BM :
|
||||
_alg == "sgbm" ? STEREO_SGBM :
|
||||
_alg == "hh" ? STEREO_HH :
|
||||
_alg == "var" ? STEREO_VAR :
|
||||
_alg == "hh4" ? STEREO_HH4 :
|
||||
_alg == "sgbm3way" ? STEREO_3WAY : -1;
|
||||
}
|
||||
numberOfDisparities = parser.get<int>("max-disparity");
|
||||
SADWindowSize = parser.get<int>("blocksize");
|
||||
scale = parser.get<float>("scale");
|
||||
no_display = parser.has("no-display");
|
||||
color_display = parser.has("color");
|
||||
if( parser.has("i") )
|
||||
intrinsic_filename = parser.get<std::string>("i");
|
||||
if( parser.has("e") )
|
||||
extrinsic_filename = parser.get<std::string>("e");
|
||||
if( parser.has("o") )
|
||||
disparity_filename = parser.get<std::string>("o");
|
||||
if( parser.has("p") )
|
||||
point_cloud_filename = parser.get<std::string>("p");
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 1;
|
||||
}
|
||||
if( alg < 0 )
|
||||
{
|
||||
printf("Command-line parameter error: Unknown stereo algorithm\n\n");
|
||||
print_help(argv);
|
||||
return -1;
|
||||
}
|
||||
if ( numberOfDisparities < 1 || numberOfDisparities % 16 != 0 )
|
||||
{
|
||||
printf("Command-line parameter error: The max disparity (--maxdisparity=<...>) must be a positive integer divisible by 16\n");
|
||||
print_help(argv);
|
||||
return -1;
|
||||
}
|
||||
if (scale < 0)
|
||||
{
|
||||
printf("Command-line parameter error: The scale factor (--scale=<...>) must be a positive floating-point number\n");
|
||||
return -1;
|
||||
}
|
||||
if (SADWindowSize < 1 || SADWindowSize % 2 != 1)
|
||||
{
|
||||
printf("Command-line parameter error: The block size (--blocksize=<...>) must be a positive odd number\n");
|
||||
return -1;
|
||||
}
|
||||
if( img1_filename.empty() || img2_filename.empty() )
|
||||
{
|
||||
printf("Command-line parameter error: both left and right images must be specified\n");
|
||||
return -1;
|
||||
}
|
||||
if( (!intrinsic_filename.empty()) ^ (!extrinsic_filename.empty()) )
|
||||
{
|
||||
printf("Command-line parameter error: either both intrinsic and extrinsic parameters must be specified, or none of them (when the stereo pair is already rectified)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( extrinsic_filename.empty() && !point_cloud_filename.empty() )
|
||||
{
|
||||
printf("Command-line parameter error: extrinsic and intrinsic parameters must be specified to compute the point cloud\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int color_mode = alg == STEREO_BM ? 0 : -1;
|
||||
Mat img1 = imread(img1_filename, color_mode);
|
||||
Mat img2 = imread(img2_filename, color_mode);
|
||||
|
||||
if (img1.empty())
|
||||
{
|
||||
printf("Command-line parameter error: could not load the first input image file\n");
|
||||
return -1;
|
||||
}
|
||||
if (img2.empty())
|
||||
{
|
||||
printf("Command-line parameter error: could not load the second input image file\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (scale != 1.f)
|
||||
{
|
||||
Mat temp1, temp2;
|
||||
int method = scale < 1 ? INTER_AREA : INTER_CUBIC;
|
||||
resize(img1, temp1, Size(), scale, scale, method);
|
||||
img1 = temp1;
|
||||
resize(img2, temp2, Size(), scale, scale, method);
|
||||
img2 = temp2;
|
||||
}
|
||||
|
||||
Size img_size = img1.size();
|
||||
|
||||
Rect roi1, roi2;
|
||||
Mat Q;
|
||||
|
||||
if( !intrinsic_filename.empty() )
|
||||
{
|
||||
// reading intrinsic parameters
|
||||
FileStorage fs(intrinsic_filename, FileStorage::READ);
|
||||
if(!fs.isOpened())
|
||||
{
|
||||
printf("Failed to open file %s\n", intrinsic_filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat M1, D1, M2, D2;
|
||||
fs["M1"] >> M1;
|
||||
fs["D1"] >> D1;
|
||||
fs["M2"] >> M2;
|
||||
fs["D2"] >> D2;
|
||||
|
||||
M1 *= scale;
|
||||
M2 *= scale;
|
||||
|
||||
fs.open(extrinsic_filename, FileStorage::READ);
|
||||
if(!fs.isOpened())
|
||||
{
|
||||
printf("Failed to open file %s\n", extrinsic_filename.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat R, T, R1, P1, R2, P2;
|
||||
fs["R"] >> R;
|
||||
fs["T"] >> T;
|
||||
|
||||
stereoRectify( M1, D1, M2, D2, img_size, R, T, R1, R2, P1, P2, Q, STEREO_ZERO_DISPARITY, -1, img_size, &roi1, &roi2 );
|
||||
|
||||
Mat map11, map12, map21, map22;
|
||||
initUndistortRectifyMap(M1, D1, R1, P1, img_size, CV_16SC2, map11, map12);
|
||||
initUndistortRectifyMap(M2, D2, R2, P2, img_size, CV_16SC2, map21, map22);
|
||||
|
||||
Mat img1r, img2r;
|
||||
remap(img1, img1r, map11, map12, INTER_LINEAR);
|
||||
remap(img2, img2r, map21, map22, INTER_LINEAR);
|
||||
|
||||
img1 = img1r;
|
||||
img2 = img2r;
|
||||
}
|
||||
|
||||
numberOfDisparities = numberOfDisparities > 0 ? numberOfDisparities : ((img_size.width/8) + 15) & -16;
|
||||
|
||||
bm->setROI1(roi1);
|
||||
bm->setROI2(roi2);
|
||||
bm->setPreFilterCap(31);
|
||||
bm->setBlockSize(SADWindowSize > 0 ? SADWindowSize : 9);
|
||||
bm->setMinDisparity(0);
|
||||
bm->setNumDisparities(numberOfDisparities);
|
||||
bm->setTextureThreshold(10);
|
||||
bm->setUniquenessRatio(15);
|
||||
bm->setSpeckleWindowSize(100);
|
||||
bm->setSpeckleRange(32);
|
||||
bm->setDisp12MaxDiff(1);
|
||||
|
||||
sgbm->setPreFilterCap(63);
|
||||
int sgbmWinSize = SADWindowSize > 0 ? SADWindowSize : 3;
|
||||
sgbm->setBlockSize(sgbmWinSize);
|
||||
|
||||
int cn = img1.channels();
|
||||
|
||||
sgbm->setP1(8*cn*sgbmWinSize*sgbmWinSize);
|
||||
sgbm->setP2(32*cn*sgbmWinSize*sgbmWinSize);
|
||||
sgbm->setMinDisparity(0);
|
||||
sgbm->setNumDisparities(numberOfDisparities);
|
||||
sgbm->setUniquenessRatio(10);
|
||||
sgbm->setSpeckleWindowSize(100);
|
||||
sgbm->setSpeckleRange(32);
|
||||
sgbm->setDisp12MaxDiff(1);
|
||||
if(alg==STEREO_HH)
|
||||
sgbm->setMode(StereoSGBM::MODE_HH);
|
||||
else if(alg==STEREO_SGBM)
|
||||
sgbm->setMode(StereoSGBM::MODE_SGBM);
|
||||
else if(alg==STEREO_HH4)
|
||||
sgbm->setMode(StereoSGBM::MODE_HH4);
|
||||
else if(alg==STEREO_3WAY)
|
||||
sgbm->setMode(StereoSGBM::MODE_SGBM_3WAY);
|
||||
|
||||
Mat disp, disp8;
|
||||
//Mat img1p, img2p, dispp;
|
||||
//copyMakeBorder(img1, img1p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
|
||||
//copyMakeBorder(img2, img2p, 0, 0, numberOfDisparities, 0, IPL_BORDER_REPLICATE);
|
||||
|
||||
int64 t = getTickCount();
|
||||
float disparity_multiplier = 1.0f;
|
||||
if( alg == STEREO_BM )
|
||||
{
|
||||
bm->compute(img1, img2, disp);
|
||||
if (disp.type() == CV_16S)
|
||||
disparity_multiplier = 16.0f;
|
||||
}
|
||||
else if( alg == STEREO_SGBM || alg == STEREO_HH || alg == STEREO_HH4 || alg == STEREO_3WAY )
|
||||
{
|
||||
sgbm->compute(img1, img2, disp);
|
||||
if (disp.type() == CV_16S)
|
||||
disparity_multiplier = 16.0f;
|
||||
}
|
||||
t = getTickCount() - t;
|
||||
printf("Time elapsed: %fms\n", t*1000/getTickFrequency());
|
||||
|
||||
//disp = dispp.colRange(numberOfDisparities, img1p.cols);
|
||||
if( alg != STEREO_VAR )
|
||||
disp.convertTo(disp8, CV_8U, 255/(numberOfDisparities*16.));
|
||||
else
|
||||
disp.convertTo(disp8, CV_8U);
|
||||
|
||||
Mat disp8_3c;
|
||||
if (color_display)
|
||||
cv::applyColorMap(disp8, disp8_3c, COLORMAP_TURBO);
|
||||
|
||||
if(!disparity_filename.empty())
|
||||
imwrite(disparity_filename, color_display ? disp8_3c : disp8);
|
||||
|
||||
if(!point_cloud_filename.empty())
|
||||
{
|
||||
printf("storing the point cloud...");
|
||||
fflush(stdout);
|
||||
Mat xyz;
|
||||
Mat floatDisp;
|
||||
disp.convertTo(floatDisp, CV_32F, 1.0f / disparity_multiplier);
|
||||
reprojectImageTo3D(floatDisp, xyz, Q, true);
|
||||
saveXYZ(point_cloud_filename.c_str(), xyz);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if( !no_display )
|
||||
{
|
||||
std::ostringstream oss;
|
||||
oss << "disparity " << (alg==STEREO_BM ? "bm" :
|
||||
alg==STEREO_SGBM ? "sgbm" :
|
||||
alg==STEREO_HH ? "hh" :
|
||||
alg==STEREO_VAR ? "var" :
|
||||
alg==STEREO_HH4 ? "hh4" :
|
||||
alg==STEREO_3WAY ? "sgbm3way" : "");
|
||||
oss << " blocksize:" << (alg==STEREO_BM ? SADWindowSize : sgbmWinSize);
|
||||
oss << " max-disparity:" << numberOfDisparities;
|
||||
std::string disp_name = oss.str();
|
||||
|
||||
namedWindow("left", cv::WINDOW_NORMAL);
|
||||
imshow("left", img1);
|
||||
namedWindow("right", cv::WINDOW_NORMAL);
|
||||
imshow("right", img2);
|
||||
namedWindow(disp_name, cv::WINDOW_AUTOSIZE);
|
||||
imshow(disp_name, color_display ? disp8_3c : disp8);
|
||||
|
||||
printf("press ESC key or CTRL+C to close...");
|
||||
fflush(stdout);
|
||||
printf("\n");
|
||||
while(1)
|
||||
{
|
||||
if(waitKey() == 27) //ESC (prevents closing on actions like taking screenshots)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file AddingImagesTrackbar.cpp
|
||||
* @brief Simple linear blender ( dst = alpha*src1 + beta*src2 )
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using std::cout;
|
||||
|
||||
/** Global Variables */
|
||||
const int alpha_slider_max = 100;
|
||||
int alpha_slider;
|
||||
double alpha;
|
||||
double beta;
|
||||
|
||||
/** Matrices to store images */
|
||||
Mat src1;
|
||||
Mat src2;
|
||||
Mat dst;
|
||||
|
||||
//![on_trackbar]
|
||||
/**
|
||||
* @function on_trackbar
|
||||
* @brief Callback for trackbar
|
||||
*/
|
||||
static void on_trackbar(int pos, void* userdata) {
|
||||
(void) userdata;
|
||||
alpha = (double)pos / alpha_slider_max;
|
||||
beta = (1.0 - alpha);
|
||||
addWeighted(src1, alpha, src2, beta, 0.0, dst);
|
||||
imshow("Linear Blend", dst);
|
||||
}
|
||||
//![on_trackbar]
|
||||
|
||||
/**
|
||||
* @function main
|
||||
* @brief Main function
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
//![load]
|
||||
/// Read images (both must be of the same size and type)
|
||||
src1 = imread(samples::findFile("LinuxLogo.jpg"));
|
||||
src2 = imread(samples::findFile("WindowsLogo.jpg"));
|
||||
//![load]
|
||||
|
||||
if (src1.empty()) { cout << "Error loading src1 \n"; return -1; }
|
||||
if (src2.empty()) { cout << "Error loading src2 \n"; return -1; }
|
||||
|
||||
// Initialize trackbar value
|
||||
alpha_slider = 0;
|
||||
|
||||
//![window]
|
||||
namedWindow("Linear Blend", WINDOW_AUTOSIZE); //Create Window
|
||||
//![window]
|
||||
|
||||
//![create_trackbar]
|
||||
char TrackbarName[50];
|
||||
snprintf(TrackbarName, sizeof(TrackbarName), "Alpha x %d", alpha_slider_max);
|
||||
// Example userdata: Pass a pointer to an integer as userdata
|
||||
createTrackbar(TrackbarName, "Linear Blend", &alpha_slider, alpha_slider_max, on_trackbar);
|
||||
//![create_trackbar]
|
||||
|
||||
/// Show initial result
|
||||
on_trackbar(alpha_slider, nullptr);
|
||||
|
||||
/// Wait for user input
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file BasicLinearTransformsTrackbar.cpp
|
||||
* @brief Simple program to change contrast and brightness
|
||||
* @date Mon, June 6, 2011
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
|
||||
using namespace cv;
|
||||
|
||||
/** Global Variables */
|
||||
const int alpha_max = 5;
|
||||
const int beta_max = 125;
|
||||
int alpha; /**< Simple contrast control */
|
||||
int beta; /**< Simple brightness control*/
|
||||
|
||||
/** Matrices to store images */
|
||||
Mat image;
|
||||
|
||||
/**
|
||||
* @function on_trackbar
|
||||
* @brief Called whenever any of alpha or beta changes
|
||||
*/
|
||||
static void on_trackbar( int, void* )
|
||||
{
|
||||
Mat new_image = Mat::zeros( image.size(), image.type() );
|
||||
|
||||
for( int y = 0; y < image.rows; y++ )
|
||||
for( int x = 0; x < image.cols; x++ )
|
||||
for( int c = 0; c < 3; c++ )
|
||||
new_image.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( alpha*( image.at<Vec3b>(y,x)[c] ) + beta );
|
||||
|
||||
imshow("New Image", new_image);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function main
|
||||
* @brief Main function
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Read image given by user
|
||||
String imageName("lena.jpg"); // by default
|
||||
if (argc > 1)
|
||||
{
|
||||
imageName = argv[1];
|
||||
}
|
||||
image = imread( samples::findFile( imageName ) );
|
||||
|
||||
/// Initialize values
|
||||
alpha = 1;
|
||||
beta = 0;
|
||||
|
||||
/// Create Windows
|
||||
namedWindow("Original Image", 1);
|
||||
namedWindow("New Image", 1);
|
||||
|
||||
/// Create Trackbars
|
||||
createTrackbar( "Contrast", "New Image", &alpha, alpha_max, on_trackbar );
|
||||
createTrackbar( "Brightness", "New Image", &beta, beta_max, on_trackbar );
|
||||
|
||||
/// Show some stuff
|
||||
imshow("Original Image", image);
|
||||
imshow("New Image", image);
|
||||
|
||||
/// Wait until user press some key
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* @function EqualizeHist_Demo.cpp
|
||||
* @brief Demo code for equalizeHist function
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//! [Load image]
|
||||
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
|
||||
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
|
||||
if( src.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
//! [Load image]
|
||||
|
||||
//! [Convert to grayscale]
|
||||
cvtColor( src, src, COLOR_BGR2GRAY );
|
||||
//! [Convert to grayscale]
|
||||
|
||||
//! [Apply Histogram Equalization]
|
||||
Mat dst;
|
||||
equalizeHist( src, dst );
|
||||
//! [Apply Histogram Equalization]
|
||||
|
||||
//! [Display results]
|
||||
imshow( "Source image", src );
|
||||
imshow( "Equalized Image", dst );
|
||||
//! [Display results]
|
||||
|
||||
//! [Wait until user exits the program]
|
||||
waitKey();
|
||||
//! [Wait until user exits the program]
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @file MatchTemplate_Demo.cpp
|
||||
* @brief Sample code to use the function MatchTemplate
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
//! [declare]
|
||||
/// Global Variables
|
||||
bool use_mask;
|
||||
Mat img; Mat templ; Mat mask; Mat result;
|
||||
const char* image_window = "Source Image";
|
||||
const char* result_window = "Result window";
|
||||
|
||||
int match_method;
|
||||
int max_Trackbar = 5;
|
||||
//! [declare]
|
||||
|
||||
/// Function Headers
|
||||
void MatchingMethod( int, void* );
|
||||
|
||||
const char* keys =
|
||||
"{ help h| | Print help message. }"
|
||||
"{ @input1 | Template_Matching_Original_Image.jpg | image_name }"
|
||||
"{ @input2 | Template_Matching_Template_Image.jpg | template_name }"
|
||||
"{ @input3 | | mask_name }";
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
samples::addSamplesDataSearchSubDirectory( "doc/tutorials/imgproc/histograms/template_matching/images" );
|
||||
|
||||
//! [load_image]
|
||||
/// Load image and template
|
||||
img = imread( samples::findFile( parser.get<String>("@input1") ) );
|
||||
templ = imread( samples::findFile( parser.get<String>("@input2") ), IMREAD_COLOR );
|
||||
|
||||
if(argc > 3) {
|
||||
use_mask = true;
|
||||
mask = imread(samples::findFile( parser.get<String>("@input3") ), IMREAD_COLOR );
|
||||
}
|
||||
|
||||
if(img.empty() || templ.empty() || (use_mask && mask.empty()))
|
||||
{
|
||||
cout << "Can't read one of the images" << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//! [load_image]
|
||||
|
||||
//! [create_windows]
|
||||
/// Create windows
|
||||
namedWindow( image_window, WINDOW_AUTOSIZE );
|
||||
namedWindow( result_window, WINDOW_AUTOSIZE );
|
||||
//! [create_windows]
|
||||
|
||||
//! [create_trackbar]
|
||||
/// Create Trackbar
|
||||
const char* trackbar_label = "Method: \n 0: SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED";
|
||||
createTrackbar( trackbar_label, image_window, &match_method, max_Trackbar, MatchingMethod );
|
||||
//! [create_trackbar]
|
||||
|
||||
MatchingMethod( 0, 0 );
|
||||
|
||||
//! [wait_key]
|
||||
waitKey(0);
|
||||
return EXIT_SUCCESS;
|
||||
//! [wait_key]
|
||||
}
|
||||
|
||||
/**
|
||||
* @function MatchingMethod
|
||||
* @brief Trackbar callback
|
||||
*/
|
||||
void MatchingMethod( int, void* )
|
||||
{
|
||||
//! [copy_source]
|
||||
/// Source image to display
|
||||
Mat img_display;
|
||||
img.copyTo( img_display );
|
||||
//! [copy_source]
|
||||
|
||||
//! [create_result_matrix]
|
||||
/// Create the result matrix
|
||||
int result_cols = img.cols - templ.cols + 1;
|
||||
int result_rows = img.rows - templ.rows + 1;
|
||||
|
||||
result.create( result_rows, result_cols, CV_32FC1 );
|
||||
//! [create_result_matrix]
|
||||
|
||||
//! [match_template]
|
||||
/// Do the Matching and Normalize
|
||||
bool method_accepts_mask = (TM_SQDIFF == match_method || match_method == TM_CCORR_NORMED);
|
||||
if (use_mask && method_accepts_mask)
|
||||
{ matchTemplate( img, templ, result, match_method, mask); }
|
||||
else
|
||||
{ matchTemplate( img, templ, result, match_method); }
|
||||
//! [match_template]
|
||||
|
||||
//! [normalize]
|
||||
normalize( result, result, 0, 1, NORM_MINMAX, -1, Mat() );
|
||||
//! [normalize]
|
||||
|
||||
//! [best_match]
|
||||
/// Localizing the best match with minMaxLoc
|
||||
double minVal; double maxVal; Point minLoc; Point maxLoc;
|
||||
Point matchLoc;
|
||||
|
||||
minMaxLoc( result, &minVal, &maxVal, &minLoc, &maxLoc, Mat() );
|
||||
//! [best_match]
|
||||
|
||||
//! [match_loc]
|
||||
/// For SQDIFF and SQDIFF_NORMED, the best matches are lower values. For all the other methods, the higher the better
|
||||
if( match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED )
|
||||
{ matchLoc = minLoc; }
|
||||
else
|
||||
{ matchLoc = maxLoc; }
|
||||
//! [match_loc]
|
||||
|
||||
//! [imshow]
|
||||
/// Show me what you got
|
||||
rectangle( img_display, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
rectangle( result, matchLoc, Point( matchLoc.x + templ.cols , matchLoc.y + templ.rows ), Scalar::all(0), 2, 8, 0 );
|
||||
|
||||
imshow( image_window, img_display );
|
||||
imshow( result_window, result );
|
||||
//! [imshow]
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @file BackProject_Demo1.cpp
|
||||
* @brief Sample code for backproject function usage
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/// Global Variables
|
||||
Mat hue;
|
||||
int bins = 25;
|
||||
|
||||
/// Function Headers
|
||||
void Hist_and_Backproj(int, void* );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
//! [Read the image]
|
||||
CommandLineParser parser( argc, argv, "{@input |Back_Projection_Theory0.jpg| input image}" );
|
||||
samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/histograms/back_projection/images");
|
||||
Mat src = imread(samples::findFile(parser.get<String>( "@input" )) );
|
||||
if( src.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
//! [Read the image]
|
||||
|
||||
//! [Transform it to HSV]
|
||||
Mat hsv;
|
||||
cvtColor( src, hsv, COLOR_BGR2HSV );
|
||||
//! [Transform it to HSV]
|
||||
|
||||
//! [Use only the Hue value]
|
||||
hue.create(hsv.size(), hsv.depth());
|
||||
int ch[] = { 0, 0 };
|
||||
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
|
||||
//! [Use only the Hue value]
|
||||
|
||||
//! [Create Trackbar to enter the number of bins]
|
||||
const char* window_image = "Source image";
|
||||
namedWindow( window_image );
|
||||
createTrackbar("* Hue bins: ", window_image, &bins, 180, Hist_and_Backproj );
|
||||
Hist_and_Backproj(0, 0);
|
||||
//! [Create Trackbar to enter the number of bins]
|
||||
|
||||
//! [Show the image]
|
||||
imshow( window_image, src );
|
||||
// Wait until user exits the program
|
||||
waitKey();
|
||||
//! [Show the image]
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Hist_and_Backproj
|
||||
* @brief Callback to Trackbar
|
||||
*/
|
||||
void Hist_and_Backproj(int, void* )
|
||||
{
|
||||
//! [initialize]
|
||||
int histSize = MAX( bins, 2 );
|
||||
float hue_range[] = { 0, 180 };
|
||||
const float* ranges[] = { hue_range };
|
||||
//! [initialize]
|
||||
|
||||
//! [Get the Histogram and normalize it]
|
||||
Mat hist;
|
||||
calcHist( &hue, 1, 0, Mat(), hist, 1, &histSize, ranges, true, false );
|
||||
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
|
||||
//! [Get the Histogram and normalize it]
|
||||
|
||||
//! [Get Backprojection]
|
||||
Mat backproj;
|
||||
calcBackProject( &hue, 1, 0, hist, backproj, ranges, 1, true );
|
||||
//! [Get Backprojection]
|
||||
|
||||
//! [Draw the backproj]
|
||||
imshow( "BackProj", backproj );
|
||||
//! [Draw the backproj]
|
||||
|
||||
//! [Draw the histogram]
|
||||
int w = 400, h = 400;
|
||||
int bin_w = cvRound( (double) w / histSize );
|
||||
Mat histImg = Mat::zeros( h, w, CV_8UC3 );
|
||||
|
||||
for (int i = 0; i < bins; i++)
|
||||
{
|
||||
rectangle( histImg, Point( i*bin_w, h ), Point( (i+1)*bin_w, h - cvRound( hist.at<float>(i)*h/255.0 ) ),
|
||||
Scalar( 0, 0, 255 ), FILLED );
|
||||
}
|
||||
|
||||
imshow( "Histogram", histImg );
|
||||
//! [Draw the histogram]
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file BackProject_Demo2.cpp
|
||||
* @brief Sample code for backproject function usage ( a bit more elaborated )
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/// Global Variables
|
||||
Mat src, hsv, mask;
|
||||
|
||||
int low = 20, up = 20;
|
||||
const char* window_image = "Source image";
|
||||
|
||||
/// Function Headers
|
||||
void Hist_and_Backproj( );
|
||||
void pickPoint (int event, int x, int y, int, void* );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int, char** argv )
|
||||
{
|
||||
/// Read the image
|
||||
src = imread( argv[1] );
|
||||
|
||||
/// Transform it to HSV
|
||||
cvtColor( src, hsv, COLOR_BGR2HSV );
|
||||
|
||||
/// Show the image
|
||||
namedWindow( window_image );
|
||||
imshow( window_image, src );
|
||||
|
||||
/// Set Trackbars for floodfill thresholds
|
||||
createTrackbar( "Low thresh", window_image, &low, 255, 0 );
|
||||
createTrackbar( "High thresh", window_image, &up, 255, 0 );
|
||||
/// Set a Mouse Callback
|
||||
setMouseCallback( window_image, pickPoint, 0 );
|
||||
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function pickPoint
|
||||
*/
|
||||
void pickPoint (int event, int x, int y, int, void* )
|
||||
{
|
||||
if( event != EVENT_LBUTTONDOWN )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Fill and get the mask
|
||||
Point seed = Point( x, y );
|
||||
|
||||
int newMaskVal = 255;
|
||||
Scalar newVal = Scalar( 120, 120, 120 );
|
||||
|
||||
int connectivity = 8;
|
||||
int flags = connectivity + (newMaskVal << 8 ) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY;
|
||||
|
||||
Mat mask2 = Mat::zeros( src.rows + 2, src.cols + 2, CV_8U );
|
||||
floodFill( src, mask2, seed, newVal, 0, Scalar( low, low, low ), Scalar( up, up, up), flags );
|
||||
mask = mask2( Range( 1, mask2.rows - 1 ), Range( 1, mask2.cols - 1 ) );
|
||||
|
||||
imshow( "Mask", mask );
|
||||
|
||||
Hist_and_Backproj( );
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Hist_and_Backproj
|
||||
*/
|
||||
void Hist_and_Backproj( )
|
||||
{
|
||||
Mat hist;
|
||||
int h_bins = 30; int s_bins = 32;
|
||||
int histSize[] = { h_bins, s_bins };
|
||||
|
||||
float h_range[] = { 0, 180 };
|
||||
float s_range[] = { 0, 256 };
|
||||
const float* ranges[] = { h_range, s_range };
|
||||
|
||||
int channels[] = { 0, 1 };
|
||||
|
||||
/// Get the Histogram and normalize it
|
||||
calcHist( &hsv, 1, channels, mask, hist, 2, histSize, ranges, true, false );
|
||||
|
||||
normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
|
||||
|
||||
/// Get Backprojection
|
||||
Mat backproj;
|
||||
calcBackProject( &hsv, 1, channels, hist, backproj, ranges, 1, true );
|
||||
|
||||
/// Draw the backproj
|
||||
imshow( "BackProj", backproj );
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @function calcHist_Demo.cpp
|
||||
* @brief Demo code to use the function calcHist
|
||||
* @author
|
||||
*/
|
||||
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
//! [Load image]
|
||||
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
|
||||
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
|
||||
if( src.empty() )
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//! [Load image]
|
||||
|
||||
//! [Separate the image in 3 places ( B, G and R )]
|
||||
vector<Mat> bgr_planes;
|
||||
split( src, bgr_planes );
|
||||
//! [Separate the image in 3 places ( B, G and R )]
|
||||
|
||||
//! [Establish the number of bins]
|
||||
int histSize = 256;
|
||||
//! [Establish the number of bins]
|
||||
|
||||
//! [Set the ranges ( for B,G,R) )]
|
||||
float range[] = { 0, 256 }; //the upper boundary is exclusive
|
||||
const float* histRange[] = { range };
|
||||
//! [Set the ranges ( for B,G,R) )]
|
||||
|
||||
//! [Set histogram param]
|
||||
bool uniform = true, accumulate = false;
|
||||
//! [Set histogram param]
|
||||
|
||||
//! [Compute the histograms]
|
||||
Mat b_hist, g_hist, r_hist;
|
||||
calcHist( &bgr_planes[0], 1, 0, Mat(), b_hist, 1, &histSize, histRange, uniform, accumulate );
|
||||
calcHist( &bgr_planes[1], 1, 0, Mat(), g_hist, 1, &histSize, histRange, uniform, accumulate );
|
||||
calcHist( &bgr_planes[2], 1, 0, Mat(), r_hist, 1, &histSize, histRange, uniform, accumulate );
|
||||
//! [Compute the histograms]
|
||||
|
||||
//! [Draw the histograms for B, G and R]
|
||||
int hist_w = 512, hist_h = 400;
|
||||
int bin_w = cvRound( (double) hist_w/histSize );
|
||||
|
||||
Mat histImage( hist_h, hist_w, CV_8UC3, Scalar( 0,0,0) );
|
||||
//! [Draw the histograms for B, G and R]
|
||||
|
||||
//! [Normalize the result to ( 0, histImage.rows )]
|
||||
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
|
||||
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
|
||||
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat() );
|
||||
//! [Normalize the result to ( 0, histImage.rows )]
|
||||
|
||||
//! [Draw for each channel]
|
||||
for( int i = 1; i < histSize; i++ )
|
||||
{
|
||||
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),
|
||||
Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
|
||||
Scalar( 255, 0, 0), 2, 8, 0 );
|
||||
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),
|
||||
Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
|
||||
Scalar( 0, 255, 0), 2, 8, 0 );
|
||||
line( histImage, Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),
|
||||
Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
|
||||
Scalar( 0, 0, 255), 2, 8, 0 );
|
||||
}
|
||||
//! [Draw for each channel]
|
||||
|
||||
//! [Display]
|
||||
imshow("Source image", src );
|
||||
imshow("calcHist Demo", histImage );
|
||||
waitKey();
|
||||
//! [Display]
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file compareHist_Demo.cpp
|
||||
* @brief Sample code to use the function compareHist
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
const char* keys =
|
||||
"{ help h| | Print help message. }"
|
||||
"{ @input1 |Histogram_Comparison_Source_0.jpg | Path to input image 1. }"
|
||||
"{ @input2 |Histogram_Comparison_Source_1.jpg | Path to input image 2. }"
|
||||
"{ @input3 |Histogram_Comparison_Source_2.jpg | Path to input image 3. }";
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//! [Load three images with different environment settings]
|
||||
CommandLineParser parser( argc, argv, keys );
|
||||
samples::addSamplesDataSearchSubDirectory( "doc/tutorials/imgproc/histograms/histogram_comparison/images" );
|
||||
Mat src_base = imread(samples::findFile( parser.get<String>( "@input1" ) ) );
|
||||
Mat src_test1 = imread(samples::findFile( parser.get<String>( "@input2" ) ) );
|
||||
Mat src_test2 = imread(samples::findFile( parser.get<String>( "@input3" ) ) );
|
||||
if( src_base.empty() || src_test1.empty() || src_test2.empty() )
|
||||
{
|
||||
cout << "Could not open or find the images!\n" << endl;
|
||||
parser.printMessage();
|
||||
return -1;
|
||||
}
|
||||
//! [Load three images with different environment settings]
|
||||
|
||||
//! [Convert to HSV]
|
||||
Mat hsv_base, hsv_test1, hsv_test2;
|
||||
cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
|
||||
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
|
||||
cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV );
|
||||
//! [Convert to HSV]
|
||||
|
||||
//! [Convert to HSV half]
|
||||
Mat hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows ), Range( 0, hsv_base.cols ) );
|
||||
//! [Convert to HSV half]
|
||||
|
||||
//! [Using 50 bins for hue and 60 for saturation]
|
||||
int h_bins = 50, s_bins = 60;
|
||||
int histSize[] = { h_bins, s_bins };
|
||||
|
||||
// hue varies from 0 to 179, saturation from 0 to 255
|
||||
float h_ranges[] = { 0, 180 };
|
||||
float s_ranges[] = { 0, 256 };
|
||||
|
||||
const float* ranges[] = { h_ranges, s_ranges };
|
||||
|
||||
// Use the 0-th and 1-st channels
|
||||
int channels[] = { 0, 1 };
|
||||
//! [Using 50 bins for hue and 60 for saturation]
|
||||
|
||||
//! [Calculate the histograms for the HSV images]
|
||||
Mat hist_base, hist_half_down, hist_test1, hist_test2;
|
||||
|
||||
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
|
||||
normalize( hist_base, hist_base, 1, 0, NORM_L1, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false );
|
||||
normalize( hist_half_down, hist_half_down, 1, 0, NORM_L1, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
|
||||
normalize( hist_test1, hist_test1, 1, 0, NORM_L1, -1, Mat() );
|
||||
|
||||
calcHist( &hsv_test2, 1, channels, Mat(), hist_test2, 2, histSize, ranges, true, false );
|
||||
normalize( hist_test2, hist_test2, 1, 0, NORM_L1, -1, Mat() );
|
||||
//! [Calculate the histograms for the HSV images]
|
||||
|
||||
//! [Apply the histogram comparison methods]
|
||||
for( int compare_method = 0; compare_method < 6; compare_method++ )
|
||||
{
|
||||
double base_base = compareHist( hist_base, hist_base, compare_method );
|
||||
double base_half = compareHist( hist_base, hist_half_down, compare_method );
|
||||
double base_test1 = compareHist( hist_base, hist_test1, compare_method );
|
||||
double base_test2 = compareHist( hist_base, hist_test2, compare_method );
|
||||
|
||||
cout << "Method " << compare_method << " Perfect, Base-Half, Base-Test(1), Base-Test(2) : "
|
||||
<< base_base << " / " << base_half << " / " << base_test1 << " / " << base_test2 << endl;
|
||||
}
|
||||
//! [Apply the histogram comparison methods]
|
||||
|
||||
cout << "Done \n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @file BasicLinearTransforms.cpp
|
||||
* @brief Simple program to change contrast and brightness
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using namespace cv;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
* @brief Main function
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Read image given by user
|
||||
//! [basic-linear-transform-load]
|
||||
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
|
||||
Mat image = imread( samples::findFile( parser.get<String>( "@input" ) ) );
|
||||
if( image.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
//! [basic-linear-transform-load]
|
||||
|
||||
//! [basic-linear-transform-output]
|
||||
Mat new_image = Mat::zeros( image.size(), image.type() );
|
||||
//! [basic-linear-transform-output]
|
||||
|
||||
//! [basic-linear-transform-parameters]
|
||||
double alpha = 1.0; /*< Simple contrast control */
|
||||
int beta = 0; /*< Simple brightness control */
|
||||
|
||||
/// Initialize values
|
||||
cout << " Basic Linear Transforms " << endl;
|
||||
cout << "-------------------------" << endl;
|
||||
cout << "* Enter the alpha value [1.0-3.0]: "; cin >> alpha;
|
||||
cout << "* Enter the beta value [0-100]: "; cin >> beta;
|
||||
//! [basic-linear-transform-parameters]
|
||||
|
||||
/// Do the operation new_image(i,j) = alpha*image(i,j) + beta
|
||||
/// Instead of these 'for' loops we could have used simply:
|
||||
/// image.convertTo(new_image, -1, alpha, beta);
|
||||
/// but we wanted to show you how to access the pixels :)
|
||||
//! [basic-linear-transform-operation]
|
||||
for( int y = 0; y < image.rows; y++ ) {
|
||||
for( int x = 0; x < image.cols; x++ ) {
|
||||
for( int c = 0; c < image.channels(); c++ ) {
|
||||
new_image.at<Vec3b>(y,x)[c] =
|
||||
saturate_cast<uchar>( alpha*image.at<Vec3b>(y,x)[c] + beta );
|
||||
}
|
||||
}
|
||||
}
|
||||
//! [basic-linear-transform-operation]
|
||||
|
||||
//! [basic-linear-transform-display]
|
||||
/// Show stuff
|
||||
imshow("Original Image", image);
|
||||
imshow("New Image", new_image);
|
||||
|
||||
/// Wait until the user press a key
|
||||
waitKey();
|
||||
//! [basic-linear-transform-display]
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
int main(){
|
||||
Mat input_image = Mat_<uchar>({8, 8}, {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 255, 255, 255, 0, 0, 0, 255,
|
||||
0, 255, 255, 255, 0, 0, 0, 0,
|
||||
0, 255, 255, 255, 0, 255, 0, 0,
|
||||
0, 0, 255, 0, 0, 0, 0, 0,
|
||||
0, 0, 255, 0, 0, 255, 255, 0,
|
||||
0, 255, 0, 255, 0, 0, 255, 0,
|
||||
0, 255, 255, 255, 0, 0, 0, 0
|
||||
});
|
||||
|
||||
Mat kernel = Mat_<int>({3, 3}, {
|
||||
0, 1, 0,
|
||||
1, -1, 1,
|
||||
0, 1, 0
|
||||
});
|
||||
|
||||
Mat output_image;
|
||||
morphologyEx(input_image, output_image, MORPH_HITMISS, kernel);
|
||||
|
||||
const int rate = 50;
|
||||
kernel = (kernel + 1) * 127;
|
||||
kernel.convertTo(kernel, CV_8U);
|
||||
|
||||
resize(kernel, kernel, Size(), rate, rate, INTER_NEAREST);
|
||||
imshow("kernel", kernel);
|
||||
moveWindow("kernel", 0, 0);
|
||||
|
||||
resize(input_image, input_image, Size(), rate, rate, INTER_NEAREST);
|
||||
imshow("Original", input_image);
|
||||
moveWindow("Original", 0, 200);
|
||||
|
||||
resize(output_image, output_image, Size(), rate, rate, INTER_NEAREST);
|
||||
imshow("Hit or Miss", output_image);
|
||||
moveWindow("Hit or Miss", 500, 200);
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @file Morphology_1.cpp
|
||||
* @brief Erosion and Dilation sample code
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/// Global variables
|
||||
Mat src, erosion_dst, dilation_dst;
|
||||
|
||||
int erosion_elem = 0;
|
||||
int erosion_size = 0;
|
||||
int dilation_elem = 0;
|
||||
int dilation_size = 0;
|
||||
int const max_elem = 3;
|
||||
int const max_kernel_size = 21;
|
||||
|
||||
/** Function Headers */
|
||||
void Erosion( int, void* );
|
||||
void Dilation( int, void* );
|
||||
|
||||
//![main]
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// Load an image
|
||||
CommandLineParser parser( argc, argv, "{@input | LinuxLogo.jpg | input image}" );
|
||||
src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
|
||||
if( src.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Create windows
|
||||
namedWindow( "Erosion Demo", WINDOW_AUTOSIZE );
|
||||
namedWindow( "Dilation Demo", WINDOW_AUTOSIZE );
|
||||
moveWindow( "Dilation Demo", src.cols, 0 );
|
||||
|
||||
/// Create Erosion Trackbar
|
||||
createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse \n 3: Diamond", "Erosion Demo",
|
||||
&erosion_elem, max_elem,
|
||||
Erosion );
|
||||
|
||||
createTrackbar( "Kernel size:\n 2n +1", "Erosion Demo",
|
||||
&erosion_size, max_kernel_size,
|
||||
Erosion );
|
||||
|
||||
/// Create Dilation Trackbar
|
||||
createTrackbar( "Element:\n 0: Rect \n 1: Cross \n 2: Ellipse \n 3: Diamond", "Dilation Demo",
|
||||
&dilation_elem, max_elem,
|
||||
Dilation );
|
||||
|
||||
createTrackbar( "Kernel size:\n 2n +1", "Dilation Demo",
|
||||
&dilation_size, max_kernel_size,
|
||||
Dilation );
|
||||
|
||||
/// Default start
|
||||
Erosion( 0, 0 );
|
||||
Dilation( 0, 0 );
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
//![main]
|
||||
|
||||
//![erosion]
|
||||
/**
|
||||
* @function Erosion
|
||||
*/
|
||||
void Erosion( int, void* )
|
||||
{
|
||||
int erosion_type = 0;
|
||||
if( erosion_elem == 0 ){ erosion_type = MORPH_RECT; }
|
||||
else if( erosion_elem == 1 ){ erosion_type = MORPH_CROSS; }
|
||||
else if( erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
|
||||
else if( erosion_elem == 3) { erosion_type = MORPH_DIAMOND; }
|
||||
|
||||
//![kernel]
|
||||
Mat element = getStructuringElement( erosion_type,
|
||||
Size( 2*erosion_size + 1, 2*erosion_size+1 ),
|
||||
Point( erosion_size, erosion_size ) );
|
||||
//![kernel]
|
||||
|
||||
/// Apply the erosion operation
|
||||
erode( src, erosion_dst, element );
|
||||
imshow( "Erosion Demo", erosion_dst );
|
||||
}
|
||||
//![erosion]
|
||||
|
||||
//![dilation]
|
||||
/**
|
||||
* @function Dilation
|
||||
*/
|
||||
void Dilation( int, void* )
|
||||
{
|
||||
int dilation_type = 0;
|
||||
if( dilation_elem == 0 ){ dilation_type = MORPH_RECT; }
|
||||
else if( dilation_elem == 1 ){ dilation_type = MORPH_CROSS; }
|
||||
else if( dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
|
||||
else if( dilation_elem == 3) { dilation_type = MORPH_DIAMOND; }
|
||||
|
||||
Mat element = getStructuringElement( dilation_type,
|
||||
Size( 2*dilation_size + 1, 2*dilation_size+1 ),
|
||||
Point( dilation_size, dilation_size ) );
|
||||
|
||||
/// Apply the dilation operation
|
||||
dilate( src, dilation_dst, element );
|
||||
imshow( "Dilation Demo", dilation_dst );
|
||||
}
|
||||
//![dilation]
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @file Morphology_2.cpp
|
||||
* @brief Advanced morphology Transformations sample code
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/// Global variables
|
||||
Mat src, dst;
|
||||
|
||||
int morph_elem = 0;
|
||||
int morph_size = 0;
|
||||
int morph_operator = 0;
|
||||
int const max_operator = 4;
|
||||
int const max_elem = 3;
|
||||
int const max_kernel_size = 21;
|
||||
|
||||
const char* window_name = "Morphology Transformations Demo";
|
||||
|
||||
|
||||
/** Function Headers */
|
||||
void Morphology_Operations( int, void* );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//![load]
|
||||
CommandLineParser parser( argc, argv, "{@input | baboon.jpg | input image}" );
|
||||
src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR );
|
||||
if (src.empty())
|
||||
{
|
||||
std::cout << "Could not open or find the image!\n" << std::endl;
|
||||
std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//![load]
|
||||
|
||||
//![window]
|
||||
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create window
|
||||
//![window]
|
||||
|
||||
//![create_trackbar1]
|
||||
/// Create Trackbar to select Morphology operation
|
||||
createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations );
|
||||
//![create_trackbar1]
|
||||
|
||||
//![create_trackbar2]
|
||||
/// Create Trackbar to select kernel type
|
||||
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse - 3: Diamond", window_name,
|
||||
&morph_elem, max_elem,
|
||||
Morphology_Operations );
|
||||
//![create_trackbar2]
|
||||
|
||||
//![create_trackbar3]
|
||||
/// Create Trackbar to choose kernel size
|
||||
createTrackbar( "Kernel size:\n 2n +1", window_name,
|
||||
&morph_size, max_kernel_size,
|
||||
Morphology_Operations );
|
||||
//![create_trackbar3]
|
||||
|
||||
/// Default start
|
||||
Morphology_Operations( 0, 0 );
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//![morphology_operations]
|
||||
/**
|
||||
* @function Morphology_Operations
|
||||
*/
|
||||
void Morphology_Operations( int, void* )
|
||||
{
|
||||
// Since MORPH_X : 2,3,4,5 and 6
|
||||
//![operation]
|
||||
int operation = morph_operator + 2;
|
||||
//![operation]
|
||||
|
||||
Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) );
|
||||
|
||||
/// Apply the specified morphology operation
|
||||
morphologyEx( src, dst, operation, element );
|
||||
imshow( window_name, dst );
|
||||
}
|
||||
//![morphology_operations]
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* @file Pyramids.cpp
|
||||
* @brief Sample code of image pyramids (pyrDown and pyrUp)
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "iostream"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
const char* window_name = "Pyramids Demo";
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
/// General instructions
|
||||
cout << "\n Zoom In-Out demo \n "
|
||||
"------------------ \n"
|
||||
" * [i] -> Zoom in \n"
|
||||
" * [o] -> Zoom out \n"
|
||||
" * [ESC] -> Close program \n" << endl;
|
||||
|
||||
//![load]
|
||||
const char* filename = argc >=2 ? argv[1] : "chicky_512.png";
|
||||
|
||||
// Loads an image
|
||||
Mat src = imread( samples::findFile( filename ) );
|
||||
|
||||
// Check if image is loaded fine
|
||||
if(src.empty()){
|
||||
printf(" Error opening image\n");
|
||||
printf(" Program Arguments: [image_name -- default chicky_512.png] \n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//![load]
|
||||
|
||||
//![loop]
|
||||
for(;;)
|
||||
{
|
||||
//![show_image]
|
||||
imshow( window_name, src );
|
||||
//![show_image]
|
||||
char c = (char)waitKey(0);
|
||||
|
||||
if( c == 27 )
|
||||
{ break; }
|
||||
//![pyrup]
|
||||
else if( c == 'i' )
|
||||
{ pyrUp( src, src, Size( src.cols*2, src.rows*2 ) );
|
||||
printf( "** Zoom In: Image x 2 \n" );
|
||||
}
|
||||
//![pyrup]
|
||||
//![pyrdown]
|
||||
else if( c == 'o' )
|
||||
{ pyrDown( src, src, Size( src.cols/2, src.rows/2 ) );
|
||||
printf( "** Zoom Out: Image / 2 \n" );
|
||||
}
|
||||
//![pyrdown]
|
||||
}
|
||||
//![loop]
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* file Smoothing.cpp
|
||||
* brief Sample code for simple filters
|
||||
* author OpenCV team
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
/// Global Variables
|
||||
int DELAY_CAPTION = 1500;
|
||||
int DELAY_BLUR = 100;
|
||||
int MAX_KERNEL_LENGTH = 31;
|
||||
|
||||
Mat src; Mat dst;
|
||||
char window_name[] = "Smoothing Demo";
|
||||
|
||||
/// Function headers
|
||||
int display_caption( const char* caption );
|
||||
int display_dst( int delay );
|
||||
|
||||
|
||||
/**
|
||||
* function main
|
||||
*/
|
||||
int main( int argc, char ** argv )
|
||||
{
|
||||
namedWindow( window_name, WINDOW_AUTOSIZE );
|
||||
|
||||
/// Load the source image
|
||||
const char* filename = argc >=2 ? argv[1] : "lena.jpg";
|
||||
|
||||
src = imread( samples::findFile( filename ), IMREAD_COLOR );
|
||||
if (src.empty())
|
||||
{
|
||||
printf(" Error opening image\n");
|
||||
printf(" Usage:\n %s [image_name-- default lena.jpg] \n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if( display_caption( "Original Image" ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
dst = src.clone();
|
||||
if( display_dst( DELAY_CAPTION ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Applying Homogeneous blur
|
||||
if( display_caption( "Homogeneous Blur" ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//![blur]
|
||||
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
|
||||
{
|
||||
blur( src, dst, Size( i, i ), Point(-1,-1) );
|
||||
if( display_dst( DELAY_BLUR ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//![blur]
|
||||
|
||||
/// Applying Gaussian blur
|
||||
if( display_caption( "Gaussian Blur" ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//![gaussianblur]
|
||||
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
|
||||
{
|
||||
GaussianBlur( src, dst, Size( i, i ), 0, 0 );
|
||||
if( display_dst( DELAY_BLUR ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//![gaussianblur]
|
||||
|
||||
/// Applying Median blur
|
||||
if( display_caption( "Median Blur" ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//![medianblur]
|
||||
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
|
||||
{
|
||||
medianBlur ( src, dst, i );
|
||||
if( display_dst( DELAY_BLUR ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//![medianblur]
|
||||
|
||||
/// Applying Bilateral Filter
|
||||
if( display_caption( "Bilateral Blur" ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//![bilateralfilter]
|
||||
for ( int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2 )
|
||||
{
|
||||
bilateralFilter ( src, dst, i, i*2, i/2 );
|
||||
if( display_dst( DELAY_BLUR ) != 0 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//![bilateralfilter]
|
||||
|
||||
/// Done
|
||||
display_caption( "Done!" );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function display_caption
|
||||
*/
|
||||
int display_caption( const char* caption )
|
||||
{
|
||||
dst = Mat::zeros( src.size(), src.type() );
|
||||
putText( dst, caption,
|
||||
Point( src.cols/4, src.rows/2),
|
||||
FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255) );
|
||||
|
||||
return display_dst(DELAY_CAPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* @function display_dst
|
||||
*/
|
||||
int display_dst( int delay )
|
||||
{
|
||||
imshow( window_name, dst );
|
||||
int c = waitKey ( delay );
|
||||
if( c >= 0 ) { return -1; }
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @file Threshold.cpp
|
||||
* @brief Sample code that shows how to use the diverse threshold options offered by OpenCV
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using std::cout;
|
||||
|
||||
/// Global variables
|
||||
|
||||
int threshold_value = 0;
|
||||
int threshold_type = 3;
|
||||
int const max_value = 255;
|
||||
int const max_type = 4;
|
||||
int const max_binary_value = 255;
|
||||
|
||||
Mat src, src_gray, dst;
|
||||
const char* window_name = "Threshold Demo";
|
||||
|
||||
const char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
|
||||
const char* trackbar_value = "Value";
|
||||
|
||||
//![Threshold_Demo]
|
||||
/**
|
||||
* @function Threshold_Demo
|
||||
*/
|
||||
static void Threshold_Demo( int, void* )
|
||||
{
|
||||
/* 0: Binary
|
||||
1: Binary Inverted
|
||||
2: Threshold Truncated
|
||||
3: Threshold to Zero
|
||||
4: Threshold to Zero Inverted
|
||||
*/
|
||||
threshold( src_gray, dst, threshold_value, max_binary_value, threshold_type );
|
||||
imshow( window_name, dst );
|
||||
}
|
||||
//![Threshold_Demo]
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//! [load]
|
||||
String imageName("stuff.jpg"); // by default
|
||||
if (argc > 1)
|
||||
{
|
||||
imageName = argv[1];
|
||||
}
|
||||
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
|
||||
|
||||
if (src.empty())
|
||||
{
|
||||
cout << "Cannot read the image: " << imageName << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to Gray
|
||||
//! [load]
|
||||
|
||||
//! [window]
|
||||
namedWindow( window_name, WINDOW_AUTOSIZE ); // Create a window to display results
|
||||
//! [window]
|
||||
|
||||
//! [trackbar]
|
||||
createTrackbar( trackbar_type,
|
||||
window_name, &threshold_type,
|
||||
max_type, Threshold_Demo ); // Create a Trackbar to choose type of Threshold
|
||||
|
||||
createTrackbar( trackbar_value,
|
||||
window_name, &threshold_value,
|
||||
max_value, Threshold_Demo ); // Create a Trackbar to choose Threshold value
|
||||
//! [trackbar]
|
||||
|
||||
Threshold_Demo( 0, 0 ); // Call the function to initialize
|
||||
|
||||
/// Wait until the user finishes the program
|
||||
waitKey();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/videoio.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/** Global Variables */
|
||||
const int max_value_H = 360/2;
|
||||
const int max_value = 255;
|
||||
const String window_capture_name = "Video Capture";
|
||||
const String window_detection_name = "Object Detection";
|
||||
int low_H = 0, low_S = 0, low_V = 0;
|
||||
int high_H = max_value_H, high_S = max_value, high_V = max_value;
|
||||
|
||||
//! [low]
|
||||
static void on_low_H_thresh_trackbar(int, void *)
|
||||
{
|
||||
low_H = min(high_H-1, low_H);
|
||||
setTrackbarPos("Low H", window_detection_name, low_H);
|
||||
}
|
||||
//! [low]
|
||||
|
||||
//! [high]
|
||||
static void on_high_H_thresh_trackbar(int, void *)
|
||||
{
|
||||
high_H = max(high_H, low_H+1);
|
||||
setTrackbarPos("High H", window_detection_name, high_H);
|
||||
}
|
||||
|
||||
//! [high]
|
||||
static void on_low_S_thresh_trackbar(int, void *)
|
||||
{
|
||||
low_S = min(high_S-1, low_S);
|
||||
setTrackbarPos("Low S", window_detection_name, low_S);
|
||||
}
|
||||
|
||||
static void on_high_S_thresh_trackbar(int, void *)
|
||||
{
|
||||
high_S = max(high_S, low_S+1);
|
||||
setTrackbarPos("High S", window_detection_name, high_S);
|
||||
}
|
||||
|
||||
static void on_low_V_thresh_trackbar(int, void *)
|
||||
{
|
||||
low_V = min(high_V-1, low_V);
|
||||
setTrackbarPos("Low V", window_detection_name, low_V);
|
||||
}
|
||||
|
||||
static void on_high_V_thresh_trackbar(int, void *)
|
||||
{
|
||||
high_V = max(high_V, low_V+1);
|
||||
setTrackbarPos("High V", window_detection_name, high_V);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
//! [cap]
|
||||
VideoCapture cap(argc > 1 ? atoi(argv[1]) : 0);
|
||||
//! [cap]
|
||||
|
||||
//! [window]
|
||||
namedWindow(window_capture_name);
|
||||
namedWindow(window_detection_name);
|
||||
//! [window]
|
||||
|
||||
//! [trackbar]
|
||||
// Trackbars to set thresholds for HSV values
|
||||
createTrackbar("Low H", window_detection_name, &low_H, max_value_H, on_low_H_thresh_trackbar);
|
||||
createTrackbar("High H", window_detection_name, &high_H, max_value_H, on_high_H_thresh_trackbar);
|
||||
createTrackbar("Low S", window_detection_name, &low_S, max_value, on_low_S_thresh_trackbar);
|
||||
createTrackbar("High S", window_detection_name, &high_S, max_value, on_high_S_thresh_trackbar);
|
||||
createTrackbar("Low V", window_detection_name, &low_V, max_value, on_low_V_thresh_trackbar);
|
||||
createTrackbar("High V", window_detection_name, &high_V, max_value, on_high_V_thresh_trackbar);
|
||||
//! [trackbar]
|
||||
|
||||
Mat frame, frame_HSV, frame_threshold;
|
||||
while (true) {
|
||||
//! [while]
|
||||
cap >> frame;
|
||||
if(frame.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert from BGR to HSV colorspace
|
||||
cvtColor(frame, frame_HSV, COLOR_BGR2HSV);
|
||||
// Detect the object based on HSV Range Values
|
||||
inRange(frame_HSV, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), frame_threshold);
|
||||
//! [while]
|
||||
|
||||
//! [show]
|
||||
// Show the frames
|
||||
imshow(window_capture_name, frame);
|
||||
imshow(window_detection_name, frame_threshold);
|
||||
//! [show]
|
||||
|
||||
char key = (char) waitKey(30);
|
||||
if (key == 'q' || key == 27)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* @brief You will learn how to segment an anisotropic image with a single local orientation by a gradient structure tensor (GST)
|
||||
* @author Karpushin Vladislav, karpushin@ngs.ru, https://github.com/VladKarpushin
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
//! [calcGST_proto]
|
||||
void calcGST(const Mat& inputImg, Mat& imgCoherencyOut, Mat& imgOrientationOut, int w);
|
||||
//! [calcGST_proto]
|
||||
|
||||
int main()
|
||||
{
|
||||
int W = 52; // window size is WxW
|
||||
double C_Thr = 0.43; // threshold for coherency
|
||||
int LowThr = 35; // threshold1 for orientation, it ranges from 0 to 180
|
||||
int HighThr = 57; // threshold2 for orientation, it ranges from 0 to 180
|
||||
|
||||
samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/anisotropic_image_segmentation/images");
|
||||
Mat imgIn = imread(samples::findFile("gst_input.jpg"), IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
cout << "ERROR : Image cannot be loaded..!!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//! [main_extra]
|
||||
//! [main]
|
||||
Mat imgCoherency, imgOrientation;
|
||||
calcGST(imgIn, imgCoherency, imgOrientation, W);
|
||||
|
||||
//! [thresholding]
|
||||
Mat imgCoherencyBin;
|
||||
imgCoherencyBin = imgCoherency > C_Thr;
|
||||
Mat imgOrientationBin;
|
||||
inRange(imgOrientation, Scalar(LowThr), Scalar(HighThr), imgOrientationBin);
|
||||
//! [thresholding]
|
||||
|
||||
//! [combining]
|
||||
Mat imgBin;
|
||||
imgBin = imgCoherencyBin & imgOrientationBin;
|
||||
//! [combining]
|
||||
//! [main]
|
||||
|
||||
normalize(imgCoherency, imgCoherency, 0, 255, NORM_MINMAX, CV_8U);
|
||||
normalize(imgOrientation, imgOrientation, 0, 255, NORM_MINMAX, CV_8U);
|
||||
|
||||
imshow("Original", imgIn);
|
||||
imshow("Result", 0.5 * (imgIn + imgBin));
|
||||
imshow("Coherency", imgCoherency);
|
||||
imshow("Orientation", imgOrientation);
|
||||
imwrite("result.jpg", 0.5*(imgIn + imgBin));
|
||||
imwrite("Coherency.jpg", imgCoherency);
|
||||
imwrite("Orientation.jpg", imgOrientation);
|
||||
waitKey(0);
|
||||
//! [main_extra]
|
||||
return 0;
|
||||
}
|
||||
//! [calcGST]
|
||||
//! [calcJ_header]
|
||||
void calcGST(const Mat& inputImg, Mat& imgCoherencyOut, Mat& imgOrientationOut, int w)
|
||||
{
|
||||
Mat img;
|
||||
inputImg.convertTo(img, CV_32F);
|
||||
|
||||
// GST components calculation (start)
|
||||
// J = (J11 J12; J12 J22) - GST
|
||||
Mat imgDiffX, imgDiffY, imgDiffXY;
|
||||
Sobel(img, imgDiffX, CV_32F, 1, 0, 3);
|
||||
Sobel(img, imgDiffY, CV_32F, 0, 1, 3);
|
||||
multiply(imgDiffX, imgDiffY, imgDiffXY);
|
||||
//! [calcJ_header]
|
||||
|
||||
Mat imgDiffXX, imgDiffYY;
|
||||
multiply(imgDiffX, imgDiffX, imgDiffXX);
|
||||
multiply(imgDiffY, imgDiffY, imgDiffYY);
|
||||
|
||||
Mat J11, J22, J12; // J11, J22 and J12 are GST components
|
||||
boxFilter(imgDiffXX, J11, CV_32F, Size(w, w));
|
||||
boxFilter(imgDiffYY, J22, CV_32F, Size(w, w));
|
||||
boxFilter(imgDiffXY, J12, CV_32F, Size(w, w));
|
||||
// GST components calculation (stop)
|
||||
|
||||
// eigenvalue calculation (start)
|
||||
// lambda1 = 0.5*(J11 + J22 + sqrt((J11-J22)^2 + 4*J12^2))
|
||||
// lambda2 = 0.5*(J11 + J22 - sqrt((J11-J22)^2 + 4*J12^2))
|
||||
Mat tmp1, tmp2, tmp3, tmp4;
|
||||
tmp1 = J11 + J22;
|
||||
tmp2 = J11 - J22;
|
||||
multiply(tmp2, tmp2, tmp2);
|
||||
multiply(J12, J12, tmp3);
|
||||
sqrt(tmp2 + 4.0 * tmp3, tmp4);
|
||||
|
||||
Mat lambda1, lambda2;
|
||||
lambda1 = tmp1 + tmp4;
|
||||
lambda1 = 0.5*lambda1; // biggest eigenvalue
|
||||
lambda2 = tmp1 - tmp4;
|
||||
lambda2 = 0.5*lambda2; // smallest eigenvalue
|
||||
// eigenvalue calculation (stop)
|
||||
|
||||
// Coherency calculation (start)
|
||||
// Coherency = (lambda1 - lambda2)/(lambda1 + lambda2)) - measure of anisotropism
|
||||
// Coherency is anisotropy degree (consistency of local orientation)
|
||||
divide(lambda1 - lambda2, lambda1 + lambda2, imgCoherencyOut);
|
||||
// Coherency calculation (stop)
|
||||
|
||||
// orientation angle calculation (start)
|
||||
// tan(2*Alpha) = 2*J12/(J22 - J11)
|
||||
// Alpha = 0.5 atan2(2*J12/(J22 - J11))
|
||||
phase(J22 - J11, 2.0*J12, imgOrientationOut, true);
|
||||
imgOrientationOut = 0.5*imgOrientationOut;
|
||||
// orientation angle calculation (stop)
|
||||
}
|
||||
//! [calcGST]
|
||||
@@ -0,0 +1,185 @@
|
||||
/**
|
||||
* @file Drawing_1.cpp
|
||||
* @brief Simple geometric drawing
|
||||
* @author OpenCV team
|
||||
*/
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
|
||||
#define w 400
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/// Function headers
|
||||
void MyEllipse( Mat img, double angle );
|
||||
void MyFilledCircle( Mat img, Point center );
|
||||
void MyPolygon( Mat img );
|
||||
void MyLine( Mat img, Point start, Point end );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
* @brief Main function
|
||||
*/
|
||||
int main( void ){
|
||||
|
||||
//![create_images]
|
||||
/// Windows names
|
||||
char atom_window[] = "Drawing 1: Atom";
|
||||
char rook_window[] = "Drawing 2: Rook";
|
||||
|
||||
/// Create black empty images
|
||||
Mat atom_image = Mat::zeros( w, w, CV_8UC3 );
|
||||
Mat rook_image = Mat::zeros( w, w, CV_8UC3 );
|
||||
//![create_images]
|
||||
|
||||
/// 1. Draw a simple atom:
|
||||
/// -----------------------
|
||||
|
||||
//![draw_atom]
|
||||
/// 1.a. Creating ellipses
|
||||
MyEllipse( atom_image, 90 );
|
||||
MyEllipse( atom_image, 0 );
|
||||
MyEllipse( atom_image, 45 );
|
||||
MyEllipse( atom_image, -45 );
|
||||
|
||||
/// 1.b. Creating circles
|
||||
MyFilledCircle( atom_image, Point( w/2, w/2) );
|
||||
//![draw_atom]
|
||||
|
||||
/// 2. Draw a rook
|
||||
/// ------------------
|
||||
|
||||
//![draw_rook]
|
||||
/// 2.a. Create a convex polygon
|
||||
MyPolygon( rook_image );
|
||||
|
||||
//![rectangle]
|
||||
/// 2.b. Creating rectangles
|
||||
rectangle( rook_image,
|
||||
Point( 0, 7*w/8 ),
|
||||
Point( w, w),
|
||||
Scalar( 0, 255, 255 ),
|
||||
FILLED,
|
||||
LINE_8 );
|
||||
//![rectangle]
|
||||
|
||||
/// 2.c. Create a few lines
|
||||
MyLine( rook_image, Point( 0, 15*w/16 ), Point( w, 15*w/16 ) );
|
||||
MyLine( rook_image, Point( w/4, 7*w/8 ), Point( w/4, w ) );
|
||||
MyLine( rook_image, Point( w/2, 7*w/8 ), Point( w/2, w ) );
|
||||
MyLine( rook_image, Point( 3*w/4, 7*w/8 ), Point( 3*w/4, w ) );
|
||||
//![draw_rook]
|
||||
|
||||
/// 3. Display your stuff!
|
||||
imshow( atom_window, atom_image );
|
||||
moveWindow( atom_window, 0, 200 );
|
||||
imshow( rook_window, rook_image );
|
||||
moveWindow( rook_window, w, 200 );
|
||||
|
||||
waitKey( 0 );
|
||||
return(0);
|
||||
}
|
||||
|
||||
/// Function Declaration
|
||||
|
||||
/**
|
||||
* @function MyEllipse
|
||||
* @brief Draw a fixed-size ellipse with different angles
|
||||
*/
|
||||
//![my_ellipse]
|
||||
void MyEllipse( Mat img, double angle )
|
||||
{
|
||||
int thickness = 2;
|
||||
int lineType = 8;
|
||||
|
||||
ellipse( img,
|
||||
Point( w/2, w/2 ),
|
||||
Size( w/4, w/16 ),
|
||||
angle,
|
||||
0,
|
||||
360,
|
||||
Scalar( 255, 0, 0 ),
|
||||
thickness,
|
||||
lineType );
|
||||
}
|
||||
//![my_ellipse]
|
||||
|
||||
/**
|
||||
* @function MyFilledCircle
|
||||
* @brief Draw a fixed-size filled circle
|
||||
*/
|
||||
//![my_filled_circle]
|
||||
void MyFilledCircle( Mat img, Point center )
|
||||
{
|
||||
circle( img,
|
||||
center,
|
||||
w/32,
|
||||
Scalar( 0, 0, 255 ),
|
||||
FILLED,
|
||||
LINE_8 );
|
||||
}
|
||||
//![my_filled_circle]
|
||||
|
||||
/**
|
||||
* @function MyPolygon
|
||||
* @brief Draw a simple concave polygon (rook)
|
||||
*/
|
||||
//![my_polygon]
|
||||
void MyPolygon( Mat img )
|
||||
{
|
||||
int lineType = LINE_8;
|
||||
|
||||
/** Create some points */
|
||||
Point rook_points[1][20];
|
||||
rook_points[0][0] = Point( w/4, 7*w/8 );
|
||||
rook_points[0][1] = Point( 3*w/4, 7*w/8 );
|
||||
rook_points[0][2] = Point( 3*w/4, 13*w/16 );
|
||||
rook_points[0][3] = Point( 11*w/16, 13*w/16 );
|
||||
rook_points[0][4] = Point( 19*w/32, 3*w/8 );
|
||||
rook_points[0][5] = Point( 3*w/4, 3*w/8 );
|
||||
rook_points[0][6] = Point( 3*w/4, w/8 );
|
||||
rook_points[0][7] = Point( 26*w/40, w/8 );
|
||||
rook_points[0][8] = Point( 26*w/40, w/4 );
|
||||
rook_points[0][9] = Point( 22*w/40, w/4 );
|
||||
rook_points[0][10] = Point( 22*w/40, w/8 );
|
||||
rook_points[0][11] = Point( 18*w/40, w/8 );
|
||||
rook_points[0][12] = Point( 18*w/40, w/4 );
|
||||
rook_points[0][13] = Point( 14*w/40, w/4 );
|
||||
rook_points[0][14] = Point( 14*w/40, w/8 );
|
||||
rook_points[0][15] = Point( w/4, w/8 );
|
||||
rook_points[0][16] = Point( w/4, 3*w/8 );
|
||||
rook_points[0][17] = Point( 13*w/32, 3*w/8 );
|
||||
rook_points[0][18] = Point( 5*w/16, 13*w/16 );
|
||||
rook_points[0][19] = Point( w/4, 13*w/16 );
|
||||
|
||||
const Point* ppt[1] = { rook_points[0] };
|
||||
int npt[] = { 20 };
|
||||
|
||||
fillPoly( img,
|
||||
ppt,
|
||||
npt,
|
||||
1,
|
||||
Scalar( 255, 255, 255 ),
|
||||
lineType );
|
||||
}
|
||||
//![my_polygon]
|
||||
|
||||
/**
|
||||
* @function MyLine
|
||||
* @brief Draw a simple line
|
||||
*/
|
||||
//![my_line]
|
||||
void MyLine( Mat img, Point start, Point end )
|
||||
{
|
||||
int thickness = 2;
|
||||
int lineType = LINE_8;
|
||||
|
||||
line( img,
|
||||
start,
|
||||
end,
|
||||
Scalar( 0, 0, 0 ),
|
||||
thickness,
|
||||
lineType );
|
||||
}
|
||||
//![my_line]
|
||||
@@ -0,0 +1,326 @@
|
||||
/**
|
||||
* @file Drawing_2.cpp
|
||||
* @brief Simple sample code
|
||||
*/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/// Global Variables
|
||||
const int NUMBER = 100;
|
||||
const int DELAY = 5;
|
||||
|
||||
const int window_width = 900;
|
||||
const int window_height = 600;
|
||||
int x_1 = -window_width/2;
|
||||
int x_2 = window_width*3/2;
|
||||
int y_1 = -window_width/2;
|
||||
int y_2 = window_width*3/2;
|
||||
|
||||
/// Function headers
|
||||
static Scalar randomColor( RNG& rng );
|
||||
int Drawing_Random_Lines( Mat image, char* window_name, RNG rng );
|
||||
int Drawing_Random_Rectangles( Mat image, char* window_name, RNG rng );
|
||||
int Drawing_Random_Ellipses( Mat image, char* window_name, RNG rng );
|
||||
int Drawing_Random_Polylines( Mat image, char* window_name, RNG rng );
|
||||
int Drawing_Random_Filled_Polygons( Mat image, char* window_name, RNG rng );
|
||||
int Drawing_Random_Circles( Mat image, char* window_name, RNG rng );
|
||||
int Displaying_Random_Text( Mat image, char* window_name, RNG rng );
|
||||
int Displaying_Big_End( Mat image, char* window_name, RNG rng );
|
||||
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( void )
|
||||
{
|
||||
int c;
|
||||
|
||||
/// Start creating a window
|
||||
char window_name[] = "Drawing_2 Tutorial";
|
||||
|
||||
/// Also create a random object (RNG)
|
||||
RNG rng( 0xFFFFFFFF );
|
||||
|
||||
/// Initialize a matrix filled with zeros
|
||||
Mat image = Mat::zeros( window_height, window_width, CV_8UC3 );
|
||||
/// Show it in a window during DELAY ms
|
||||
imshow( window_name, image );
|
||||
waitKey( DELAY );
|
||||
|
||||
/// Now, let's draw some lines
|
||||
c = Drawing_Random_Lines(image, window_name, rng);
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Go on drawing, this time nice rectangles
|
||||
c = Drawing_Random_Rectangles(image, window_name, rng);
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw some ellipses
|
||||
c = Drawing_Random_Ellipses( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Now some polylines
|
||||
c = Drawing_Random_Polylines( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw filled polygons
|
||||
c = Drawing_Random_Filled_Polygons( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Draw circles
|
||||
c = Drawing_Random_Circles( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Display text in random positions
|
||||
c = Displaying_Random_Text( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
/// Displaying the big end!
|
||||
c = Displaying_Big_End( image, window_name, rng );
|
||||
if( c != 0 ) return 0;
|
||||
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Function definitions
|
||||
|
||||
/**
|
||||
* @function randomColor
|
||||
* @brief Produces a random color given a random object
|
||||
*/
|
||||
static Scalar randomColor( RNG& rng )
|
||||
{
|
||||
int icolor = (unsigned) rng;
|
||||
return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function Drawing_Random_Lines
|
||||
*/
|
||||
int Drawing_Random_Lines( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
Point pt1, pt2;
|
||||
|
||||
for( int i = 0; i < NUMBER; i++ )
|
||||
{
|
||||
pt1.x = rng.uniform( x_1, x_2 );
|
||||
pt1.y = rng.uniform( y_1, y_2 );
|
||||
pt2.x = rng.uniform( x_1, x_2 );
|
||||
pt2.y = rng.uniform( y_1, y_2 );
|
||||
|
||||
line( image, pt1, pt2, randomColor(rng), rng.uniform(1, 10), 8 );
|
||||
imshow( window_name, image );
|
||||
if( waitKey( DELAY ) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Drawing_Rectangles
|
||||
*/
|
||||
int Drawing_Random_Rectangles( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
Point pt1, pt2;
|
||||
int lineType = 8;
|
||||
int thickness = rng.uniform( -3, 10 );
|
||||
|
||||
for( int i = 0; i < NUMBER; i++ )
|
||||
{
|
||||
pt1.x = rng.uniform( x_1, x_2 );
|
||||
pt1.y = rng.uniform( y_1, y_2 );
|
||||
pt2.x = rng.uniform( x_1, x_2 );
|
||||
pt2.y = rng.uniform( y_1, y_2 );
|
||||
|
||||
rectangle( image, pt1, pt2, randomColor(rng), MAX( thickness, -1 ), lineType );
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey( DELAY ) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Drawing_Random_Ellipses
|
||||
*/
|
||||
int Drawing_Random_Ellipses( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for ( int i = 0; i < NUMBER; i++ )
|
||||
{
|
||||
Point center;
|
||||
center.x = rng.uniform(x_1, x_2);
|
||||
center.y = rng.uniform(y_1, y_2);
|
||||
|
||||
Size axes;
|
||||
axes.width = rng.uniform(0, 200);
|
||||
axes.height = rng.uniform(0, 200);
|
||||
|
||||
double angle = rng.uniform(0, 180);
|
||||
|
||||
ellipse( image, center, axes, angle, angle - 100, angle + 200,
|
||||
randomColor(rng), rng.uniform(-1,9), lineType );
|
||||
|
||||
imshow( window_name, image );
|
||||
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Drawing_Random_Polylines
|
||||
*/
|
||||
int Drawing_Random_Polylines( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for( int i = 0; i< NUMBER; i++ )
|
||||
{
|
||||
Point pt[2][3];
|
||||
pt[0][0].x = rng.uniform(x_1, x_2);
|
||||
pt[0][0].y = rng.uniform(y_1, y_2);
|
||||
pt[0][1].x = rng.uniform(x_1, x_2);
|
||||
pt[0][1].y = rng.uniform(y_1, y_2);
|
||||
pt[0][2].x = rng.uniform(x_1, x_2);
|
||||
pt[0][2].y = rng.uniform(y_1, y_2);
|
||||
pt[1][0].x = rng.uniform(x_1, x_2);
|
||||
pt[1][0].y = rng.uniform(y_1, y_2);
|
||||
pt[1][1].x = rng.uniform(x_1, x_2);
|
||||
pt[1][1].y = rng.uniform(y_1, y_2);
|
||||
pt[1][2].x = rng.uniform(x_1, x_2);
|
||||
pt[1][2].y = rng.uniform(y_1, y_2);
|
||||
|
||||
const Point* ppt[2] = {pt[0], pt[1]};
|
||||
int npt[] = {3, 3};
|
||||
|
||||
polylines(image, ppt, npt, 2, true, randomColor(rng), rng.uniform(1,10), lineType);
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Drawing_Random_Filled_Polygons
|
||||
*/
|
||||
int Drawing_Random_Filled_Polygons( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for ( int i = 0; i < NUMBER; i++ )
|
||||
{
|
||||
Point pt[2][3];
|
||||
pt[0][0].x = rng.uniform(x_1, x_2);
|
||||
pt[0][0].y = rng.uniform(y_1, y_2);
|
||||
pt[0][1].x = rng.uniform(x_1, x_2);
|
||||
pt[0][1].y = rng.uniform(y_1, y_2);
|
||||
pt[0][2].x = rng.uniform(x_1, x_2);
|
||||
pt[0][2].y = rng.uniform(y_1, y_2);
|
||||
pt[1][0].x = rng.uniform(x_1, x_2);
|
||||
pt[1][0].y = rng.uniform(y_1, y_2);
|
||||
pt[1][1].x = rng.uniform(x_1, x_2);
|
||||
pt[1][1].y = rng.uniform(y_1, y_2);
|
||||
pt[1][2].x = rng.uniform(x_1, x_2);
|
||||
pt[1][2].y = rng.uniform(y_1, y_2);
|
||||
|
||||
const Point* ppt[2] = {pt[0], pt[1]};
|
||||
int npt[] = {3, 3};
|
||||
|
||||
fillPoly( image, ppt, npt, 2, randomColor(rng), lineType );
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Drawing_Random_Circles
|
||||
*/
|
||||
int Drawing_Random_Circles( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for (int i = 0; i < NUMBER; i++)
|
||||
{
|
||||
Point center;
|
||||
center.x = rng.uniform(x_1, x_2);
|
||||
center.y = rng.uniform(y_1, y_2);
|
||||
|
||||
circle( image, center, rng.uniform(0, 300), randomColor(rng),
|
||||
rng.uniform(-1, 9), lineType );
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Displaying_Random_Text
|
||||
*/
|
||||
int Displaying_Random_Text( Mat image, char* window_name, RNG rng )
|
||||
{
|
||||
int lineType = 8;
|
||||
|
||||
for ( int i = 1; i < NUMBER; i++ )
|
||||
{
|
||||
Point org;
|
||||
org.x = rng.uniform(x_1, x_2);
|
||||
org.y = rng.uniform(y_1, y_2);
|
||||
|
||||
putText( image, "Testing text rendering", org, rng.uniform(0,8),
|
||||
rng.uniform(0,100)*0.05+0.1, randomColor(rng), rng.uniform(1, 10), lineType);
|
||||
|
||||
imshow( window_name, image );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Displaying_Big_End
|
||||
*/
|
||||
int Displaying_Big_End( Mat image, char* window_name, RNG )
|
||||
{
|
||||
Size textsize = getTextSize("OpenCV forever!", FONT_HERSHEY_COMPLEX, 3, 5, 0);
|
||||
Point org((window_width - textsize.width)/2, (window_height - textsize.height)/2);
|
||||
int lineType = 8;
|
||||
|
||||
Mat image2;
|
||||
|
||||
for( int i = 0; i < 255; i += 2 )
|
||||
{
|
||||
image2 = image - Scalar::all(i);
|
||||
putText( image2, "OpenCV forever!", org, FONT_HERSHEY_COMPLEX, 3,
|
||||
Scalar(i, i, 255), 5, lineType );
|
||||
|
||||
imshow( window_name, image2 );
|
||||
if( waitKey(DELAY) >= 0 )
|
||||
{ return -1; }
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
#include <iostream>
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
// we're NOT "using namespace std;" here, to avoid collisions between the beta variable and std::beta in c++17
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using namespace cv;
|
||||
|
||||
namespace
|
||||
{
|
||||
/** Global Variables */
|
||||
int alpha = 100;
|
||||
int beta = 100;
|
||||
int gamma_cor = 100;
|
||||
Mat img_original, img_corrected, img_gamma_corrected;
|
||||
|
||||
void basicLinearTransform(const Mat &img, const double alpha_, const int beta_)
|
||||
{
|
||||
Mat res;
|
||||
img.convertTo(res, -1, alpha_, beta_);
|
||||
|
||||
hconcat(img, res, img_corrected);
|
||||
imshow("Brightness and contrast adjustments", img_corrected);
|
||||
}
|
||||
|
||||
void gammaCorrection(const Mat &img, const double gamma_)
|
||||
{
|
||||
CV_Assert(gamma_ >= 0);
|
||||
//! [changing-contrast-brightness-gamma-correction]
|
||||
Mat lookUpTable(1, 256, CV_8U);
|
||||
uchar* p = lookUpTable.ptr();
|
||||
for( int i = 0; i < 256; ++i)
|
||||
p[i] = saturate_cast<uchar>(pow(i / 255.0, gamma_) * 255.0);
|
||||
|
||||
Mat res = img.clone();
|
||||
LUT(img, lookUpTable, res);
|
||||
//! [changing-contrast-brightness-gamma-correction]
|
||||
|
||||
hconcat(img, res, img_gamma_corrected);
|
||||
imshow("Gamma correction", img_gamma_corrected);
|
||||
}
|
||||
|
||||
void on_linear_transform_alpha_trackbar(int, void *)
|
||||
{
|
||||
double alpha_value = alpha / 100.0;
|
||||
int beta_value = beta - 100;
|
||||
basicLinearTransform(img_original, alpha_value, beta_value);
|
||||
}
|
||||
|
||||
void on_linear_transform_beta_trackbar(int, void *)
|
||||
{
|
||||
double alpha_value = alpha / 100.0;
|
||||
int beta_value = beta - 100;
|
||||
basicLinearTransform(img_original, alpha_value, beta_value);
|
||||
}
|
||||
|
||||
void on_gamma_correction_trackbar(int, void *)
|
||||
{
|
||||
double gamma_value = gamma_cor / 100.0;
|
||||
gammaCorrection(img_original, gamma_value);
|
||||
}
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
|
||||
img_original = imread( samples::findFile( parser.get<String>( "@input" ) ) );
|
||||
if( img_original.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
img_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
|
||||
img_gamma_corrected = Mat(img_original.rows, img_original.cols*2, img_original.type());
|
||||
|
||||
hconcat(img_original, img_original, img_corrected);
|
||||
hconcat(img_original, img_original, img_gamma_corrected);
|
||||
|
||||
namedWindow("Brightness and contrast adjustments");
|
||||
namedWindow("Gamma correction");
|
||||
|
||||
createTrackbar("Alpha gain (contrast)", "Brightness and contrast adjustments", &alpha, 500, on_linear_transform_alpha_trackbar);
|
||||
createTrackbar("Beta bias (brightness)", "Brightness and contrast adjustments", &beta, 200, on_linear_transform_beta_trackbar);
|
||||
createTrackbar("Gamma correction", "Gamma correction", &gamma_cor, 200, on_gamma_correction_trackbar);
|
||||
|
||||
on_linear_transform_alpha_trackbar(0, 0);
|
||||
on_gamma_correction_trackbar(0, 0);
|
||||
|
||||
waitKey();
|
||||
|
||||
imwrite("linear_transform_correction.png", img_corrected);
|
||||
imwrite("gamma_correction.png", img_gamma_corrected);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @file Morphology_3(Extract_Lines).cpp
|
||||
* @brief Use morphology transformations for extracting horizontal and vertical lines sample code
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/highgui.hpp>
|
||||
#include <iostream>
|
||||
|
||||
void show_wait_destroy(const char* winname, cv::Mat img);
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
//! [load_image]
|
||||
CommandLineParser parser(argc, argv, "{@input | notes.png | input image}");
|
||||
Mat src = imread( samples::findFile( parser.get<String>("@input") ), IMREAD_COLOR);
|
||||
if (src.empty())
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Show source image
|
||||
imshow("src", src);
|
||||
//! [load_image]
|
||||
|
||||
//! [gray]
|
||||
// Transform source image to gray if it is not already
|
||||
Mat gray;
|
||||
|
||||
if (src.channels() == 3)
|
||||
{
|
||||
cvtColor(src, gray, COLOR_BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
gray = src;
|
||||
}
|
||||
|
||||
// Show gray image
|
||||
show_wait_destroy("gray", gray);
|
||||
//! [gray]
|
||||
|
||||
//! [bin]
|
||||
// Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol
|
||||
Mat bw;
|
||||
adaptiveThreshold(~gray, bw, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
|
||||
|
||||
// Show binary image
|
||||
show_wait_destroy("binary", bw);
|
||||
//! [bin]
|
||||
|
||||
//! [init]
|
||||
// Create the images that will use to extract the horizontal and vertical lines
|
||||
Mat horizontal = bw.clone();
|
||||
Mat vertical = bw.clone();
|
||||
//! [init]
|
||||
|
||||
//! [horiz]
|
||||
// Specify size on horizontal axis
|
||||
int horizontal_size = horizontal.cols / 30;
|
||||
|
||||
// Create structure element for extracting horizontal lines through morphology operations
|
||||
Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1));
|
||||
|
||||
// Apply morphology operations
|
||||
erode(horizontal, horizontal, horizontalStructure, Point(-1, -1));
|
||||
dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1));
|
||||
|
||||
// Show extracted horizontal lines
|
||||
show_wait_destroy("horizontal", horizontal);
|
||||
//! [horiz]
|
||||
|
||||
//! [vert]
|
||||
// Specify size on vertical axis
|
||||
int vertical_size = vertical.rows / 30;
|
||||
|
||||
// Create structure element for extracting vertical lines through morphology operations
|
||||
Mat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size));
|
||||
|
||||
// Apply morphology operations
|
||||
erode(vertical, vertical, verticalStructure, Point(-1, -1));
|
||||
dilate(vertical, vertical, verticalStructure, Point(-1, -1));
|
||||
|
||||
// Show extracted vertical lines
|
||||
show_wait_destroy("vertical", vertical);
|
||||
//! [vert]
|
||||
|
||||
//! [smooth]
|
||||
// Inverse vertical image
|
||||
bitwise_not(vertical, vertical);
|
||||
show_wait_destroy("vertical_bit", vertical);
|
||||
|
||||
// Extract edges and smooth image according to the logic
|
||||
// 1. extract edges
|
||||
// 2. dilate(edges)
|
||||
// 3. src.copyTo(smooth)
|
||||
// 4. blur smooth img
|
||||
// 5. smooth.copyTo(src, edges)
|
||||
|
||||
// Step 1
|
||||
Mat edges;
|
||||
adaptiveThreshold(vertical, edges, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2);
|
||||
show_wait_destroy("edges", edges);
|
||||
|
||||
// Step 2
|
||||
Mat kernel = Mat::ones(2, 2, CV_8UC1);
|
||||
dilate(edges, edges, kernel);
|
||||
show_wait_destroy("dilate", edges);
|
||||
|
||||
// Step 3
|
||||
Mat smooth;
|
||||
vertical.copyTo(smooth);
|
||||
|
||||
// Step 4
|
||||
blur(smooth, smooth, Size(2, 2));
|
||||
|
||||
// Step 5
|
||||
smooth.copyTo(vertical, edges);
|
||||
|
||||
// Show final result
|
||||
show_wait_destroy("smooth - final", vertical);
|
||||
//! [smooth]
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void show_wait_destroy(const char* winname, cv::Mat img) {
|
||||
imshow(winname, img);
|
||||
moveWindow(winname, 500, 0);
|
||||
waitKey(0);
|
||||
destroyWindow(winname);
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @brief You will learn how to recover an image with motion blur distortion using a Wiener filter
|
||||
* @author Karpushin Vladislav, karpushin@ngs.ru, https://github.com/VladKarpushin
|
||||
*/
|
||||
#include <iostream>
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void help();
|
||||
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta);
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg);
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
|
||||
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr);
|
||||
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma = 5.0, double beta = 0.2);
|
||||
|
||||
const String keys =
|
||||
"{help h usage ? | | print this message }"
|
||||
"{image |input.png | input image name }"
|
||||
"{LEN |125 | length of a motion }"
|
||||
"{THETA |0 | angle of a motion in degrees }"
|
||||
"{SNR |700 | signal to noise ratio }"
|
||||
;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
help();
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LEN = parser.get<int>("LEN");
|
||||
double THETA = parser.get<double>("THETA");
|
||||
int snr = parser.get<int>("SNR");
|
||||
string strInFileName = parser.get<String>("image");
|
||||
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mat imgIn;
|
||||
imgIn = imread(strInFileName, IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
cout << "ERROR : Image cannot be loaded..!!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat imgOut;
|
||||
|
||||
//! [main]
|
||||
// it needs to process even image only
|
||||
Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
|
||||
|
||||
//Hw calculation (start)
|
||||
Mat Hw, h;
|
||||
calcPSF(h, roi.size(), LEN, THETA);
|
||||
calcWnrFilter(h, Hw, 1.0 / double(snr));
|
||||
//Hw calculation (stop)
|
||||
|
||||
imgIn.convertTo(imgIn, CV_32F);
|
||||
edgetaper(imgIn, imgIn);
|
||||
|
||||
// filtering (start)
|
||||
filter2DFreq(imgIn(roi), imgOut, Hw);
|
||||
// filtering (stop)
|
||||
//! [main]
|
||||
|
||||
imgOut.convertTo(imgOut, CV_8U);
|
||||
normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
|
||||
imwrite("result.jpg", imgOut);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void help()
|
||||
{
|
||||
cout << "2018-08-14" << endl;
|
||||
cout << "Motion_deblur_v2" << endl;
|
||||
cout << "You will learn how to recover an image with motion blur distortion using a Wiener filter" << endl;
|
||||
}
|
||||
|
||||
//! [calcPSF]
|
||||
void calcPSF(Mat& outputImg, Size filterSize, int len, double theta)
|
||||
{
|
||||
Mat h(filterSize, CV_32F, Scalar(0));
|
||||
Point point(filterSize.width / 2, filterSize.height / 2);
|
||||
ellipse(h, point, Size(0, cvRound(float(len) / 2.0)), 90.0 - theta, 0, 360, Scalar(255), FILLED);
|
||||
Scalar summa = sum(h);
|
||||
outputImg = h / summa[0];
|
||||
}
|
||||
//! [calcPSF]
|
||||
|
||||
//! [fftshift]
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg)
|
||||
{
|
||||
outputImg = inputImg.clone();
|
||||
int cx = outputImg.cols / 2;
|
||||
int cy = outputImg.rows / 2;
|
||||
Mat q0(outputImg, Rect(0, 0, cx, cy));
|
||||
Mat q1(outputImg, Rect(cx, 0, cx, cy));
|
||||
Mat q2(outputImg, Rect(0, cy, cx, cy));
|
||||
Mat q3(outputImg, Rect(cx, cy, cx, cy));
|
||||
Mat tmp;
|
||||
q0.copyTo(tmp);
|
||||
q3.copyTo(q0);
|
||||
tmp.copyTo(q3);
|
||||
q1.copyTo(tmp);
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
}
|
||||
//! [fftshift]
|
||||
|
||||
//! [filter2DFreq]
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
|
||||
{
|
||||
Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI, DFT_SCALE);
|
||||
|
||||
Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
|
||||
Mat complexH;
|
||||
merge(planesH, 2, complexH);
|
||||
Mat complexIH;
|
||||
mulSpectrums(complexI, complexH, complexIH, 0);
|
||||
|
||||
idft(complexIH, complexIH);
|
||||
split(complexIH, planes);
|
||||
outputImg = planes[0];
|
||||
}
|
||||
//! [filter2DFreq]
|
||||
|
||||
//! [calcWnrFilter]
|
||||
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr)
|
||||
{
|
||||
Mat h_PSF_shifted;
|
||||
fftshift(input_h_PSF, h_PSF_shifted);
|
||||
Mat planes[2] = { Mat_<float>(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI);
|
||||
split(complexI, planes);
|
||||
Mat denom;
|
||||
pow(abs(planes[0]), 2, denom);
|
||||
denom += nsr;
|
||||
divide(planes[0], denom, output_G);
|
||||
}
|
||||
//! [calcWnrFilter]
|
||||
|
||||
//! [edgetaper]
|
||||
void edgetaper(const Mat& inputImg, Mat& outputImg, double gamma, double beta)
|
||||
{
|
||||
int Nx = inputImg.cols;
|
||||
int Ny = inputImg.rows;
|
||||
Mat w1(1, Nx, CV_32F, Scalar(0));
|
||||
Mat w2(Ny, 1, CV_32F, Scalar(0));
|
||||
|
||||
float* p1 = w1.ptr<float>(0);
|
||||
float* p2 = w2.ptr<float>(0);
|
||||
float dx = float(2.0 * CV_PI / Nx);
|
||||
float x = float(-CV_PI);
|
||||
for (int i = 0; i < Nx; i++)
|
||||
{
|
||||
p1[i] = float(0.5 * (tanh((x + gamma / 2) / beta) - tanh((x - gamma / 2) / beta)));
|
||||
x += dx;
|
||||
}
|
||||
float dy = float(2.0 * CV_PI / Ny);
|
||||
float y = float(-CV_PI);
|
||||
for (int i = 0; i < Ny; i++)
|
||||
{
|
||||
p2[i] = float(0.5 * (tanh((y + gamma / 2) / beta) - tanh((y - gamma / 2) / beta)));
|
||||
y += dy;
|
||||
}
|
||||
Mat w = w2 * w1;
|
||||
multiply(inputImg, w, outputImg);
|
||||
}
|
||||
//! [edgetaper]
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @brief You will learn how to recover an out-of-focus image by Wiener filter
|
||||
* @author Karpushin Vladislav, karpushin@ngs.ru, https://github.com/VladKarpushin
|
||||
*/
|
||||
#include <iostream>
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void help();
|
||||
void calcPSF(Mat& outputImg, Size filterSize, int R);
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg);
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
|
||||
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr);
|
||||
|
||||
const String keys =
|
||||
"{help h usage ? | | print this message }"
|
||||
"{image |original.jpg | input image name }"
|
||||
"{R |5 | radius }"
|
||||
"{SNR |100 | signal to noise ratio}"
|
||||
;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
help();
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help"))
|
||||
{
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int R = parser.get<int>("R");
|
||||
int snr = parser.get<int>("SNR");
|
||||
string strInFileName = parser.get<String>("image");
|
||||
samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/out_of_focus_deblur_filter/images");
|
||||
|
||||
if (!parser.check())
|
||||
{
|
||||
parser.printErrors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mat imgIn;
|
||||
imgIn = imread(samples::findFile( strInFileName ), IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
cout << "ERROR : Image cannot be loaded..!!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Mat imgOut;
|
||||
|
||||
//! [main]
|
||||
// it needs to process even image only
|
||||
Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
|
||||
|
||||
//Hw calculation (start)
|
||||
Mat Hw, h;
|
||||
calcPSF(h, roi.size(), R);
|
||||
calcWnrFilter(h, Hw, 1.0 / double(snr));
|
||||
//Hw calculation (stop)
|
||||
|
||||
// filtering (start)
|
||||
filter2DFreq(imgIn(roi), imgOut, Hw);
|
||||
// filtering (stop)
|
||||
//! [main]
|
||||
|
||||
imgOut.convertTo(imgOut, CV_8U);
|
||||
normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
|
||||
imshow("Original", imgIn);
|
||||
imshow("Debluring", imgOut);
|
||||
imwrite("result.jpg", imgOut);
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void help()
|
||||
{
|
||||
cout << "2018-07-12" << endl;
|
||||
cout << "DeBlur_v8" << endl;
|
||||
cout << "You will learn how to recover an out-of-focus image by Wiener filter" << endl;
|
||||
}
|
||||
|
||||
//! [calcPSF]
|
||||
void calcPSF(Mat& outputImg, Size filterSize, int R)
|
||||
{
|
||||
Mat h(filterSize, CV_32F, Scalar(0));
|
||||
Point point(filterSize.width / 2, filterSize.height / 2);
|
||||
circle(h, point, R, 255, -1, 8);
|
||||
Scalar summa = sum(h);
|
||||
outputImg = h / summa[0];
|
||||
}
|
||||
//! [calcPSF]
|
||||
|
||||
//! [fftshift]
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg)
|
||||
{
|
||||
outputImg = inputImg.clone();
|
||||
int cx = outputImg.cols / 2;
|
||||
int cy = outputImg.rows / 2;
|
||||
Mat q0(outputImg, Rect(0, 0, cx, cy));
|
||||
Mat q1(outputImg, Rect(cx, 0, cx, cy));
|
||||
Mat q2(outputImg, Rect(0, cy, cx, cy));
|
||||
Mat q3(outputImg, Rect(cx, cy, cx, cy));
|
||||
Mat tmp;
|
||||
q0.copyTo(tmp);
|
||||
q3.copyTo(q0);
|
||||
tmp.copyTo(q3);
|
||||
q1.copyTo(tmp);
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
}
|
||||
//! [fftshift]
|
||||
|
||||
//! [filter2DFreq]
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
|
||||
{
|
||||
Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI, DFT_SCALE);
|
||||
|
||||
Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
|
||||
Mat complexH;
|
||||
merge(planesH, 2, complexH);
|
||||
Mat complexIH;
|
||||
mulSpectrums(complexI, complexH, complexIH, 0);
|
||||
|
||||
idft(complexIH, complexIH);
|
||||
split(complexIH, planes);
|
||||
outputImg = planes[0];
|
||||
}
|
||||
//! [filter2DFreq]
|
||||
|
||||
//! [calcWnrFilter]
|
||||
void calcWnrFilter(const Mat& input_h_PSF, Mat& output_G, double nsr)
|
||||
{
|
||||
Mat h_PSF_shifted;
|
||||
fftshift(input_h_PSF, h_PSF_shifted);
|
||||
Mat planes[2] = { Mat_<float>(h_PSF_shifted.clone()), Mat::zeros(h_PSF_shifted.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI);
|
||||
split(complexI, planes);
|
||||
Mat denom;
|
||||
pow(abs(planes[0]), 2, denom);
|
||||
denom += nsr;
|
||||
divide(planes[0], denom, output_G);
|
||||
}
|
||||
//! [calcWnrFilter]
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* @brief You will learn how to remove periodic noise in the Fourier domain
|
||||
* @author Karpushin Vladislav, karpushin@ngs.ru, https://github.com/VladKarpushin
|
||||
*/
|
||||
#include <iostream>
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <opencv2/imgcodecs.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg);
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H);
|
||||
void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius);
|
||||
void calcPSD(const Mat& inputImg, Mat& outputImg, int flag = 0);
|
||||
|
||||
const String keys =
|
||||
"{help h usage ? | | print this message }"
|
||||
"{@image |period_input.jpg | input image name }"
|
||||
;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
string strInFileName = parser.get<String>("@image");
|
||||
samples::addSamplesDataSearchSubDirectory("doc/tutorials/imgproc/periodic_noise_removing_filter/images");
|
||||
|
||||
Mat imgIn = imread(samples::findFile(strInFileName), IMREAD_GRAYSCALE);
|
||||
if (imgIn.empty()) //check whether the image is loaded or not
|
||||
{
|
||||
cout << "ERROR : Image cannot be loaded..!!" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
imshow("Image corrupted", imgIn);
|
||||
imgIn.convertTo(imgIn, CV_32F);
|
||||
|
||||
//! [main]
|
||||
// it needs to process even image only
|
||||
Rect roi = Rect(0, 0, imgIn.cols & -2, imgIn.rows & -2);
|
||||
imgIn = imgIn(roi);
|
||||
|
||||
// PSD calculation (start)
|
||||
Mat imgPSD;
|
||||
calcPSD(imgIn, imgPSD);
|
||||
fftshift(imgPSD, imgPSD);
|
||||
normalize(imgPSD, imgPSD, 0, 255, NORM_MINMAX);
|
||||
// PSD calculation (stop)
|
||||
|
||||
//H calculation (start)
|
||||
Mat H = Mat(roi.size(), CV_32F, Scalar(1));
|
||||
const int r = 21;
|
||||
synthesizeFilterH(H, Point(705, 458), r);
|
||||
synthesizeFilterH(H, Point(850, 391), r);
|
||||
synthesizeFilterH(H, Point(993, 325), r);
|
||||
//H calculation (stop)
|
||||
// filtering (start)
|
||||
Mat imgOut;
|
||||
fftshift(H, H);
|
||||
filter2DFreq(imgIn, imgOut, H);
|
||||
// filtering (stop)
|
||||
//! [main]
|
||||
|
||||
imgOut.convertTo(imgOut, CV_8U);
|
||||
normalize(imgOut, imgOut, 0, 255, NORM_MINMAX);
|
||||
imwrite("result.jpg", imgOut);
|
||||
imwrite("PSD.jpg", imgPSD);
|
||||
fftshift(H, H);
|
||||
normalize(H, H, 0, 255, NORM_MINMAX);
|
||||
imshow("Debluring", imgOut);
|
||||
imwrite("filter.jpg", H);
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! [fftshift]
|
||||
void fftshift(const Mat& inputImg, Mat& outputImg)
|
||||
{
|
||||
outputImg = inputImg.clone();
|
||||
int cx = outputImg.cols / 2;
|
||||
int cy = outputImg.rows / 2;
|
||||
Mat q0(outputImg, Rect(0, 0, cx, cy));
|
||||
Mat q1(outputImg, Rect(cx, 0, cx, cy));
|
||||
Mat q2(outputImg, Rect(0, cy, cx, cy));
|
||||
Mat q3(outputImg, Rect(cx, cy, cx, cy));
|
||||
Mat tmp;
|
||||
q0.copyTo(tmp);
|
||||
q3.copyTo(q0);
|
||||
tmp.copyTo(q3);
|
||||
q1.copyTo(tmp);
|
||||
q2.copyTo(q1);
|
||||
tmp.copyTo(q2);
|
||||
}
|
||||
//! [fftshift]
|
||||
|
||||
//! [filter2DFreq]
|
||||
void filter2DFreq(const Mat& inputImg, Mat& outputImg, const Mat& H)
|
||||
{
|
||||
Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI, DFT_SCALE);
|
||||
|
||||
Mat planesH[2] = { Mat_<float>(H.clone()), Mat::zeros(H.size(), CV_32F) };
|
||||
Mat complexH;
|
||||
merge(planesH, 2, complexH);
|
||||
Mat complexIH;
|
||||
mulSpectrums(complexI, complexH, complexIH, 0);
|
||||
|
||||
idft(complexIH, complexIH);
|
||||
split(complexIH, planes);
|
||||
outputImg = planes[0];
|
||||
}
|
||||
//! [filter2DFreq]
|
||||
|
||||
//! [synthesizeFilterH]
|
||||
void synthesizeFilterH(Mat& inputOutput_H, Point center, int radius)
|
||||
{
|
||||
Point c2 = center, c3 = center, c4 = center;
|
||||
c2.y = inputOutput_H.rows - center.y;
|
||||
c3.x = inputOutput_H.cols - center.x;
|
||||
c4 = Point(c3.x,c2.y);
|
||||
circle(inputOutput_H, center, radius, 0, -1, 8);
|
||||
circle(inputOutput_H, c2, radius, 0, -1, 8);
|
||||
circle(inputOutput_H, c3, radius, 0, -1, 8);
|
||||
circle(inputOutput_H, c4, radius, 0, -1, 8);
|
||||
}
|
||||
//! [synthesizeFilterH]
|
||||
|
||||
// Function calculates PSD(Power spectrum density) by fft with two flags
|
||||
// flag = 0 means to return PSD
|
||||
// flag = 1 means to return log(PSD)
|
||||
//! [calcPSD]
|
||||
void calcPSD(const Mat& inputImg, Mat& outputImg, int flag)
|
||||
{
|
||||
Mat planes[2] = { Mat_<float>(inputImg.clone()), Mat::zeros(inputImg.size(), CV_32F) };
|
||||
Mat complexI;
|
||||
merge(planes, 2, complexI);
|
||||
dft(complexI, complexI);
|
||||
split(complexI, planes); // planes[0] = Re(DFT(I)), planes[1] = Im(DFT(I))
|
||||
|
||||
planes[0].at<float>(0) = 0;
|
||||
planes[1].at<float>(0) = 0;
|
||||
|
||||
// compute the PSD = sqrt(Re(DFT(I))^2 + Im(DFT(I))^2)^2
|
||||
Mat imgPSD;
|
||||
magnitude(planes[0], planes[1], imgPSD); //imgPSD = sqrt(Power spectrum density)
|
||||
pow(imgPSD, 2, imgPSD); //it needs ^2 in order to get PSD
|
||||
outputImg = imgPSD;
|
||||
|
||||
// logPSD = log(1 + PSD)
|
||||
if (flag)
|
||||
{
|
||||
Mat imglogPSD;
|
||||
imglogPSD = imgPSD + Scalar::all(1);
|
||||
log(imglogPSD, imglogPSD);
|
||||
outputImg = imglogPSD;
|
||||
}
|
||||
}
|
||||
//! [calcPSD]
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file CannyDetector_Demo.cpp
|
||||
* @brief Sample code showing how to detect edges using the Canny Detector
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
//![variables]
|
||||
Mat src, src_gray;
|
||||
Mat dst, detected_edges;
|
||||
|
||||
int lowThreshold = 0;
|
||||
const int max_lowThreshold = 100;
|
||||
const int ratio = 3;
|
||||
const int kernel_size = 3;
|
||||
const char* window_name = "Edge Map";
|
||||
//![variables]
|
||||
|
||||
/**
|
||||
* @function CannyThreshold
|
||||
* @brief Trackbar callback - Canny thresholds input with a ratio 1:3
|
||||
*/
|
||||
static void CannyThreshold(int, void*)
|
||||
{
|
||||
//![reduce_noise]
|
||||
/// Reduce noise with a kernel 3x3
|
||||
blur( src_gray, detected_edges, Size(3,3) );
|
||||
//![reduce_noise]
|
||||
|
||||
//![canny]
|
||||
/// Canny detector
|
||||
Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
|
||||
//![canny]
|
||||
|
||||
/// Using Canny's output as a mask, we display our result
|
||||
//![fill]
|
||||
dst = Scalar::all(0);
|
||||
//![fill]
|
||||
|
||||
//![copyto]
|
||||
src.copyTo( dst, detected_edges);
|
||||
//![copyto]
|
||||
|
||||
//![display]
|
||||
imshow( window_name, dst );
|
||||
//![display]
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//![load]
|
||||
CommandLineParser parser( argc, argv, "{@input | fruits.jpg | input image}" );
|
||||
src = imread( samples::findFile( parser.get<String>( "@input" ) ), IMREAD_COLOR ); // Load an image
|
||||
|
||||
if( src.empty() )
|
||||
{
|
||||
std::cout << "Could not open or find the image!\n" << std::endl;
|
||||
std::cout << "Usage: " << argv[0] << " <Input image>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
//![load]
|
||||
|
||||
//![create_mat]
|
||||
/// Create a matrix of the same type and size as src (for dst)
|
||||
dst.create( src.size(), src.type() );
|
||||
//![create_mat]
|
||||
|
||||
//![convert_to_gray]
|
||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||
//![convert_to_gray]
|
||||
|
||||
//![create_window]
|
||||
namedWindow( window_name, WINDOW_AUTOSIZE );
|
||||
//![create_window]
|
||||
|
||||
//![create_trackbar]
|
||||
/// Create a Trackbar for user to enter threshold
|
||||
createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
|
||||
//![create_trackbar]
|
||||
|
||||
/// Show the image
|
||||
CannyThreshold(0, 0);
|
||||
|
||||
/// Wait until user exit program by pressing a key
|
||||
waitKey(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* @function Geometric_Transforms_Demo.cpp
|
||||
* @brief Demo code for Geometric Transforms
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/geometry.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//! [Load the image]
|
||||
CommandLineParser parser( argc, argv, "{@input | lena.jpg | input image}" );
|
||||
Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
|
||||
if( src.empty() )
|
||||
{
|
||||
cout << "Could not open or find the image!\n" << endl;
|
||||
cout << "Usage: " << argv[0] << " <Input image>" << endl;
|
||||
return -1;
|
||||
}
|
||||
//! [Load the image]
|
||||
|
||||
//! [Set your 3 points to calculate the Affine Transform]
|
||||
Point2f srcTri[3];
|
||||
srcTri[0] = Point2f( 0.f, 0.f );
|
||||
srcTri[1] = Point2f( src.cols - 1.f, 0.f );
|
||||
srcTri[2] = Point2f( 0.f, src.rows - 1.f );
|
||||
|
||||
Point2f dstTri[3];
|
||||
dstTri[0] = Point2f( 0.f, src.rows*0.33f );
|
||||
dstTri[1] = Point2f( src.cols*0.85f, src.rows*0.25f );
|
||||
dstTri[2] = Point2f( src.cols*0.15f, src.rows*0.7f );
|
||||
//! [Set your 3 points to calculate the Affine Transform]
|
||||
|
||||
//! [Get the Affine Transform]
|
||||
Mat warp_mat = getAffineTransform( srcTri, dstTri );
|
||||
//! [Get the Affine Transform]
|
||||
|
||||
//! [Apply the Affine Transform just found to the src image]
|
||||
/// Set the dst image the same type and size as src
|
||||
Mat warp_dst = Mat::zeros( src.rows, src.cols, src.type() );
|
||||
|
||||
warpAffine( src, warp_dst, warp_mat, warp_dst.size() );
|
||||
//! [Apply the Affine Transform just found to the src image]
|
||||
|
||||
/** Rotating the image after Warp */
|
||||
|
||||
//! [Compute a rotation matrix with respect to the center of the image]
|
||||
Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
|
||||
double angle = -50.0;
|
||||
double scale = 0.6;
|
||||
//! [Compute a rotation matrix with respect to the center of the image]
|
||||
|
||||
//! [Get the rotation matrix with the specifications above]
|
||||
Mat rot_mat = getRotationMatrix2D( center, angle, scale );
|
||||
//! [Get the rotation matrix with the specifications above]
|
||||
|
||||
//! [Rotate the warped image]
|
||||
Mat warp_rotate_dst;
|
||||
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() );
|
||||
//! [Rotate the warped image]
|
||||
|
||||
//! [Show what you got]
|
||||
imshow( "Source image", src );
|
||||
imshow( "Warp", warp_dst );
|
||||
imshow( "Warp + Rotate", warp_rotate_dst );
|
||||
//! [Show what you got]
|
||||
|
||||
//! [Wait until user exits the program]
|
||||
waitKey();
|
||||
//! [Wait until user exits the program]
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* @file HoughCircle_Demo.cpp
|
||||
* @brief Demo code for Hough Transform
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
namespace
|
||||
{
|
||||
// windows and trackbars name
|
||||
const std::string windowName = "Hough Circle Detection Demo";
|
||||
const std::string cannyThresholdTrackbarName = "Canny threshold";
|
||||
const std::string accumulatorThresholdTrackbarName = "Accumulator Threshold";
|
||||
|
||||
// initial and max values of the parameters of interests.
|
||||
const int cannyThresholdInitialValue = 100;
|
||||
const int accumulatorThresholdInitialValue = 50;
|
||||
const int maxAccumulatorThreshold = 200;
|
||||
const int maxCannyThreshold = 255;
|
||||
|
||||
void HoughDetection(const Mat& src_gray, const Mat& src_display, int cannyThreshold, int accumulatorThreshold)
|
||||
{
|
||||
// will hold the results of the detection
|
||||
std::vector<Vec3f> circles;
|
||||
// runs the actual detection
|
||||
HoughCircles( src_gray, circles, HOUGH_GRADIENT, 1, src_gray.rows/8, cannyThreshold, accumulatorThreshold, 0, 0 );
|
||||
|
||||
// clone the colour, input image for displaying purposes
|
||||
Mat display = src_display.clone();
|
||||
for( size_t i = 0; i < circles.size(); i++ )
|
||||
{
|
||||
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
|
||||
int radius = cvRound(circles[i][2]);
|
||||
// circle center
|
||||
circle( display, center, 3, Scalar(0,255,0), -1, 8, 0 );
|
||||
// circle outline
|
||||
circle( display, center, radius, Scalar(0,0,255), 3, 8, 0 );
|
||||
}
|
||||
|
||||
// shows the results
|
||||
imshow( windowName, display);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Mat src, src_gray;
|
||||
|
||||
// Read the image
|
||||
String imageName("stuff.jpg"); // by default
|
||||
if (argc > 1)
|
||||
{
|
||||
imageName = argv[1];
|
||||
}
|
||||
src = imread( samples::findFile( imageName ), IMREAD_COLOR );
|
||||
|
||||
if( src.empty() )
|
||||
{
|
||||
std::cerr << "Invalid input image\n";
|
||||
std::cout << "Usage : " << argv[0] << " <path_to_input_image>\n";;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Convert it to gray
|
||||
cvtColor( src, src_gray, COLOR_BGR2GRAY );
|
||||
|
||||
// Reduce the noise so we avoid false circle detection
|
||||
GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
|
||||
|
||||
//declare and initialize both parameters that are subjects to change
|
||||
int cannyThreshold = cannyThresholdInitialValue;
|
||||
int accumulatorThreshold = accumulatorThresholdInitialValue;
|
||||
|
||||
// create the main window, and attach the trackbars
|
||||
namedWindow( windowName, WINDOW_AUTOSIZE );
|
||||
createTrackbar(cannyThresholdTrackbarName, windowName, &cannyThreshold,maxCannyThreshold);
|
||||
createTrackbar(accumulatorThresholdTrackbarName, windowName, &accumulatorThreshold, maxAccumulatorThreshold);
|
||||
|
||||
// infinite loop to display
|
||||
// and refresh the content of the output image
|
||||
// until the user presses q or Q
|
||||
char key = 0;
|
||||
while(key != 'q' && key != 'Q')
|
||||
{
|
||||
// those parameters cannot be =0
|
||||
// so we must check here
|
||||
cannyThreshold = std::max(cannyThreshold, 1);
|
||||
accumulatorThreshold = std::max(accumulatorThreshold, 1);
|
||||
|
||||
//runs the detection, and update the display
|
||||
HoughDetection(src_gray, src, cannyThreshold, accumulatorThreshold);
|
||||
|
||||
// get user key
|
||||
key = (char)waitKey(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @file HoughLines_Demo.cpp
|
||||
* @brief Demo code for Hough Transform
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/// Global variables
|
||||
|
||||
/** General variables */
|
||||
Mat src, canny_edge, sobel_edge;
|
||||
Mat src_gray;
|
||||
Mat standard_hough, probabilistic_hough, weighted_hough;
|
||||
int min_threshold = 50;
|
||||
int max_trackbar = 150;
|
||||
int weightedhough_max_trackbar = 100000;
|
||||
|
||||
const char* standard_name = "Standard Hough Lines Demo";
|
||||
const char* probabilistic_name = "Probabilistic Hough Lines Demo";
|
||||
const char* weighted_name = "Weighted Hough Lines Demo";
|
||||
|
||||
int s_trackbar = max_trackbar;
|
||||
int p_trackbar = max_trackbar;
|
||||
int e_trackbar = 60;
|
||||
int w_trackbar = 60000;
|
||||
|
||||
/// Function Headers
|
||||
void help();
|
||||
void Standard_Hough( int, void* );
|
||||
void Probabilistic_Hough( int, void* );
|
||||
void Weighted_Hough( int, void* );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
// Read the image
|
||||
String imageName("building.jpg"); // by default
|
||||
if (argc > 1)
|
||||
{
|
||||
imageName = argv[1];
|
||||
}
|
||||
src = imread( samples::findFile( imageName ), IMREAD_COLOR );
|
||||
|
||||
if( src.empty() )
|
||||
{ help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Pass the image to gray
|
||||
cvtColor( src, src_gray, COLOR_RGB2GRAY );
|
||||
|
||||
/// Apply Canny/Sobel edge detector
|
||||
Canny( src_gray, canny_edge, 50, 200, 3 );
|
||||
Sobel( src_gray, sobel_edge, CV_16S, 1, 0 ); // dx(order of the derivative x)=1,dy=0
|
||||
|
||||
/// Create Trackbars for Thresholds
|
||||
char thresh_label[50];
|
||||
snprintf( thresh_label, sizeof(thresh_label), "Thres: %d + input", min_threshold );
|
||||
namedWindow( standard_name, WINDOW_AUTOSIZE );
|
||||
createTrackbar( thresh_label, standard_name, &s_trackbar, max_trackbar, Standard_Hough );
|
||||
|
||||
namedWindow( probabilistic_name, WINDOW_AUTOSIZE );
|
||||
createTrackbar( thresh_label, probabilistic_name, &p_trackbar, max_trackbar, Probabilistic_Hough );
|
||||
|
||||
const char* edge_thresh_label = "Edge Thres: input";
|
||||
namedWindow( weighted_name, WINDOW_AUTOSIZE);
|
||||
createTrackbar( edge_thresh_label, weighted_name, &e_trackbar, max_trackbar, Weighted_Hough);
|
||||
createTrackbar( thresh_label, weighted_name, &w_trackbar, weightedhough_max_trackbar, Weighted_Hough);
|
||||
|
||||
/// Initialize
|
||||
Standard_Hough(0, 0);
|
||||
Probabilistic_Hough(0, 0);
|
||||
Weighted_Hough(0, 0);
|
||||
waitKey(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function help
|
||||
* @brief Indications of how to run this program and why is it for
|
||||
*/
|
||||
void help()
|
||||
{
|
||||
printf("\t Hough Transform to detect lines \n ");
|
||||
printf("\t---------------------------------\n ");
|
||||
printf(" Usage: ./HoughLines_Demo <image_name> \n");
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Standard_Hough
|
||||
*/
|
||||
void Standard_Hough( int, void* )
|
||||
{
|
||||
vector<Vec2f> s_lines;
|
||||
cvtColor( canny_edge, standard_hough, COLOR_GRAY2BGR );
|
||||
|
||||
/// 1. Use Standard Hough Transform
|
||||
HoughLines( canny_edge, s_lines, 1, CV_PI/180, min_threshold + s_trackbar, 0, 0 );
|
||||
|
||||
/// Show the result
|
||||
for( size_t i = 0; i < s_lines.size(); i++ )
|
||||
{
|
||||
float r = s_lines[i][0], t = s_lines[i][1];
|
||||
double cos_t = cos(t), sin_t = sin(t);
|
||||
double x0 = r*cos_t, y0 = r*sin_t;
|
||||
double alpha = 1000;
|
||||
|
||||
Point pt1( cvRound(x0 + alpha*(-sin_t)), cvRound(y0 + alpha*cos_t) );
|
||||
Point pt2( cvRound(x0 - alpha*(-sin_t)), cvRound(y0 - alpha*cos_t) );
|
||||
line( standard_hough, pt1, pt2, Scalar(255,0,0), 3, LINE_AA);
|
||||
}
|
||||
|
||||
imshow( standard_name, standard_hough );
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Probabilistic_Hough
|
||||
*/
|
||||
void Probabilistic_Hough( int, void* )
|
||||
{
|
||||
vector<Vec4i> p_lines;
|
||||
cvtColor( canny_edge, probabilistic_hough, COLOR_GRAY2BGR );
|
||||
|
||||
/// 2. Use Probabilistic Hough Transform
|
||||
HoughLinesP( canny_edge, p_lines, 1, CV_PI/180, min_threshold + p_trackbar, 30, 10 );
|
||||
|
||||
/// Show the result
|
||||
for( size_t i = 0; i < p_lines.size(); i++ )
|
||||
{
|
||||
Vec4i l = p_lines[i];
|
||||
line( probabilistic_hough, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(255,0,0), 3, LINE_AA);
|
||||
}
|
||||
|
||||
imshow( probabilistic_name, probabilistic_hough );
|
||||
}
|
||||
|
||||
/**
|
||||
* @function Weighted_Hough
|
||||
* This can detect lines based on the edge intensities.
|
||||
*/
|
||||
void Weighted_Hough( int, void* )
|
||||
{
|
||||
vector<Vec2f> s_lines;
|
||||
|
||||
/// prepare
|
||||
Mat edge_img;
|
||||
convertScaleAbs(sobel_edge, edge_img );
|
||||
// use same threshold for edge with Hough.
|
||||
threshold( edge_img, edge_img, e_trackbar, 255, cv::THRESH_TOZERO);
|
||||
cvtColor( edge_img, weighted_hough, COLOR_GRAY2BGR );
|
||||
|
||||
/// 3. Use Weighted Hough Transform
|
||||
const bool use_edgeval{true};
|
||||
HoughLines( edge_img, s_lines, 1, CV_PI/180, min_threshold + w_trackbar, 0, 0, 0, CV_PI, use_edgeval);
|
||||
|
||||
/// Show the result
|
||||
for( size_t i = 0; i < s_lines.size(); i++ )
|
||||
{
|
||||
float r = s_lines[i][0], t = s_lines[i][1];
|
||||
double cos_t = cos(t), sin_t = sin(t);
|
||||
double x0 = r*cos_t, y0 = r*sin_t;
|
||||
double alpha = 1000;
|
||||
|
||||
Point pt1( cvRound(x0 + alpha*(-sin_t)), cvRound(y0 + alpha*cos_t) );
|
||||
Point pt2( cvRound(x0 - alpha*(-sin_t)), cvRound(y0 - alpha*cos_t) );
|
||||
line( weighted_hough, pt1, pt2, Scalar(255,0,0), 3, LINE_AA );
|
||||
}
|
||||
|
||||
imshow( weighted_name, weighted_hough );
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @file Laplace_Demo.cpp
|
||||
* @brief Sample code showing how to detect edges using the Laplace operator
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
//![variables]
|
||||
// Declare the variables we are going to use
|
||||
Mat src, src_gray, dst;
|
||||
int kernel_size = 3;
|
||||
int scale = 1;
|
||||
int delta = 0;
|
||||
int ddepth = CV_16S;
|
||||
const char* window_name = "Laplace Demo";
|
||||
//![variables]
|
||||
|
||||
//![load]
|
||||
const char* imageName = argc >=2 ? argv[1] : "lena.jpg";
|
||||
|
||||
src = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
|
||||
|
||||
// Check if image is loaded fine
|
||||
if(src.empty()){
|
||||
printf(" Error opening image\n");
|
||||
printf(" Program Arguments: [image_name -- default lena.jpg] \n");
|
||||
return -1;
|
||||
}
|
||||
//![load]
|
||||
|
||||
//![reduce_noise]
|
||||
// Reduce noise by blurring with a Gaussian filter ( kernel size = 3 )
|
||||
GaussianBlur( src, src, Size(3, 3), 0, 0, BORDER_DEFAULT );
|
||||
//![reduce_noise]
|
||||
|
||||
//![convert_to_gray]
|
||||
cvtColor( src, src_gray, COLOR_BGR2GRAY ); // Convert the image to grayscale
|
||||
//![convert_to_gray]
|
||||
|
||||
/// Apply Laplace function
|
||||
Mat abs_dst;
|
||||
//![laplacian]
|
||||
Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
|
||||
//![laplacian]
|
||||
|
||||
//![convert]
|
||||
// converting back to CV_8U
|
||||
convertScaleAbs( dst, abs_dst );
|
||||
//![convert]
|
||||
|
||||
//![display]
|
||||
imshow( window_name, abs_dst );
|
||||
waitKey(0);
|
||||
//![display]
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* @function Remap_Demo.cpp
|
||||
* @brief Demo code for Remap
|
||||
* @author Ana Huaman
|
||||
*/
|
||||
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
|
||||
/// Function Headers
|
||||
void update_map( int &ind, Mat &map_x, Mat &map_y );
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main(int argc, const char** argv)
|
||||
{
|
||||
CommandLineParser parser(argc, argv, "{@image |chicky_512.png|input image name}");
|
||||
std::string filename = parser.get<std::string>(0);
|
||||
//! [Load]
|
||||
/// Load the image
|
||||
Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );
|
||||
if (src.empty())
|
||||
{
|
||||
std::cout << "Cannot read image: " << filename << std::endl;
|
||||
return -1;
|
||||
}
|
||||
//! [Load]
|
||||
|
||||
//! [Create]
|
||||
/// Create dst, map_x and map_y with the same size as src:
|
||||
Mat dst(src.size(), src.type());
|
||||
Mat map_x(src.size(), CV_32FC1);
|
||||
Mat map_y(src.size(), CV_32FC1);
|
||||
//! [Create]
|
||||
|
||||
//! [Window]
|
||||
/// Create window
|
||||
const char* remap_window = "Remap demo";
|
||||
namedWindow( remap_window, WINDOW_AUTOSIZE );
|
||||
//! [Window]
|
||||
|
||||
//! [Loop]
|
||||
/// Index to switch between the remap modes
|
||||
int ind = 0;
|
||||
for(;;)
|
||||
{
|
||||
/// Update map_x & map_y. Then apply remap
|
||||
update_map(ind, map_x, map_y);
|
||||
remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0) );
|
||||
|
||||
/// Display results
|
||||
imshow( remap_window, dst );
|
||||
|
||||
/// Each 1 sec. Press ESC to exit the program
|
||||
char c = (char)waitKey( 1000 );
|
||||
if( c == 27 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
//! [Loop]
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @function update_map
|
||||
* @brief Fill the map_x and map_y matrices with 4 types of mappings
|
||||
*/
|
||||
//! [Update]
|
||||
void update_map( int &ind, Mat &map_x, Mat &map_y )
|
||||
{
|
||||
for( int i = 0; i < map_x.rows; i++ )
|
||||
{
|
||||
for( int j = 0; j < map_x.cols; j++ )
|
||||
{
|
||||
switch( ind )
|
||||
{
|
||||
case 0:
|
||||
if( j > map_x.cols*0.25 && j < map_x.cols*0.75 && i > map_x.rows*0.25 && i < map_x.rows*0.75 )
|
||||
{
|
||||
map_x.at<float>(i, j) = 2*( j - map_x.cols*0.25f ) + 0.5f;
|
||||
map_y.at<float>(i, j) = 2*( i - map_x.rows*0.25f ) + 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
map_x.at<float>(i, j) = 0;
|
||||
map_y.at<float>(i, j) = 0;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
map_x.at<float>(i, j) = (float)j;
|
||||
map_y.at<float>(i, j) = (float)(map_x.rows - i);
|
||||
break;
|
||||
case 2:
|
||||
map_x.at<float>(i, j) = (float)(map_x.cols - j);
|
||||
map_y.at<float>(i, j) = (float)i;
|
||||
break;
|
||||
case 3:
|
||||
map_x.at<float>(i, j) = (float)(map_x.cols - j);
|
||||
map_y.at<float>(i, j) = (float)(map_x.rows - i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // end of switch
|
||||
}
|
||||
}
|
||||
ind = (ind+1) % 4;
|
||||
}
|
||||
//! [Update]
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file Sobel_Demo.cpp
|
||||
* @brief Sample code uses Sobel or Scharr OpenCV functions for edge detection
|
||||
* @author OpenCV team
|
||||
*/
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/imgcodecs.hpp"
|
||||
#include "opencv2/highgui.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace cv;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* @function main
|
||||
*/
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
cv::CommandLineParser parser(argc, argv,
|
||||
"{@input |lena.jpg|input image}"
|
||||
"{ksize k|1|ksize (hit 'K' to increase its value at run time)}"
|
||||
"{scale s|1|scale (hit 'S' to increase its value at run time)}"
|
||||
"{delta d|0|delta (hit 'D' to increase its value at run time)}"
|
||||
"{help h|false|show help message}");
|
||||
|
||||
cout << "The sample uses Sobel or Scharr OpenCV functions for edge detection\n\n";
|
||||
parser.printMessage();
|
||||
cout << "\nPress 'ESC' to exit program.\nPress 'R' to reset values ( ksize will be -1 equal to Scharr function )";
|
||||
|
||||
//![variables]
|
||||
// First we declare the variables we are going to use
|
||||
Mat image,src, src_gray;
|
||||
Mat grad;
|
||||
const String window_name = "Sobel Demo - Simple Edge Detector";
|
||||
int ksize = parser.get<int>("ksize");
|
||||
int scale = parser.get<int>("scale");
|
||||
int delta = parser.get<int>("delta");
|
||||
int ddepth = CV_16S;
|
||||
//![variables]
|
||||
|
||||
//![load]
|
||||
String imageName = parser.get<String>("@input");
|
||||
// As usual we load our source image (src)
|
||||
image = imread( samples::findFile( imageName ), IMREAD_COLOR ); // Load an image
|
||||
|
||||
// Check if image is loaded fine
|
||||
if( image.empty() )
|
||||
{
|
||||
printf("Error opening image: %s\n", imageName.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
//![load]
|
||||
|
||||
for (;;)
|
||||
{
|
||||
//![reduce_noise]
|
||||
// Remove noise by blurring with a Gaussian filter ( kernel size = 3 )
|
||||
GaussianBlur(image, src, Size(3, 3), 0, 0, BORDER_DEFAULT);
|
||||
//![reduce_noise]
|
||||
|
||||
//![convert_to_gray]
|
||||
// Convert the image to grayscale
|
||||
cvtColor(src, src_gray, COLOR_BGR2GRAY);
|
||||
//![convert_to_gray]
|
||||
|
||||
//![sobel]
|
||||
/// Generate grad_x and grad_y
|
||||
Mat grad_x, grad_y;
|
||||
Mat abs_grad_x, abs_grad_y;
|
||||
|
||||
/// Gradient X
|
||||
Sobel(src_gray, grad_x, ddepth, 1, 0, ksize, scale, delta, BORDER_DEFAULT);
|
||||
|
||||
/// Gradient Y
|
||||
Sobel(src_gray, grad_y, ddepth, 0, 1, ksize, scale, delta, BORDER_DEFAULT);
|
||||
//![sobel]
|
||||
|
||||
//![convert]
|
||||
// converting back to CV_8U
|
||||
convertScaleAbs(grad_x, abs_grad_x);
|
||||
convertScaleAbs(grad_y, abs_grad_y);
|
||||
//![convert]
|
||||
|
||||
//![blend]
|
||||
/// Total Gradient (approximate)
|
||||
addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad);
|
||||
//![blend]
|
||||
|
||||
//![display]
|
||||
imshow(window_name, grad);
|
||||
char key = (char)waitKey(0);
|
||||
//![display]
|
||||
|
||||
if(key == 27)
|
||||
{
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (key == 'k' || key == 'K')
|
||||
{
|
||||
ksize = ksize < 30 ? ksize+2 : -1;
|
||||
}
|
||||
|
||||
if (key == 's' || key == 'S')
|
||||
{
|
||||
scale++;
|
||||
}
|
||||
|
||||
if (key == 'd' || key == 'D')
|
||||
{
|
||||
delta++;
|
||||
}
|
||||
|
||||
if (key == 'r' || key == 'R')
|
||||
{
|
||||
scale = 1;
|
||||
ksize = -1;
|
||||
delta = 0;
|
||||
}
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user