IPSDK  4_1_0_2
IPSDK : Image Processing Software Development Kit
// main.cpp:
// ---------
//
//
// --- IPSDK includes
// ------------------
// used to initialize IPSDK environment
#include <IPSDKCore/Config/LibraryInitializer.h>
// used to retrieve usual folders (IPSDK temporary folder, root development folder, etc.)
// used to catch exceptions potentially thrown by functions loadRawImageFile and saveRawImageFile
#include <IPSDKImageFile/Logger/IPSDKImageFileException.h>
// used to read/write an image from/to a TIFF file:
// used to smooth images with a Gaussian kernel
// used to threshold images
// used to compute a label image
// used to exstract shapes from label images
// used for Shape2dColl
#include <IPSDKBaseShapeSegmentation/Entity/2d/Shape2dColl.h>
// used for Hu moments and its distance
// used for numeric limits (minimum, maximum and epsilon)
// used for LUT transform
// used to display log messages
// --- third-party boost includes
// ------------------------------
// boost/filesystem/*: contains functions and classes providing facilities to
// manipulate files and directories, and associated paths
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
// boost/program_options/*: contains functions and classes used to manage and
// interpret arguments of command line
#include <boost/program_options/cmdline.hpp>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/parsers.hpp>
#include <boost/program_options/variables_map.hpp>
// --- third-party log4cplus include
// ---------------------------------
// used to add console as output support of logs
#include <log4cplus/consoleappender.h>
// --- STL include
// ---------------
#include <iostream>
#include <fstream>
using namespace boost::filesystem;
using namespace boost::program_options;
using namespace ipsdk;
using namespace ipsdk::image;
using namespace ipsdk::sample;
bool
readCmdArguments(int argc, char* argv[],
boost::filesystem::path& inImgFilePath,
std::vector<boost::filesystem::path>& vInTemplateImgFilePath,
boost::filesystem::path& outImgFilePath,
boost::filesystem::path& outputCsvResultPath,
ipReal64& thresholdBinarization, ipReal64& thresholdDistance, ipReal32& stdDev);
ipBool writeToCsvFile(const boost::filesystem::path& fileName, const ipsdk::geom::MatchShapesInfo& bestMatches, const ipReal64 threshold)
{
// Open the tile
std::ofstream file(fileName.c_str(), std::ios::out | std::ios::trunc);
if (!file)
// Return false if the file does not exist
return false;
// Write the titles
file << "Shape Index;Matched Template Index; Distance\n";
// For each shape (do not process the background)
const ipUInt32 nbTemplates = static_cast<ipUInt32>(bestMatches._shapesIdx.size());
for (ipUInt32 i = 0; i < nbTemplates; i++) {
file << (i + 1) << ";";
// Test for a bad match
if (bestMatches._shapesHuDistance[i] < threshold)
file << bestMatches._shapesIdx[i];
else
file << "Bad match according to threshold ( = " << threshold << ")";
file << ";" << bestMatches._shapesHuDistance[i] << "\n";
}
// Close the file
file.close();
return true;
}
int
main(int argc, char* argv[])
{
// add console appender for application logs
log4cplus::SharedAppenderPtr pConsole(new log4cplus::ConsoleAppender);
log4cplus::Logger::getRoot().addAppender(pConsole);
log4cplus::Logger::getRoot().setLogLevel(log4cplus::INFO_LOG_LEVEL);
// initialize IPSDK environment (first call to be done before calling any
// function or using any entity of IPSDK environment)
switch (initRes.getResult().value()) {
case ipsdk::core::eLibInitStatus::eLIS_Warn:
// IPSDK library is initialized but there were warnings;
// notify the user by displaying a message
break;
case ipsdk::core::eLibInitStatus::eLIS_Failed:
// IPSDK library initialization; notify the user and exit
return -1;
break;
default:
break;
}
// boost objects, used to store input and output images files paths
boost::filesystem::path inImgFilePath, outImgFilePath, outputCsvResultPath;
std::vector<boost::filesystem::path> vInTemplateImgFilePath;
// variables storing dimensions of input image
ipReal64 thresholdDistance, thresholdBinarization;
// read program options from command line, and, if appropriate,
// initialize input and output images files paths
if (!readCmdArguments(argc, argv, inImgFilePath, vInTemplateImgFilePath, outImgFilePath, outputCsvResultPath, thresholdBinarization, thresholdDistance, stdDev))
return -1;
// display a log message in "INFO" level, to notify the user of the current step
% inImgFilePath.string());
// declare the variable that will contain the input image, loaded from TIFF file
try {
// read input image from specified path
pInImg = image::file::loadTiffImageFile(inImgFilePath);
}
// loadRawImageFile function threw an exception; display error log
// message
% inImgFilePath.string() % e.getMsg());
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
// display a log message in "INFO" level, to notify the user of the current step
% inImgFilePath.string());
const ipUInt32 nbTemplates = static_cast<ipUInt32>(vInTemplateImgFilePath.size());
// declare the collection of template images, loaded from TIFF file
std::vector<ipsdk::image::ImagePtr> vTemplateImg;
vTemplateImg.resize(nbTemplates);
// For each template image path
for (ipUInt32 i = 0; i < nbTemplates; ++i) {
try {
vTemplateImg[i] = image::file::loadTiffImageFile(vInTemplateImgFilePath[i]);
}
// loadRawImageFile function threw an exception; display error log
// message
% vInTemplateImgFilePath[i].string() % e.getMsg());
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
}
// display a log message in "INFO" level, to notify the user of the current step
if (stdDev > 0.f)
if (stdDev > 0.f) {
// Blur the images to degrade them and avoid smooth the edges
try {
const ipReal32 gaussRatio = 0.99f;
for(ipUInt32 i = 0; i<nbTemplates; ++i)
ipsdk::imaproc::filter::gaussianSmoothing2dImg(vTemplateImg[i], stdDev, stdDev, ipsdk::imaproc::attr::createGaussianCoverage(gaussRatio, 2), vTemplateImg[i]);
}
// loadRawImageFile function threw an exception; display error log
// message
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
}
// display a log message in "INFO" level, to notify the user of the current step
Shape2dCollPtr pShape2dColl;
std::vector<Shape2dCollPtr> vTemplateShape2dCollPtr;
// Label image pointer is declared out of the try-catch block because it is used later
ImagePtr pLabelImg;
try {
// Binarize the input image
ImagePtr pBinImg = ipsdk::imaproc::bin::thresholdImg(pInImg, thresholdBinarization, 255);
// Compute the corresponding label image
// Extract the shapes from the label image
// For each template input image
for (ipUInt32 i = 0; i < nbTemplates; ++i) {
// Binarize the current template image
ImagePtr pBinImg_template = ipsdk::imaproc::bin::thresholdImg(vTemplateImg[i], thresholdBinarization, 255);
// Compute the label image
ImagePtr pLabelImg_template = ipsdk::imaproc::advmorpho::connectedComponent2dImg(pBinImg_template);
// Extrac tthe current template shapes
vTemplateShape2dCollPtr.push_back(pShape2dColl_template);
}
}
// loadRawImageFile function threw an exception; display error log
// message
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
// display a log message in "INFO" level, to notify the user of the current step
std::vector<ipsdk::geom::MatchShapesInfo> vMatches;
vMatches.resize(nbTemplates);
// For each template
for (ipUInt32 templateIdx = 0; templateIdx < nbTemplates; ++templateIdx) {
// Compute the distance between the template and the shapes in the input image
ipsdk::geom::MatchShapesInfo matches = ipsdk::geom::matchShapes(vTemplateShape2dCollPtr[templateIdx]->getColl()[1], pShape2dColl->getColl(), ipsdk::geom::eShapeHuDistanceType::eSDT_Type3, false);
// Store the result
vMatches[templateIdx] = matches;
}
// Declare and allocate the collection
// bestMatches is the collection of (_shapeHuDistance, _shapeIdx) structures
// where _shapeHuDistance is the minimum distance between a shape in the
// input image and the most similar template of index _shapeIdx
const ipUInt32 nbShapes = static_cast<ipUInt32>(vMatches[0]._shapesIdx.size());
bestMatches._shapesHuDistance.resize(nbShapes);
bestMatches._shapesIdx.resize(nbShapes);
// Initialize the best matches for each templates
for (ipUInt32 shapeIdx = 0; shapeIdx < nbShapes; ++shapeIdx) {
// For each template
for (ipUInt32 templateIdx = 0; templateIdx < nbTemplates; ++templateIdx) {
// Check if we find a new minimum distance
const ipsdk::geom::MatchShapesInfo matches = vMatches[templateIdx];
if (bestMatches._shapesHuDistance[shapeIdx] > matches._shapesHuDistance[shapeIdx]) {
bestMatches._shapesHuDistance[shapeIdx] = matches._shapesHuDistance[shapeIdx];
bestMatches._shapesIdx[shapeIdx] = templateIdx;
}
}
// Validate the match : if the distance is lower than the input threshold,
// then the algorithm considers that the match is correct, otherwise, it
// notes the match is unsatisfying by assigning the shape index to its
// maximum possible value
if (bestMatches._shapesHuDistance[shapeIdx] >= thresholdDistance)
bestMatches._shapesIdx[shapeIdx] = NumericLimits<ipUInt32>::max();
}
// Initialize the LUT
boost::shared_ptr<ipsdk::imaproc::attr::IntensityLUT> pLut = ipsdk::imaproc::attr::IntensityLUT::createNode();
pLut->setValue<ipsdk::imaproc::attr::IntensityLUT::BinWidth>(1.0);
pLut->setValue<ipsdk::imaproc::attr::IntensityLUT::InMin>(.0);
// Add the first value : the background is 0
pLut->push_back<ipsdk::imaproc::attr::IntensityLUT::LookupTable>(0);
// Add the other values
const ipUInt32 nbLabels = pShape2dColl->getSize();
for (ipUInt32 i = 1; i < nbLabels; ++i) {
const ipUInt32 matchIndex = static_cast<ipUInt32>(bestMatches._shapesIdx[i - 1] + 1);
if(matchIndex < NumericLimits<ipUInt32>::max())
pLut->push_back<ipsdk::imaproc::attr::IntensityLUT::LookupTable>(matchIndex);
else
pLut->push_back<ipsdk::imaproc::attr::IntensityLUT::LookupTable>(0);
}
// Apply the LUT
try {
ipsdk::image::file::saveTiffImageFile(outImgFilePath, pMatchImg);
}
// clear IPSDK environment features; should be called before exiting
// program
// quit the application with an exit code indicating an error
return -1;
}
// save matching results to output csv file
ipBool bWritten = writeToCsvFile(outputCsvResultPath, bestMatches, thresholdDistance);
if (bWritten == false) {
// clear IPSDK environment features; should be called before exiting
// program
return -1;
}
// display a log message in "INFO" level, to notify the user that the job
// is successfully done
// clearing IPSDK environment features; should be called before exiting
// program
return 0;
}
bool
readCmdArguments(int argc, char* argv[],
boost::filesystem::path& inImgFilePath,
std::vector<boost::filesystem::path>& vInTemplateImgFilePath,
boost::filesystem::path& outImgFilePath,
boost::filesystem::path& outputCsvResultPath,
ipReal64& thresholdBinarization, ipReal64& thresholdDistance, ipReal32& stdDev)
{
// list of allowed program options
boost::program_options::options_description desc("Allowed options");
desc.add_options()
("help", "produce help message")
("inputImgFilePath", value<path>(),
"input image file path (optional; only TIFF format is accepted)")
("inTemplatePathList", boost::program_options::value< std::vector<boost::filesystem::path> >()->multitoken(),
"list of input template image file path (optional; only TIFF format is accepted)")
("inThresholdBinarization", value<ipReal64>(),
"input binarization used for shape extraction (optional; default value = 15)")
("inThresholdDistance", value<ipReal64>(),
"input shape distance threshold (optional; default value = 0.2)")
("inStdDev", value<ipReal32>(),
"standard deviation for Gaussian smoothing, used to blur the edges (optional; default value = 1)")
("outputImgFilePath", value<path>(),
"output image file path (optional; only TIFF format is accepted)")
("outputCsvResultPath", value<path>(),
"output CSV file path (optional; the file is named shapeMatches.csv and is located in the same directory thant the output image path if not specified)")
;
// vm stores options found in command line
boost::program_options::variables_map vm;
try {
boost::program_options::store(parse_command_line(argc, argv, desc), vm);
}
catch (const std::exception& e) {
% e.what());
return false;
}
boost::program_options::notify(vm);
// if the user typed "<app_name>.exe --help"...
if (vm.count("help")) {
// ... the application displays help message
std::cout << desc << "\n";
return false;
}
// initialize input image file path with a default file path
inImgFilePath = getIPSDKDirectory(eInternalDirectory::eID_Images) / "shapes.tif";
// if the user specified his own input image file path as argument...
if (vm.count("inputImgFilePath")) {
// ... replace the default one by his own
inImgFilePath = vm["inputImgFilePath"].as<path>();
}
// check that input image file path exists. If not...
if (!boost::filesystem::exists(inImgFilePath)) {
// ... notify the user, and abort
% inImgFilePath);
return false;
}
// Read the orientation class centers
if (vm.count("inTemplatePathList")) {
vInTemplateImgFilePath = vm["inTemplatePathList"].as< std::vector<boost::filesystem::path> >();
}
else {
vInTemplateImgFilePath.clear();
vInTemplateImgFilePath.push_back(getIPSDKDirectory(eInternalDirectory::eID_Images) / "Templates/templateSquare.tif");
vInTemplateImgFilePath.push_back(getIPSDKDirectory(eInternalDirectory::eID_Images) / "Templates/templateTriangle.tif");
vInTemplateImgFilePath.push_back(getIPSDKDirectory(eInternalDirectory::eID_Images) / "Templates/templateHexagon.tif");
vInTemplateImgFilePath.push_back(getIPSDKDirectory(eInternalDirectory::eID_Images) / "Templates/templateCircle.tif");
}
// if the user specified his own input image file path as argument...
if (vm.count("outputImgFilePath"))
// ... replace the default one by his own
outImgFilePath = vm["outputImgFilePath"].as<path>();
else {
// ... otherwise, output image will be saved in temporary user directory
const path outputDir =
getIPSDKDefaultDirectory(eDefaultExternalDirectory::eDED_Tmp) /
"Sample";
// if output directory does not exist, we try to create it
if (!boost::filesystem::exists(outputDir)) {
try {
boost::filesystem::create_directories(outputDir);
}
catch (const std::exception& e) {
% outputDir % e.what());
}
}
// initialize output image file path with a default file path
outImgFilePath = outputDir / "shapeMatch.tif";
}
// if the user specified his own input CSV file path as argument...
if (vm.count("outputCsvResultPath"))
// ... replace the default one by his own
outputCsvResultPath = vm["outputCsvResultPath"].as<path>();
else {
// ... otherwise, output image will be saved in temporary user directory
const path outputDir = outImgFilePath.parent_path();
// initialize output image file path with a default file path
outputCsvResultPath = outputDir / "shapeMatch.csv";
}
// if the user specified his own input binarization threshold as argument...
if (vm.count("inThresholdBinarization")) {
thresholdBinarization = vm["inThresholdBinarization"].as<ipReal64>();
}
else thresholdBinarization = 15;
// if the user specified his own input distance threshold as argument...
if (vm.count("inThresholdDistance")) {
thresholdDistance = vm["inThresholdDistance"].as<ipReal64>();
}
else thresholdDistance = 0.2;
// if the user specified his own input Gaussian blurring standard deviation path as argument...
if (vm.count("inStdDev")) {
stdDev = vm["inStdDev"].as<ipReal32>();
}
else stdDev = 1.f;
return true;
}