<commit_before>#include "ofp/api/rpcencoder.h"
#include "ofp/api/apiconnection.h"

using ofp::api::RpcEncoder;

static bool errorFound(llvm::yaml::IO &io) {
  // This is a kludge. We need to know if the io object encountered an error
  // but the IO class doesn't support this. We need to reach into the Input
  // subclass to check for the error.
  llvm::yaml::Input *yin = static_cast<llvm::yaml::Input *>(&io);
  return yin->error();
}

RpcEncoder::RpcEncoder(const std::string &input, ApiConnection *conn, yaml::Encoder::ChannelFinder finder)
    : conn_{conn}, errorStream_{error_}, finder_{finder} {

  llvm::yaml::Input yin{input, nullptr, RpcEncoder::diagnosticHandler, this};
  if (!yin.error()) {
    yin >> *this;
  }

  if (yin.error()) {
    // Error string will be empty if there's no content.

    RpcErrorResponse response{id_ ? *id_ : 0};
    response.error.message = error();

    int code = ERROR_CODE_INVALID_REQUEST;
    if (response.error.message.find("unknown method") != std::string::npos) {
      code = ERROR_CODE_METHOD_NOT_FOUND;
    }

    response.error.code = code;
    conn_->rpcReply(&response);
  }
}

void RpcEncoder::diagnosticHandler(const llvm::SMDiagnostic &diag,
                                   void *context) {
  RpcEncoder *encoder = reinterpret_cast<RpcEncoder *>(context);
  encoder->addDiagnostic(diag);
}

void RpcEncoder::addDiagnostic(const llvm::SMDiagnostic &diag) {
  diag.print("", errorStream_, false);
}

void RpcEncoder::encodeParams(llvm::yaml::IO &io) {
  // Make sure no one uses an explicit ID value of 2^64 - 1. We use this value
  // to indicate a missing ID value.
  UInt64 id;
  if (id_) {
    id = *id_;
    if (id == RPC_ID_MISSING) {
      method_ = METHOD_UNSUPPORTED;
      return;
    }
  } else {
    id = RPC_ID_MISSING;
  }

  switch (method_) {
    case METHOD_LISTEN: {
        RpcListen listen{id};
        io.mapRequired("params", listen.params);
        if (!errorFound(io)) {
            conn_->onRpcListen(&listen);
        }
        break;
    }
    case METHOD_CONNECT: {
        RpcConnect connect{id};
        io.mapRequired("params", connect.params);
        if (!errorFound(io)) {
            conn_->onRpcConnect(&connect);
        }
        break;
    }
    case METHOD_CLOSE: {
        RpcClose close{id};
        io.mapRequired("params", close.params);
        if (!errorFound(io)) {
            conn_->onRpcClose(&close);
        }
        break;
    }
    case METHOD_SEND: {
        RpcSend send{id, finder_};
        io.mapRequired("params", send.params);
        if (!errorFound(io)) {
            conn_->onRpcSend(&send);
        }
        break;
    }
    case METHOD_CONFIG: {
        RpcConfig config{id};
        io.mapRequired("params", config.params);
        if (!errorFound(io)) {
          conn_->onRpcConfig(&config);
        }
        break;
    }
    case METHOD_LIST_CONNS: {
        RpcListConns list{id};
        io.mapRequired("params", list.params);
        if (!errorFound(io)) {
          conn_->onRpcListConns(&list);
        }
        break;
    }
    default:
      break;
  }
}
<commit_msg>Improve error message for misuse of notification method names.<commit_after>#include "ofp/api/rpcencoder.h"
#include "ofp/api/apiconnection.h"

using ofp::api::RpcEncoder;

static bool errorFound(llvm::yaml::IO &io) {
  // This is a kludge. We need to know if the io object encountered an error
  // but the IO class doesn't support this. We need to reach into the Input
  // subclass to check for the error.
  llvm::yaml::Input *yin = static_cast<llvm::yaml::Input *>(&io);
  return yin->error();
}

RpcEncoder::RpcEncoder(const std::string &input, ApiConnection *conn, yaml::Encoder::ChannelFinder finder)
    : conn_{conn}, errorStream_{error_}, finder_{finder} {

  llvm::yaml::Input yin{input, nullptr, RpcEncoder::diagnosticHandler, this};
  if (!yin.error()) {
    yin >> *this;
  }

  if (yin.error()) {
    // Error string will be empty if there's no content.

    RpcErrorResponse response{id_ ? *id_ : 0};
    response.error.message = error();

    int code = ERROR_CODE_INVALID_REQUEST;
    if (response.error.message.find("unknown method") != std::string::npos) {
      code = ERROR_CODE_METHOD_NOT_FOUND;
    }

    response.error.code = code;
    conn_->rpcReply(&response);
  }
}

void RpcEncoder::diagnosticHandler(const llvm::SMDiagnostic &diag,
                                   void *context) {
  RpcEncoder *encoder = reinterpret_cast<RpcEncoder *>(context);
  encoder->addDiagnostic(diag);
}

void RpcEncoder::addDiagnostic(const llvm::SMDiagnostic &diag) {
  diag.print("", errorStream_, false);
}

void RpcEncoder::encodeParams(llvm::yaml::IO &io) {
  // Make sure no one uses an explicit ID value of 2^64 - 1. We use this value
  // to indicate a missing ID value. (FIXME)
  UInt64 id;
  if (id_) {
    id = *id_;
    if (id == RPC_ID_MISSING) {
      method_ = METHOD_UNSUPPORTED;
      return;
    }
  } else {
    id = RPC_ID_MISSING;
  }

  switch (method_) {
    case METHOD_LISTEN: {
        RpcListen listen{id};
        io.mapRequired("params", listen.params);
        if (!errorFound(io)) {
            conn_->onRpcListen(&listen);
        }
        break;
    }
    case METHOD_CONNECT: {
        RpcConnect connect{id};
        io.mapRequired("params", connect.params);
        if (!errorFound(io)) {
            conn_->onRpcConnect(&connect);
        }
        break;
    }
    case METHOD_CLOSE: {
        RpcClose close{id};
        io.mapRequired("params", close.params);
        if (!errorFound(io)) {
            conn_->onRpcClose(&close);
        }
        break;
    }
    case METHOD_SEND: {
        RpcSend send{id, finder_};
        io.mapRequired("params", send.params);
        if (!errorFound(io)) {
            conn_->onRpcSend(&send);
        }
        break;
    }
    case METHOD_CONFIG: {
        RpcConfig config{id};
        io.mapRequired("params", config.params);
        if (!errorFound(io)) {
          conn_->onRpcConfig(&config);
        }
        break;
    }
    case METHOD_LIST_CONNS: {
        RpcListConns list{id};
        io.mapRequired("params", list.params);
        if (!errorFound(io)) {
          conn_->onRpcListConns(&list);
        }
        break;
    }
    case METHOD_DATAPATH:
      io.setError("'ofp.datapath' is for notifications only.");
      break;
    case METHOD_MESSAGE:
      io.setError("Use 'ofp.send' instead; 'ofp.message' is for notifications only.");
      break;
    case METHOD_MESSAGE_ERROR:
      io.setError("'ofp.message_error' is for notifications only.");
      break;
    default:
      break;
  }
}
<|endoftext|>


<commit_before>
#include "ofxCvContourFinder.h"

//--------------------------------------------------------------------------------
bool sort_carea_compare( const CvSeq* a, const CvSeq* b) {
	// use opencv to calc size, then sort based on size
	float areaa = fabs(cvContourArea(a, CV_WHOLE_SEQ));
	float areab = fabs(cvContourArea(b, CV_WHOLE_SEQ));

    //return 0;
	return (areaa > areab);
}

//--------------------------------------------------------------------------------
ofxCvContourFinder::ofxCvContourFinder() {
    _width = 0;
    _height = 0;
	myMoments = (CvMoments*)malloc( sizeof(CvMoments) );
	reset();
}

//--------------------------------------------------------------------------------
ofxCvContourFinder::~ofxCvContourFinder() {
	free( myMoments );
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::reset() {
    cvSeqBlobs.clear();
    blobs.clear();
    nBlobs = 0;
}

//--------------------------------------------------------------------------------
int ofxCvContourFinder::findContours( ofxCvGrayscaleImage&  input,
									  int minArea,
									  int maxArea,
									  int nConsidered,
									  bool bFindHoles,
                                      bool bUseApproximation) {

    // get width/height disregarding ROI
    IplImage* ipltemp = input.getCvImage();
    _width = ipltemp->width;
    _height = ipltemp->height;

	reset();

	// opencv will clober the image it detects contours on, so we want to
    // copy it into a copy before we detect contours.  That copy is allocated
    // if necessary (necessary = (a) not allocated or (b) wrong size)
	// so be careful if you pass in different sized images to "findContours"
	// there is a performance penalty, but we think there is not a memory leak
    // to worry about better to create mutiple contour finders for different
    // sizes, ie, if you are finding contours in a 640x480 image but also a
    // 320x240 image better to make two ofxCvContourFinder objects then to use
    // one, because you will get penalized less.

	if( inputCopy.width == 0 ) {
		inputCopy.allocate( _width, _height );
	} else if( inputCopy.width != _width || inputCopy.height != _height ) {
        // reallocate to new size
        inputCopy.clear();
        inputCopy.allocate( _width, _height );
	}

    inputCopy = input;
    inputCopy.setROI( input.getROI() );

	CvSeq* contour_list = NULL;
	contour_storage = cvCreateMemStorage( 1000 );
	storage	= cvCreateMemStorage( 1000 );

	CvContourRetrievalMode  retrieve_mode
        = (bFindHoles) ? CV_RETR_LIST : CV_RETR_EXTERNAL;
	cvFindContours( inputCopy.getCvImage(), contour_storage, &contour_list,
                    sizeof(CvContour), retrieve_mode, bUseApproximation ? CV_CHAIN_APPROX_SIMPLE : CV_CHAIN_APPROX_NONE );
	CvSeq* contour_ptr = contour_list;

	// put the contours from the linked list, into an array for sorting
	while( (contour_ptr != NULL) ) {
		float area = fabs( cvContourArea(contour_ptr, CV_WHOLE_SEQ) );
		if( (area > minArea) && (area < maxArea) ) {
            cvSeqBlobs.push_back(contour_ptr);
		}
		contour_ptr = contour_ptr->h_next;
	}

	// sort the pointers based on size
	if( cvSeqBlobs.size() > 1 ) {
        sort( cvSeqBlobs.begin(), cvSeqBlobs.end(), sort_carea_compare );
	}

	// now, we have cvSeqBlobs.size() contours, sorted by size in the array
    // cvSeqBlobs let's get the data out and into our structures that we like
	for( int i = 0; i < MIN(nConsidered, cvSeqBlobs.size()); i++ ) {
		blobs.push_back( ofxCvBlob() );
		float area = cvContourArea( cvSeqBlobs[i], CV_WHOLE_SEQ );
		CvRect rect	= cvBoundingRect( cvSeqBlobs[i], 0 );
		cvMoments( cvSeqBlobs[i], myMoments );

		blobs[i].area                     = fabs(area);
		blobs[i].hole                     = area < 0 ? true : false;
		blobs[i].length 			      = cvArcLength(cvSeqBlobs[i]);
		blobs[i].boundingRect.x           = rect.x;
		blobs[i].boundingRect.y           = rect.y;
		blobs[i].boundingRect.width       = rect.width;
		blobs[i].boundingRect.height      = rect.height;
		blobs[i].centroid.x 			  = (int) (myMoments->m10 / myMoments->m00);
		blobs[i].centroid.y 			  = (int) (myMoments->m01 / myMoments->m00);

		// get the points for the blob:
		CvPoint           pt;
		CvSeqReader       reader;
		cvStartReadSeq( cvSeqBlobs[i], &reader, 0 );

    	for( int j=0; j < cvSeqBlobs[i]->total; j++ ) {
			CV_READ_SEQ_ELEM( pt, reader );
            blobs[i].pts.push_back( ofPoint((float)pt.x, (float)pt.y) );
		}
		blobs[i].nPts = blobs[i].pts.size();

	}

    nBlobs = blobs.size();

	// Free the storage memory.
	// Warning: do this inside this function otherwise a strange memory leak
	if( contour_storage != NULL ) { cvReleaseMemStorage(&contour_storage); }
	if( storage != NULL ) { cvReleaseMemStorage(&storage); }

	return nBlobs;

}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::draw( float x, float y, float w, float h ) {

    float scalex = 0.0f;
    float scaley = 0.0f;
    if( _width != 0 ) { scalex = w/_width; } else { scalex = 1.0f; }
    if( _height != 0 ) { scaley = h/_height; } else { scaley = 1.0f; }

    if(bAnchorIsPct){
        x -= anchor.x * w;
        y -= anchor.y * h;
    }else{
        x -= anchor.x;
        y -= anchor.y;
    }

	// ---------------------------- draw the bounding rectangle
	ofSetColor(0xDD00CC);
    glPushMatrix();
    glTranslatef( x, y, 0.0 );
    glScalef( scalex, scaley, 0.0 );

	ofNoFill();
	for( int i=0; i<blobs.size(); i++ ) {
		ofRect( blobs[i].boundingRect.x, blobs[i].boundingRect.y,
                blobs[i].boundingRect.width, blobs[i].boundingRect.height );
	}

	// ---------------------------- draw the blobs
	ofSetColor(0x00FFFF);

	for( int i=0; i<blobs.size(); i++ ) {
		ofNoFill();
		ofBeginShape();
		for( int j=0; j<blobs[i].nPts; j++ ) {
			ofVertex( blobs[i].pts[j].x, blobs[i].pts[j].y );
		}
		ofEndShape();

	}
	glPopMatrix();
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::setAnchorPercent( float xPct, float yPct ){
    anchor.x = xPct;
    anchor.y = yPct;
    bAnchorIsPct = true;
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::setAnchorPoint( int x, int y ){
    anchor.x = x;
    anchor.y = y;
    bAnchorIsPct = false;
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::resetAnchor(){
    anchor.set(0,0);
    bAnchorIsPct = false;
}

<commit_msg>getting rid of some warnings by casting size of vectors to int<commit_after>
#include "ofxCvContourFinder.h"

//--------------------------------------------------------------------------------
bool sort_carea_compare( const CvSeq* a, const CvSeq* b) {
	// use opencv to calc size, then sort based on size
	float areaa = fabs(cvContourArea(a, CV_WHOLE_SEQ));
	float areab = fabs(cvContourArea(b, CV_WHOLE_SEQ));

    //return 0;
	return (areaa > areab);
}

//--------------------------------------------------------------------------------
ofxCvContourFinder::ofxCvContourFinder() {
    _width = 0;
    _height = 0;
	myMoments = (CvMoments*)malloc( sizeof(CvMoments) );
	reset();
}

//--------------------------------------------------------------------------------
ofxCvContourFinder::~ofxCvContourFinder() {
	free( myMoments );
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::reset() {
    cvSeqBlobs.clear();
    blobs.clear();
    nBlobs = 0;
}

//--------------------------------------------------------------------------------
int ofxCvContourFinder::findContours( ofxCvGrayscaleImage&  input,
									  int minArea,
									  int maxArea,
									  int nConsidered,
									  bool bFindHoles,
                                      bool bUseApproximation) {

    // get width/height disregarding ROI
    IplImage* ipltemp = input.getCvImage();
    _width = ipltemp->width;
    _height = ipltemp->height;

	reset();

	// opencv will clober the image it detects contours on, so we want to
    // copy it into a copy before we detect contours.  That copy is allocated
    // if necessary (necessary = (a) not allocated or (b) wrong size)
	// so be careful if you pass in different sized images to "findContours"
	// there is a performance penalty, but we think there is not a memory leak
    // to worry about better to create mutiple contour finders for different
    // sizes, ie, if you are finding contours in a 640x480 image but also a
    // 320x240 image better to make two ofxCvContourFinder objects then to use
    // one, because you will get penalized less.

	if( inputCopy.width == 0 ) {
		inputCopy.allocate( _width, _height );
	} else if( inputCopy.width != _width || inputCopy.height != _height ) {
        // reallocate to new size
        inputCopy.clear();
        inputCopy.allocate( _width, _height );
	}

    inputCopy = input;
    inputCopy.setROI( input.getROI() );

	CvSeq* contour_list = NULL;
	contour_storage = cvCreateMemStorage( 1000 );
	storage	= cvCreateMemStorage( 1000 );

	CvContourRetrievalMode  retrieve_mode
        = (bFindHoles) ? CV_RETR_LIST : CV_RETR_EXTERNAL;
	cvFindContours( inputCopy.getCvImage(), contour_storage, &contour_list,
                    sizeof(CvContour), retrieve_mode, bUseApproximation ? CV_CHAIN_APPROX_SIMPLE : CV_CHAIN_APPROX_NONE );
	CvSeq* contour_ptr = contour_list;

	// put the contours from the linked list, into an array for sorting
	while( (contour_ptr != NULL) ) {
		float area = fabs( cvContourArea(contour_ptr, CV_WHOLE_SEQ) );
		if( (area > minArea) && (area < maxArea) ) {
            cvSeqBlobs.push_back(contour_ptr);
		}
		contour_ptr = contour_ptr->h_next;
	}

	// sort the pointers based on size
	if( cvSeqBlobs.size() > 1 ) {
        sort( cvSeqBlobs.begin(), cvSeqBlobs.end(), sort_carea_compare );
	}

	// now, we have cvSeqBlobs.size() contours, sorted by size in the array
    // cvSeqBlobs let's get the data out and into our structures that we like
	for( int i = 0; i < MIN(nConsidered, (int)cvSeqBlobs.size()); i++ ) {
		blobs.push_back( ofxCvBlob() );
		float area = cvContourArea( cvSeqBlobs[i], CV_WHOLE_SEQ );
		CvRect rect	= cvBoundingRect( cvSeqBlobs[i], 0 );
		cvMoments( cvSeqBlobs[i], myMoments );

		blobs[i].area                     = fabs(area);
		blobs[i].hole                     = area < 0 ? true : false;
		blobs[i].length 			      = cvArcLength(cvSeqBlobs[i]);
		blobs[i].boundingRect.x           = rect.x;
		blobs[i].boundingRect.y           = rect.y;
		blobs[i].boundingRect.width       = rect.width;
		blobs[i].boundingRect.height      = rect.height;
		blobs[i].centroid.x 			  = (int) (myMoments->m10 / myMoments->m00);
		blobs[i].centroid.y 			  = (int) (myMoments->m01 / myMoments->m00);

		// get the points for the blob:
		CvPoint           pt;
		CvSeqReader       reader;
		cvStartReadSeq( cvSeqBlobs[i], &reader, 0 );

    	for( int j=0; j < cvSeqBlobs[i]->total; j++ ) {
			CV_READ_SEQ_ELEM( pt, reader );
            blobs[i].pts.push_back( ofPoint((float)pt.x, (float)pt.y) );
		}
		blobs[i].nPts = blobs[i].pts.size();

	}

    nBlobs = blobs.size();

	// Free the storage memory.
	// Warning: do this inside this function otherwise a strange memory leak
	if( contour_storage != NULL ) { cvReleaseMemStorage(&contour_storage); }
	if( storage != NULL ) { cvReleaseMemStorage(&storage); }

	return nBlobs;

}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::draw( float x, float y, float w, float h ) {

    float scalex = 0.0f;
    float scaley = 0.0f;
    if( _width != 0 ) { scalex = w/_width; } else { scalex = 1.0f; }
    if( _height != 0 ) { scaley = h/_height; } else { scaley = 1.0f; }

    if(bAnchorIsPct){
        x -= anchor.x * w;
        y -= anchor.y * h;
    }else{
        x -= anchor.x;
        y -= anchor.y;
    }

	// ---------------------------- draw the bounding rectangle
	ofSetColor(0xDD00CC);
    glPushMatrix();
    glTranslatef( x, y, 0.0 );
    glScalef( scalex, scaley, 0.0 );

	ofNoFill();
	for( int i=0; i<(int)blobs.size(); i++ ) {
		ofRect( blobs[i].boundingRect.x, blobs[i].boundingRect.y,
                blobs[i].boundingRect.width, blobs[i].boundingRect.height );
	}

	// ---------------------------- draw the blobs
	ofSetColor(0x00FFFF);

	for( int i=0; i<(int)blobs.size(); i++ ) {
		ofNoFill();
		ofBeginShape();
		for( int j=0; j<blobs[i].nPts; j++ ) {
			ofVertex( blobs[i].pts[j].x, blobs[i].pts[j].y );
		}
		ofEndShape();

	}
	glPopMatrix();
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::setAnchorPercent( float xPct, float yPct ){
    anchor.x = xPct;
    anchor.y = yPct;
    bAnchorIsPct = true;
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::setAnchorPoint( int x, int y ){
    anchor.x = x;
    anchor.y = y;
    bAnchorIsPct = false;
}

//--------------------------------------------------------------------------------
void ofxCvContourFinder::resetAnchor(){
    anchor.set(0,0);
    bAnchorIsPct = false;
}

<|endoftext|>


<commit_before>#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#define DEGREES_TO_RADIANS(x) (x * PI / 180.0f)
#endif

#include "bmp_heightmap_loader.hpp"
#include "code/ylikuutio/triangulation/quad_triangulation.hpp"
#include "code/ylikuutio/common/globals.hpp"
#include "code/ylikuutio/common/global_variables.hpp"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cmath>    // NAN, std::isnan, std::pow
#include <cstdio>   // std::FILE, std::fclose, std::fopen, std::fread, std::getchar, std::printf etc.
#include <cstring>  // std::memcmp, std::strcmp, std::strlen, std::strncmp
#include <iostream> // std::cout, std::cin, std::cerr
#include <stdint.h> // uint32_t etc.
#include <string>   // std::string
#include <vector>   // std::vector

namespace loaders
{
    bool load_BMP_world(
            std::string image_path,
            std::vector<glm::vec3>& out_vertices,
            std::vector<glm::vec2>& out_UVs,
            std::vector<glm::vec3>& out_normals,
            uint32_t &image_width,
            uint32_t &image_height,
            std::string color_channel,
            uint32_t x_step,
            uint32_t z_step,
            std::string triangulation_type)
    {
        std::cout << "Loading BMP file " << image_path << " ...\n";

        uint8_t header_size = 54;

        // Data read from the header of the BMP file
        uint8_t header[header_size];
        uint32_t image_size;
        // Actual RGB image data.
        uint8_t *image_data;

        // Open the file
        const char* char_image_path = image_path.c_str();
        std::FILE* file = std::fopen(char_image_path, "rb");
        if (!file)
        {
            std::cerr << image_path << " could not be opened.\n";
            return false;
        }

        // Read the header, i.e. the 54 first bytes

        // If less than 54 bytes are read, it's a problem.
        if (std::fread(header, 1, sizeof(header), file) != sizeof(header))
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // A BMP files always begins with "BM"
        if ((header[0] != 'B') || (header[1] != 'M'))
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // Make sure this is a 24bpp file
        if (*(uint32_t*) &header[0x1e] != 0)
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        if (*(uint32_t*) &header[0x1c] != 24)
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // Read the information about the image
        image_size = *(uint32_t*) &header[0x22];
        image_width = *(uint32_t*) &header[0x12];
        image_height = *(uint32_t*) &header[0x16];

        // Define world size.
        uint32_t world_size = image_width * image_height;

        // Some BMP files are misformatted, guess missing information
        if (image_size == 0)
        {
            image_size = image_width * image_height * 3; // 3 : one byte for each Red, Green and Blue component
        }

        // Create a buffer.
        image_data = new uint8_t[image_size];

        if (image_data == nullptr)
        {
            std::cerr << "Reserving memory for image data failed.\n";
            std::fclose(file);
            return false;
        }

        // Read the actual image data from the file into the buffer.
        std::fread(image_data, 1, sizeof(image_data), file);

        // Everything is in memory now, the file can be closed
        std::fclose(file);

        uint8_t *image_pointer;
        image_pointer = image_data;

        float* vertex_data;
        vertex_data = new float[world_size];

        if (vertex_data == nullptr)
        {
            std::cerr << "Reserving memory for vertex data failed.\n";
            delete image_data;
            std::fclose(file);
            return false;
        }

        float* vertex_pointer;
        vertex_pointer = vertex_data;

        const char* char_color_channel = color_channel.c_str();

        // start processing image_data.
        for (int32_t z = image_height - 1; z >= 0; z--)
        {
            uint32_t image_width_in_bytes = 3 * image_width;
            image_pointer = image_data + z * image_width_in_bytes;

            for (int32_t x = 0; x < image_width; x++)
            {
                uint32_t y;

                if (std::strcmp(char_color_channel, "blue") == 0)
                {
                    y = static_cast<float>(*image_pointer);       // y-coordinate is the blue (B) value.
                }
                else if (std::strcmp(char_color_channel, "green") == 0)
                {
                    y = static_cast<float>(*(image_pointer + 1)); // y-coordinate is the green (G) value.
                }
                else if (std::strcmp(char_color_channel, "red") == 0)
                {
                    y = static_cast<float>(*(image_pointer + 2)); // y-coordinate is the red (R) value.
                }
                // y-coordinate is the mean of R, G, & B.
                else if ((std::strcmp(char_color_channel, "mean") == 0) || (std::strcmp(char_color_channel, "all") == 0))
                {
                    y = (static_cast<float>(*image_pointer) + static_cast<float>(*(image_pointer + 1)) + static_cast<float>(*(image_pointer + 2))) / 3.0f;
                }
                else
                {
                    std::cerr << "invalid color channel!\n";
                    delete image_data;
                    delete vertex_data;
                    return false;
                }

                // std::cout << color_channel << " color channel value at (" << x << ", " << z << "): " << y << ".\n";
                *vertex_pointer++ = y;
                image_pointer += 3; // R, G, & B.
            }
        }

        delete image_data;

        std::cout << "color channel in use: " << color_channel << "\n";

        TriangulateQuadsStruct triangulate_quads_struct;
        triangulate_quads_struct.image_width = image_width;
        triangulate_quads_struct.image_height = image_height;
        triangulate_quads_struct.x_step = x_step;
        triangulate_quads_struct.z_step = z_step;
        triangulate_quads_struct.triangulation_type = triangulation_type;
        triangulate_quads_struct.sphere_radius = NAN;
        triangulate_quads_struct.spherical_world_struct = SphericalWorldStruct(); // not used, but is needed in the function call.

        bool result = geometry::triangulate_quads(vertex_data, triangulate_quads_struct, out_vertices, out_UVs, out_normals);
        delete vertex_data;
        return result;
    }
}
<commit_msg>Bugfix: Do not try to close the file twice if reserving memory failed.<commit_after>#ifndef GLM_FORCE_RADIANS
#define GLM_FORCE_RADIANS
#define DEGREES_TO_RADIANS(x) (x * PI / 180.0f)
#endif

#include "bmp_heightmap_loader.hpp"
#include "code/ylikuutio/triangulation/quad_triangulation.hpp"
#include "code/ylikuutio/common/globals.hpp"
#include "code/ylikuutio/common/global_variables.hpp"

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cmath>    // NAN, std::isnan, std::pow
#include <cstdio>   // std::FILE, std::fclose, std::fopen, std::fread, std::getchar, std::printf etc.
#include <cstring>  // std::memcmp, std::strcmp, std::strlen, std::strncmp
#include <iostream> // std::cout, std::cin, std::cerr
#include <stdint.h> // uint32_t etc.
#include <string>   // std::string
#include <vector>   // std::vector

namespace loaders
{
    bool load_BMP_world(
            std::string image_path,
            std::vector<glm::vec3>& out_vertices,
            std::vector<glm::vec2>& out_UVs,
            std::vector<glm::vec3>& out_normals,
            uint32_t &image_width,
            uint32_t &image_height,
            std::string color_channel,
            uint32_t x_step,
            uint32_t z_step,
            std::string triangulation_type)
    {
        std::cout << "Loading BMP file " << image_path << " ...\n";

        uint8_t header_size = 54;

        // Data read from the header of the BMP file
        uint8_t header[header_size];
        uint32_t image_size;
        // Actual RGB image data.
        uint8_t *image_data;

        // Open the file
        const char* char_image_path = image_path.c_str();
        std::FILE* file = std::fopen(char_image_path, "rb");
        if (!file)
        {
            std::cerr << image_path << " could not be opened.\n";
            return false;
        }

        // Read the header, i.e. the 54 first bytes

        // If less than 54 bytes are read, it's a problem.
        if (std::fread(header, 1, sizeof(header), file) != sizeof(header))
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // A BMP files always begins with "BM"
        if ((header[0] != 'B') || (header[1] != 'M'))
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // Make sure this is a 24bpp file
        if (*(uint32_t*) &header[0x1e] != 0)
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        if (*(uint32_t*) &header[0x1c] != 24)
        {
            std::cerr << "not a correct BMP file.\n";
            std::fclose(file);
            return false;
        }

        // Read the information about the image
        image_size = *(uint32_t*) &header[0x22];
        image_width = *(uint32_t*) &header[0x12];
        image_height = *(uint32_t*) &header[0x16];

        // Define world size.
        uint32_t world_size = image_width * image_height;

        // Some BMP files are misformatted, guess missing information
        if (image_size == 0)
        {
            image_size = image_width * image_height * 3; // 3 : one byte for each Red, Green and Blue component
        }

        // Create a buffer.
        image_data = new uint8_t[image_size];

        if (image_data == nullptr)
        {
            std::cerr << "Reserving memory for image data failed.\n";
            std::fclose(file);
            return false;
        }

        // Read the actual image data from the file into the buffer.
        std::fread(image_data, 1, sizeof(image_data), file);

        // Everything is in memory now, the file can be closed
        std::fclose(file);

        uint8_t *image_pointer;
        image_pointer = image_data;

        float* vertex_data;
        vertex_data = new float[world_size];

        if (vertex_data == nullptr)
        {
            std::cerr << "Reserving memory for vertex data failed.\n";
            delete image_data;
            return false;
        }

        float* vertex_pointer;
        vertex_pointer = vertex_data;

        const char* char_color_channel = color_channel.c_str();

        // start processing image_data.
        for (int32_t z = image_height - 1; z >= 0; z--)
        {
            uint32_t image_width_in_bytes = 3 * image_width;
            image_pointer = image_data + z * image_width_in_bytes;

            for (int32_t x = 0; x < image_width; x++)
            {
                uint32_t y;

                if (std::strcmp(char_color_channel, "blue") == 0)
                {
                    y = static_cast<float>(*image_pointer);       // y-coordinate is the blue (B) value.
                }
                else if (std::strcmp(char_color_channel, "green") == 0)
                {
                    y = static_cast<float>(*(image_pointer + 1)); // y-coordinate is the green (G) value.
                }
                else if (std::strcmp(char_color_channel, "red") == 0)
                {
                    y = static_cast<float>(*(image_pointer + 2)); // y-coordinate is the red (R) value.
                }
                // y-coordinate is the mean of R, G, & B.
                else if ((std::strcmp(char_color_channel, "mean") == 0) || (std::strcmp(char_color_channel, "all") == 0))
                {
                    y = (static_cast<float>(*image_pointer) + static_cast<float>(*(image_pointer + 1)) + static_cast<float>(*(image_pointer + 2))) / 3.0f;
                }
                else
                {
                    std::cerr << "invalid color channel!\n";
                    delete image_data;
                    delete vertex_data;
                    return false;
                }

                // std::cout << color_channel << " color channel value at (" << x << ", " << z << "): " << y << ".\n";
                *vertex_pointer++ = y;
                image_pointer += 3; // R, G, & B.
            }
        }

        delete image_data;

        std::cout << "color channel in use: " << color_channel << "\n";

        TriangulateQuadsStruct triangulate_quads_struct;
        triangulate_quads_struct.image_width = image_width;
        triangulate_quads_struct.image_height = image_height;
        triangulate_quads_struct.x_step = x_step;
        triangulate_quads_struct.z_step = z_step;
        triangulate_quads_struct.triangulation_type = triangulation_type;
        triangulate_quads_struct.sphere_radius = NAN;
        triangulate_quads_struct.spherical_world_struct = SphericalWorldStruct(); // not used, but is needed in the function call.

        bool result = geometry::triangulate_quads(vertex_data, triangulate_quads_struct, out_vertices, out_UVs, out_normals);
        delete vertex_data;
        return result;
    }
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkTernaryMagnitudeImageFilter.h"

int itkTernaryMagnitudeImageFilterTest(int, char* [] )
{

  // Define the dimension of the images
  const unsigned int myDimension = 3;

  // Declare the types of the images
  typedef itk::Image<float, myDimension>  myImageType1;
  typedef itk::Image<float, myDimension>  myImageType2;
  typedef itk::Image<float, myDimension>  myImageType3;
  typedef itk::Image<float, myDimension>  myImageType4;

  // Declare the type of the index to access images
  typedef itk::Index<myDimension>         myIndexType;

  // Declare the type of the size
  typedef itk::Size<myDimension>          mySizeType;

  // Declare the type of the Region
  typedef itk::ImageRegion<myDimension>        myRegionType;

  // Create two images
  myImageType1::Pointer inputImageA  = myImageType1::New();
  myImageType2::Pointer inputImageB  = myImageType2::New();
  myImageType3::Pointer inputImageC  = myImageType3::New();

  // Define their size, and start index
  mySizeType size;
  size[0] = 2;
  size[1] = 2;
  size[2] = 2;

  myIndexType start;
  start[0] = 0;
  start[1] = 0;
  start[2] = 0;

  myRegionType region;
  region.SetIndex( start );
  region.SetSize( size );

  // Initialize Image A
  inputImageA->SetLargestPossibleRegion( region );
  inputImageA->SetBufferedRegion( region );
  inputImageA->SetRequestedRegion( region );
  inputImageA->Allocate();

  // Initialize Image B
  inputImageB->SetLargestPossibleRegion( region );
  inputImageB->SetBufferedRegion( region );
  inputImageB->SetRequestedRegion( region );
  inputImageB->Allocate();

  // Initialize Image C
  inputImageC->SetLargestPossibleRegion( region );
  inputImageC->SetBufferedRegion( region );
  inputImageC->SetRequestedRegion( region );
  inputImageC->Allocate();

  // Declare Iterator types apropriated for each image
  typedef itk::ImageRegionIteratorWithIndex<myImageType1>  myIteratorType1;
  typedef itk::ImageRegionIteratorWithIndex<myImageType2>  myIteratorType2;
  typedef itk::ImageRegionIteratorWithIndex<myImageType3>  myIteratorType3;
  typedef itk::ImageRegionIteratorWithIndex<myImageType4>  myIteratorType4;

  // Create one iterator for Image A (this is a light object)
  myIteratorType1 it1( inputImageA, inputImageA->GetBufferedRegion() );

  // Initialize the content of Image A
  std::cout << "First operand " << std::endl;
  while( !it1.IsAtEnd() )
  {
    it1.Set( 2.0 );
    std::cout << it1.Get() << std::endl;
    ++it1;
  }

  // Create one iterator for Image B (this is a light object)
  myIteratorType2 it2( inputImageB, inputImageB->GetBufferedRegion() );

  // Initialize the content of Image B
  std::cout << "Second operand " << std::endl;
  while( !it2.IsAtEnd() )
  {
    it2.Set( 3.0 );
    std::cout << it2.Get() << std::endl;
    ++it2;
  }

  // Create one iterator for Image C (this is a light object)
  myIteratorType3 it3( inputImageC, inputImageC->GetBufferedRegion() );

  // Initialize the content of Image C
  std::cout << "Third operand " << std::endl;
  while( !it3.IsAtEnd() )
  {
    it3.Set( 4.0 );
    std::cout << it3.Get() << std::endl;
    ++it3;
  }

  // Declare the type for the Magnitude Filter
  typedef itk::TernaryMagnitudeImageFilter<
                                myImageType1,
                                myImageType2,
                                myImageType3,
                                myImageType4  >       myFilterType;

  // Create a MagnitudeImageFilter
  myFilterType::Pointer filter = myFilterType::New();

  // Connect the input images
  filter->SetInput1( inputImageA );
  filter->SetInput2( inputImageB );
  filter->SetInput3( inputImageC );

  // Get the Smart Pointer to the Filter Output
  myImageType4::Pointer outputImage = filter->GetOutput();

  // Execute the filter
  filter->Update();
  filter->SetFunctor(filter->GetFunctor());

  // Create an iterator for going through the image output
  myIteratorType4 it4(outputImage, outputImage->GetBufferedRegion());

  //  Print the content of the result image
  std::cout << " Result " << std::endl;
  while( !it4.IsAtEnd() )
  {
    std::cout << it4.Get() << std::endl;
    ++it4;
  }

  // All objects should be automatically destroyed at this point
  return EXIT_SUCCESS;

}
<commit_msg>ENH: Improve TernaryMagnitudeImageFilter coverage.<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/

#include "itkTernaryMagnitudeImageFilter.h"
#include "itkTestingMacros.h"

int itkTernaryMagnitudeImageFilterTest( int, char* [] )
{
  // Define the dimension of the images
  const unsigned int Dimension = 3;

  // Define the pixel types
  typedef float PixelType;

  // Declare the types of the images
  typedef itk::Image< PixelType, Dimension > InputImage1Type;
  typedef itk::Image< PixelType, Dimension > InputImage2Type;
  typedef itk::Image< PixelType, Dimension > InputImage3Type;
  typedef itk::Image< PixelType, Dimension > OutputImageType;

  // Declare the type of the index to access images
  typedef itk::Index< Dimension >         IndexType;

  // Declare the type of the size
  typedef itk::Size< Dimension >          SizeType;

  // Declare the type of the Region
  typedef itk::ImageRegion< Dimension >   RegionType;

  // Create the input images
  InputImage1Type::Pointer inputImageA = InputImage1Type::New();
  InputImage2Type::Pointer inputImageB = InputImage2Type::New();
  InputImage3Type::Pointer inputImageC = InputImage3Type::New();

  // Define their size and start index
  SizeType size;
  size[0] = 2;
  size[1] = 2;
  size[2] = 2;

  IndexType start;
  start[0] = 0;
  start[1] = 0;
  start[2] = 0;

  RegionType region;
  region.SetIndex( start );
  region.SetSize( size );

  // Initialize Image A
  inputImageA->SetLargestPossibleRegion( region );
  inputImageA->SetBufferedRegion( region );
  inputImageA->SetRequestedRegion( region );
  inputImageA->Allocate();

  // Initialize Image B
  inputImageB->SetLargestPossibleRegion( region );
  inputImageB->SetBufferedRegion( region );
  inputImageB->SetRequestedRegion( region );
  inputImageB->Allocate();

  // Initialize Image C
  inputImageC->SetLargestPossibleRegion( region );
  inputImageC->SetBufferedRegion( region );
  inputImageC->SetRequestedRegion( region );
  inputImageC->Allocate();

  // Declare Iterator types for each image
  typedef itk::ImageRegionIteratorWithIndex< InputImage1Type > InputImage1IteratorType;
  typedef itk::ImageRegionIteratorWithIndex< InputImage2Type > InputImage2IteratorType;
  typedef itk::ImageRegionIteratorWithIndex< InputImage3Type > InputImage3IteratorType;
  typedef itk::ImageRegionIteratorWithIndex< OutputImageType > OutputImageIteratorType;

  // Create one iterator for Image A (this is a light object)
  InputImage1IteratorType it1( inputImageA, inputImageA->GetBufferedRegion() );

  // Initialize the content of Image A
  std::cout << "First operand " << std::endl;
  InputImage1Type::PixelType valueA = 2.0;
  while( !it1.IsAtEnd() )
  {
    it1.Set( valueA );
    std::cout
      << itk::NumericTraits< InputImage1Type::PixelType >::PrintType( it1.Get() )
      << std::endl;
    ++it1;
  }

  // Create one iterator for Image B (this is a light object)
  InputImage2IteratorType it2( inputImageB, inputImageB->GetBufferedRegion() );

  // Initialize the content of Image B
  std::cout << "Second operand " << std::endl;
  InputImage2Type::PixelType valueB = 3.0;
  while( !it2.IsAtEnd() )
  {
    it2.Set( valueB );
    std::cout
      << itk::NumericTraits< InputImage2Type::PixelType >::PrintType( it2.Get() )
      << std::endl;
    ++it2;
  }

  // Create one iterator for Image C (this is a light object)
  InputImage3IteratorType it3( inputImageC, inputImageC->GetBufferedRegion() );

  // Initialize the content of Image C
  std::cout << "Third operand " << std::endl;
  InputImage3Type::PixelType valueC = 4.0;
  while( !it3.IsAtEnd() )
  {
    it3.Set( valueC );
    std::cout
      << itk::NumericTraits< InputImage3Type::PixelType >::PrintType( it3.Get() )
      << std::endl;
    ++it3;
  }

  typedef itk::TernaryMagnitudeImageFilter<
    InputImage1Type,
    InputImage2Type,
    InputImage3Type,
    OutputImageType > TernaryMagnitudeImageFilterType;

  // Create the filter
  TernaryMagnitudeImageFilterType::Pointer filter =
    TernaryMagnitudeImageFilterType::New();

  EXERCISE_BASIC_OBJECT_METHODS( filter, TernaryMagnitudeImageFilter,
    TernaryFunctorImageFilter );

  // Set the input images
  filter->SetInput1( inputImageA );
  filter->SetInput2( inputImageB );
  filter->SetInput3( inputImageC );

  filter->SetFunctor( filter->GetFunctor() );

  // Execute the filter
  filter->Update();

  // Get the filter output
  OutputImageType::Pointer outputImage = filter->GetOutput();

  // Create an iterator for going through the image output
  OutputImageIteratorType it4( outputImage, outputImage->GetBufferedRegion() );

  // Print the content of the result image
  std::cout << "Result " << std::endl;
  while( !it4.IsAtEnd() )
  {
    std::cout
      << itk::NumericTraits< OutputImageType::PixelType >::PrintType( it4.Get() )
      << std::endl;
    ++it4;
  }

  // All objects should be automatically destroyed at this point
  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>#include "fbx_symbiosis_loader.hpp"
#include "code/ylikuutio/file/file_loader.hpp"
#include <ofbx.h>

// OpenFBX wants `u8` == `unsigned char`.
typedef unsigned char u8;

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cstddef>       // std::size_t
#include <iostream>      // std::cout, std::cin, std::cerr
#include <stdint.h>      // uint32_t etc.
#include <string>        // std::string
#include <unordered_map> // std::unordered_map
#include <vector>        // std::vector

namespace loaders
{
    bool load_FBX(
            const std::string& filename,
            std::vector<std::vector<glm::vec3>>& out_vertices,
            std::vector<std::vector<glm::vec2>>& out_uvs,
            std::vector<std::vector<glm::vec3>>& out_normals,
            std::unordered_map<const ofbx::Texture*, std::vector<int32_t>>& ofbx_diffuse_texture_mesh_map,
            std::vector<const ofbx::Mesh*>& ofbx_meshes,
            std::vector<const ofbx::Texture*>& ofbx_diffuse_texture_vector,
            std::vector<const ofbx::Texture*>& ofbx_normal_texture_vector,
            std::vector<const ofbx::Texture*>& ofbx_count_texture_vector,
            std::size_t& mesh_count,
            const bool is_debug_mode)
    {
        // Functions and data of interest in OpenFBX:
        // struct TakeInfo
        // {
        //     DataView name;
        //     DataView filename;
        //     double local_time_from;
        //     double local_time_to;
        //     double reference_time_from;
        //     double reference_time_to;
        // };
        //
        // IScene* load(const u8* data, int size)
        const std::vector<uint8_t> data_vector = yli::file::binary_slurp(filename);

        // OpenFBX wants `u8` == `unsigned char`.
        const u8* data = reinterpret_cast<const u8*>(data_vector.data());
        const int64_t size = data_vector.size();

        if (is_debug_mode)
        {
            std::cout << "loaded FBX data vector size: " << size << "\n";
        }

        const ofbx::IScene* ofbx_iscene = ofbx::load(data, size);

        if (ofbx_iscene == nullptr)
        {
            std::cerr << "Error: ofbx_iscene is nullptr!\n";
            return false;
        }

        int temp_mesh_count = ofbx_iscene->getMeshCount(); // `getMeshCount()` returns `int`.

        if (temp_mesh_count < 0)
        {
            std::cerr << "Error: mesh count is negative!\n";
            return false;
        }

        mesh_count = static_cast<std::size_t>(temp_mesh_count);

        ofbx_meshes.reserve(mesh_count);

        for (std::size_t mesh_i = 0; mesh_i < mesh_count; mesh_i++)
        {
            const ofbx::Mesh* mesh = ofbx_iscene->getMesh(mesh_i);
            ofbx_meshes[mesh_i] = mesh;

            if (mesh == nullptr)
            {
                std::cerr << "Error: mesh is nullptr!\n";
                return false;
            }

            const ofbx::Geometry* geometry = mesh->getGeometry();

            if (geometry == nullptr)
            {
                std::cerr << "Error: geometry is nullptr!\n";
                return false;
            }

            // TODO: finalize the implementation of `yli::ontology::Symbiosis`
            // to be able to support for different materials!
            const int material_count = mesh->getMaterialCount(); // TODO: use this in  `yli::ontology::Symbiosis` entities!

            if (is_debug_mode)
            {
                std::cout << filename << ": mesh " << mesh_i << ": getMaterialCount(): " << material_count << "\n";
            }

            for (std::size_t material_i = 0; material_i < material_count; material_i++)
            {
                if (is_debug_mode)
                {
                    std::cout << "mesh " << mesh_i << ", material " << material_i << "\n";
                }

                const ofbx::Material* material = mesh->getMaterial(material_i);

                if (material == nullptr)
                {
                    // Material should not be `nullptr`.
                    continue;
                }

                const ofbx::Texture* diffuse_texture = material->getTexture(ofbx::Texture::DIFFUSE);

                if (diffuse_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", DIFFUSE texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", DIFFUSE texture at " << std::hex << (void*) diffuse_texture << std::dec << "\n";
                    }

                    // Add new texture to map.
                    if (ofbx_diffuse_texture_mesh_map.count(diffuse_texture) != 1)
                    {
                        // This `const ofbx::Material*` is not in `ofbx_diffuse_texture_mesh_map` yet.
                        ofbx_diffuse_texture_mesh_map[diffuse_texture] = std::vector<int32_t>();
                    }

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to DIFFUSE texture at " << std::hex << (void*) diffuse_texture << std::dec << "\n";
                    }

                    ofbx_diffuse_texture_mesh_map[diffuse_texture].push_back(mesh_i);

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to DIFFUSE textures.\n";
                    }

                    ofbx_diffuse_texture_vector.push_back(diffuse_texture);
                }

                const ofbx::Texture* normal_texture = material->getTexture(ofbx::Texture::NORMAL);

                if (normal_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", NORMAL texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", NORMAL texture at " << std::hex << (void*) normal_texture << std::dec << "\n";
                    }

                    // TODO: store NORMAL textures similarly as DIFFUSE textures.

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to NORMAL textures.\n";
                    }

                    ofbx_normal_texture_vector.push_back(normal_texture);
                }

                const ofbx::Texture* count_texture = material->getTexture(ofbx::Texture::COUNT);

                if (count_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", COUNT texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", COUNT texture at " << std::hex << (void*) count_texture << std::dec << "\n";
                    }

                    // TODO: store COUNT textures similarly as DIFFUSE textures.

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to COUNT textures.\n";
                    }
                    ofbx_count_texture_vector.push_back(count_texture);
                }
            }

            const int vertex_count = geometry->getVertexCount();

            if (is_debug_mode)
            {
                std::cout << filename << ": mesh " << mesh_i << ": getVertexCount(): " << vertex_count << "\n";
            }

            const ofbx::Vec3* vertices = geometry->getVertices();

            if (vertices == nullptr)
            {
                std::cerr << "Error: vertices is nullptr!\n";
                return false;
            }

            const ofbx::Vec3* normals = geometry->getNormals();

            if (normals == nullptr)
            {
                std::cerr << "Error: normals is nullptr!\n";
                return false;
            }

            std::vector<glm::vec3> mesh_out_vertices;

            for (std::size_t i = 0; i < vertex_count; i++)
            {
                // vertices.
                glm::vec3 vertex = { vertices[i].x, vertices[i].y, vertices[i].z };
                mesh_out_vertices.push_back(vertex);
            }

            out_vertices.push_back(mesh_out_vertices);

            std::vector<glm::vec2> mesh_out_uvs;

            const ofbx::Vec2* uvs = geometry->getUVs();

            if (uvs == nullptr)
            {
                // `uvs` should not be `nullptr`.
                std::cerr << "Error: uvs is nullptr!\n";
            }
            else
            {
                for (std::size_t vertex_i = 0; vertex_i < vertex_count; vertex_i++)
                {
                    // UVs.
                    glm::vec2 uv = { uvs[vertex_i].x, uvs[vertex_i].y };
                    mesh_out_uvs.push_back(uv);
                }
            }

            out_uvs.push_back(mesh_out_uvs);

            std::vector<glm::vec3> mesh_out_normals;

            for (std::size_t i = 0; i < vertex_count; i++)
            {
                // Normals.
                glm::vec3 normal = { normals[i].x, normals[i].y, normals[i].z };
                mesh_out_normals.push_back(normal);
            }

            out_normals.push_back(mesh_out_normals);
        }

        return true;
    }
}
<commit_msg>FBX `Symbiosis` loader: added `const` modifier to `temp_mesh_count`.<commit_after>#include "fbx_symbiosis_loader.hpp"
#include "code/ylikuutio/file/file_loader.hpp"
#include <ofbx.h>

// OpenFBX wants `u8` == `unsigned char`.
typedef unsigned char u8;

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp> // glm
#endif

// Include standard headers
#include <cstddef>       // std::size_t
#include <iostream>      // std::cout, std::cin, std::cerr
#include <stdint.h>      // uint32_t etc.
#include <string>        // std::string
#include <unordered_map> // std::unordered_map
#include <vector>        // std::vector

namespace loaders
{
    bool load_FBX(
            const std::string& filename,
            std::vector<std::vector<glm::vec3>>& out_vertices,
            std::vector<std::vector<glm::vec2>>& out_uvs,
            std::vector<std::vector<glm::vec3>>& out_normals,
            std::unordered_map<const ofbx::Texture*, std::vector<int32_t>>& ofbx_diffuse_texture_mesh_map,
            std::vector<const ofbx::Mesh*>& ofbx_meshes,
            std::vector<const ofbx::Texture*>& ofbx_diffuse_texture_vector,
            std::vector<const ofbx::Texture*>& ofbx_normal_texture_vector,
            std::vector<const ofbx::Texture*>& ofbx_count_texture_vector,
            std::size_t& mesh_count,
            const bool is_debug_mode)
    {
        // Functions and data of interest in OpenFBX:
        // struct TakeInfo
        // {
        //     DataView name;
        //     DataView filename;
        //     double local_time_from;
        //     double local_time_to;
        //     double reference_time_from;
        //     double reference_time_to;
        // };
        //
        // IScene* load(const u8* data, int size)
        const std::vector<uint8_t> data_vector = yli::file::binary_slurp(filename);

        // OpenFBX wants `u8` == `unsigned char`.
        const u8* data = reinterpret_cast<const u8*>(data_vector.data());
        const int64_t size = data_vector.size();

        if (is_debug_mode)
        {
            std::cout << "loaded FBX data vector size: " << size << "\n";
        }

        const ofbx::IScene* ofbx_iscene = ofbx::load(data, size);

        if (ofbx_iscene == nullptr)
        {
            std::cerr << "Error: ofbx_iscene is nullptr!\n";
            return false;
        }

        const int temp_mesh_count = ofbx_iscene->getMeshCount(); // `getMeshCount()` returns `int`.

        if (temp_mesh_count < 0)
        {
            std::cerr << "Error: mesh count is negative!\n";
            return false;
        }

        mesh_count = static_cast<std::size_t>(temp_mesh_count);

        ofbx_meshes.reserve(mesh_count);

        for (std::size_t mesh_i = 0; mesh_i < mesh_count; mesh_i++)
        {
            const ofbx::Mesh* mesh = ofbx_iscene->getMesh(mesh_i);
            ofbx_meshes[mesh_i] = mesh;

            if (mesh == nullptr)
            {
                std::cerr << "Error: mesh is nullptr!\n";
                return false;
            }

            const ofbx::Geometry* geometry = mesh->getGeometry();

            if (geometry == nullptr)
            {
                std::cerr << "Error: geometry is nullptr!\n";
                return false;
            }

            // TODO: finalize the implementation of `yli::ontology::Symbiosis`
            // to be able to support for different materials!
            const int material_count = mesh->getMaterialCount(); // TODO: use this in  `yli::ontology::Symbiosis` entities!

            if (is_debug_mode)
            {
                std::cout << filename << ": mesh " << mesh_i << ": getMaterialCount(): " << material_count << "\n";
            }

            for (std::size_t material_i = 0; material_i < material_count; material_i++)
            {
                if (is_debug_mode)
                {
                    std::cout << "mesh " << mesh_i << ", material " << material_i << "\n";
                }

                const ofbx::Material* material = mesh->getMaterial(material_i);

                if (material == nullptr)
                {
                    // Material should not be `nullptr`.
                    continue;
                }

                const ofbx::Texture* diffuse_texture = material->getTexture(ofbx::Texture::DIFFUSE);

                if (diffuse_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", DIFFUSE texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", DIFFUSE texture at " << std::hex << (void*) diffuse_texture << std::dec << "\n";
                    }

                    // Add new texture to map.
                    if (ofbx_diffuse_texture_mesh_map.count(diffuse_texture) != 1)
                    {
                        // This `const ofbx::Material*` is not in `ofbx_diffuse_texture_mesh_map` yet.
                        ofbx_diffuse_texture_mesh_map[diffuse_texture] = std::vector<int32_t>();
                    }

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to DIFFUSE texture at " << std::hex << (void*) diffuse_texture << std::dec << "\n";
                    }

                    ofbx_diffuse_texture_mesh_map[diffuse_texture].push_back(mesh_i);

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to DIFFUSE textures.\n";
                    }

                    ofbx_diffuse_texture_vector.push_back(diffuse_texture);
                }

                const ofbx::Texture* normal_texture = material->getTexture(ofbx::Texture::NORMAL);

                if (normal_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", NORMAL texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", NORMAL texture at " << std::hex << (void*) normal_texture << std::dec << "\n";
                    }

                    // TODO: store NORMAL textures similarly as DIFFUSE textures.

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to NORMAL textures.\n";
                    }

                    ofbx_normal_texture_vector.push_back(normal_texture);
                }

                const ofbx::Texture* count_texture = material->getTexture(ofbx::Texture::COUNT);

                if (count_texture == nullptr)
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", COUNT texture is nullptr\n";
                    }
                }
                else
                {
                    if (is_debug_mode)
                    {
                        std::cout << "mesh " << mesh_i << ", COUNT texture at " << std::hex << (void*) count_texture << std::dec << "\n";
                    }

                    // TODO: store COUNT textures similarly as DIFFUSE textures.

                    if (is_debug_mode)
                    {
                        std::cout << "Adding mesh " << mesh_i << " to COUNT textures.\n";
                    }
                    ofbx_count_texture_vector.push_back(count_texture);
                }
            }

            const int vertex_count = geometry->getVertexCount();

            if (is_debug_mode)
            {
                std::cout << filename << ": mesh " << mesh_i << ": getVertexCount(): " << vertex_count << "\n";
            }

            const ofbx::Vec3* vertices = geometry->getVertices();

            if (vertices == nullptr)
            {
                std::cerr << "Error: vertices is nullptr!\n";
                return false;
            }

            const ofbx::Vec3* normals = geometry->getNormals();

            if (normals == nullptr)
            {
                std::cerr << "Error: normals is nullptr!\n";
                return false;
            }

            std::vector<glm::vec3> mesh_out_vertices;

            for (std::size_t i = 0; i < vertex_count; i++)
            {
                // vertices.
                glm::vec3 vertex = { vertices[i].x, vertices[i].y, vertices[i].z };
                mesh_out_vertices.push_back(vertex);
            }

            out_vertices.push_back(mesh_out_vertices);

            std::vector<glm::vec2> mesh_out_uvs;

            const ofbx::Vec2* uvs = geometry->getUVs();

            if (uvs == nullptr)
            {
                // `uvs` should not be `nullptr`.
                std::cerr << "Error: uvs is nullptr!\n";
            }
            else
            {
                for (std::size_t vertex_i = 0; vertex_i < vertex_count; vertex_i++)
                {
                    // UVs.
                    glm::vec2 uv = { uvs[vertex_i].x, uvs[vertex_i].y };
                    mesh_out_uvs.push_back(uv);
                }
            }

            out_uvs.push_back(mesh_out_uvs);

            std::vector<glm::vec3> mesh_out_normals;

            for (std::size_t i = 0; i < vertex_count; i++)
            {
                // Normals.
                glm::vec3 normal = { normals[i].x, normals[i].y, normals[i].z };
                mesh_out_normals.push_back(normal);
            }

            out_normals.push_back(mesh_out_normals);
        }

        return true;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef otbWrapperInputImageParameter_hxx
#define otbWrapperInputImageParameter_hxx

#include "otbWrapperInputImageParameter.h"

#include "otbClampImageFilter.h"

namespace otb
{
namespace Wrapper
{

#define CLAMP_IMAGE_IF( Out, In, image_base )	\
  {							\
    In * in_image = dynamic_cast< In * >( image_base );	\
							\
    if( in_image )						\
      return Cast< Out, In >( in_image );			\
  }

#define CLAMP_IMAGE_BASE( T, image_base )				\
  {									\
    CLAMP_IMAGE_IF( T, UInt8VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int16VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt16VectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, Int32VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt32VectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, FloatVectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, DoubleVectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexInt16VectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexInt32VectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexFloatVectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexDoubleVectorImageType, image_base );	\
    									\
    CLAMP_IMAGE_IF( T, UInt8RGBImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt8RGBAImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, UInt8ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int16ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt16ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int32ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt32ImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, FloatImageType, image_base );		\
    CLAMP_IMAGE_IF( T, DoubleImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, ComplexInt16ImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexInt32ImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexFloatImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexDoubleImageType, image_base );	\
									\
    return nullptr;							\
  }

template< typename TOutputImage,
	  typename TInputImage >
TOutputImage *
InputImageParameter
::Cast( TInputImage * image )
{
  // Optimize pipeline if input-image can be directly cast into
  // output-image.
  CLAMP_IMAGE_IF( TOutputImage, TInputImage, image );

  // Allocate and link pipeline in local scope before overwriting
  // member attributes in order to ensure exception safety.

  auto icif = InputClampImageFilter< TInputImage >::New();

  auto ocif = OutputClampImageFilter< TOutputImage >::New();

  icif->SetInput( image );

  ocif->SetInput( icif->GetOutput() );

  ocif->UpdateOutputInformation();

  m_InputCaster = icif;
  m_OutputCaster = ocif;

  return ocif->GetOutput();
}

template <class TImageType>
TImageType*
InputImageParameter::GetImage()
{
  // Used m_PreviousFileName because if not, when the user call twice GetImage,
  // it without changing the filename, it returns 2 different
  // image pointers
  // Only one image type can be used

  // 2 cases : the user set a filename vs. the user set an image
  if( m_UseFilename )
    {
    if( m_PreviousFileName!=m_FileName &&
	!m_FileName.empty() )
      {
      //////////////////////// Filename case:
      // A new valid filename has been given : a reader is created
      typedef otb::ImageFileReader<TImageType> ReaderType;

      typename ReaderType::Pointer reader = ReaderType::New();

      reader->SetFileName( m_FileName );

      reader->UpdateOutputInformation();

      m_Image = reader->GetOutput();
      m_Reader = reader;

      m_PreviousFileName = m_FileName;

      // Pay attention, don't return m_Image because it is a ImageBase...
      return reader->GetOutput();
      }
    else
      {
      // In this case, the reader and the image should already be there
      if (m_Image.IsNull())
        {
        itkExceptionMacro("No input image or filename detected...");
        }
      else
        {
        // Check if the image type asked here is the same as the one used for the reader
        if (dynamic_cast<TImageType*> (m_Image.GetPointer()))
          {
          return dynamic_cast<TImageType*> (m_Image.GetPointer());
          }
        else
          {
          itkExceptionMacro("Cannot ask a different image type");
          }
        }
      }
    }

  else
    {
    //////////////////////// Image case:
    if (m_Image.IsNull())
      {
      return nullptr;
      }
    else
      CLAMP_IMAGE_BASE( TImageType, m_Image.GetPointer() );
    }
}

/** declare a specialization for ImageBaseType */
template <>
OTBApplicationEngine_EXPORT
ImageBaseType*
InputImageParameter::GetImage<ImageBaseType>();

} // End namespace Wrapper
} // End namespace otb

#endif
<commit_msg>ENH: 1649: Fixed stack overflow crash due to infinite recursion.<commit_after>/*
 * Copyright (C) 2005-2019 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef otbWrapperInputImageParameter_hxx
#define otbWrapperInputImageParameter_hxx

#include "otbWrapperInputImageParameter.h"

#include "otbClampImageFilter.h"

namespace otb
{
namespace Wrapper
{

#define CLAMP_IMAGE_IF( Out, In, image_base )	\
  {							\
    In * in_image = dynamic_cast< In * >( image_base );	\
							\
    if( in_image )						\
      return Cast< Out, In >( in_image );			\
  }

#define CLAMP_IMAGE_BASE( T, image_base )				\
  {									\
    CLAMP_IMAGE_IF( T, UInt8VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int16VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt16VectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, Int32VectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt32VectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, FloatVectorImageType, image_base );		\
    CLAMP_IMAGE_IF( T, DoubleVectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexInt16VectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexInt32VectorImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexFloatVectorImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexDoubleVectorImageType, image_base );	\
    									\
    CLAMP_IMAGE_IF( T, UInt8RGBImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt8RGBAImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, UInt8ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int16ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt16ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, Int32ImageType, image_base );		\
    CLAMP_IMAGE_IF( T, UInt32ImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, FloatImageType, image_base );		\
    CLAMP_IMAGE_IF( T, DoubleImageType, image_base );		\
									\
    CLAMP_IMAGE_IF( T, ComplexInt16ImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexInt32ImageType, image_base );	\
									\
    CLAMP_IMAGE_IF( T, ComplexFloatImageType, image_base );	\
    CLAMP_IMAGE_IF( T, ComplexDoubleImageType, image_base );	\
									\
    return nullptr;							\
  }

template< typename TOutputImage,
	  typename TInputImage >
TOutputImage *
InputImageParameter
::Cast( TInputImage * image )
{
  // Optimize pipeline if input-image can be directly cast into
  // output-image.
  {
    auto img = dynamic_cast< TOutputImage * >( image );

    if( img )
      return img;
  }

  // Allocate and link pipeline in local scope before overwriting
  // member attributes in order to ensure exception safety.

  auto icif = InputClampImageFilter< TInputImage >::New();

  auto ocif = OutputClampImageFilter< TOutputImage >::New();

  icif->SetInput( image );

  ocif->SetInput( icif->GetOutput() );

  ocif->UpdateOutputInformation();

  m_InputCaster = icif;
  m_OutputCaster = ocif;

  return ocif->GetOutput();
}

template <class TImageType>
TImageType*
InputImageParameter::GetImage()
{
  // Used m_PreviousFileName because if not, when the user call twice GetImage,
  // it without changing the filename, it returns 2 different
  // image pointers
  // Only one image type can be used

  // 2 cases : the user set a filename vs. the user set an image
  if( m_UseFilename )
    {
    if( m_PreviousFileName!=m_FileName &&
	!m_FileName.empty() )
      {
      //////////////////////// Filename case:
      // A new valid filename has been given : a reader is created
      typedef otb::ImageFileReader<TImageType> ReaderType;

      typename ReaderType::Pointer reader = ReaderType::New();

      reader->SetFileName( m_FileName );

      reader->UpdateOutputInformation();

      m_Image = reader->GetOutput();
      m_Reader = reader;

      m_PreviousFileName = m_FileName;

      // Pay attention, don't return m_Image because it is a ImageBase...
      return reader->GetOutput();
      }
    else
      {
      // In this case, the reader and the image should already be there
      if (m_Image.IsNull())
        {
        itkExceptionMacro("No input image or filename detected...");
        }
      else
        {
        // Check if the image type asked here is the same as the one used for the reader
        if (dynamic_cast<TImageType*> (m_Image.GetPointer()))
          {
          return dynamic_cast<TImageType*> (m_Image.GetPointer());
          }
        else
          {
          itkExceptionMacro("Cannot ask a different image type");
          }
        }
      }
    }

  else
    {
    //////////////////////// Image case:
    if (m_Image.IsNull())
      {
      return nullptr;
      }
    else
      CLAMP_IMAGE_BASE( TImageType, m_Image.GetPointer() );
    }
}

/** declare a specialization for ImageBaseType */
template <>
OTBApplicationEngine_EXPORT
ImageBaseType*
InputImageParameter::GetImage<ImageBaseType>();

} // End namespace Wrapper
} // End namespace otb

#endif
<|endoftext|>


<commit_before><commit_msg>New test for `yli::config::Setting`.<commit_after><|endoftext|>