/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
// GDAL Macros
#include "cvconfig.h"
#ifdef HAVE_GDAL
// Our Header
#include "grfmt_gdal.hpp"
/// C++ Standard Libraries
#include <iostream>
#include <stdexcept>
#include <string>
namespace cv{
/**
* Convert GDAL Palette Interpretation to OpenCV Pixel Type
*/
int gdalPaletteInterpretation2OpenCV( GDALPaletteInterp const& paletteInterp, GDALDataType const& gdalType ){
switch( paletteInterp ){
/// GRAYSCALE
case GPI_Gray:
if( gdalType == GDT_Byte ){ return CV_8UC1; }
if( gdalType == GDT_UInt16 ){ return CV_16UC1; }
if( gdalType == GDT_Int16 ){ return CV_16SC1; }
if( gdalType == GDT_UInt32 ){ return CV_32SC1; }
if( gdalType == GDT_Int32 ){ return CV_32SC1; }
if( gdalType == GDT_Float32 ){ return CV_32FC1; }
if( gdalType == GDT_Float64 ){ return CV_64FC1; }
return -1;
/// RGB
case GPI_RGB:
if( gdalType == GDT_Byte ){ return CV_8UC1; }
if( gdalType == GDT_UInt16 ){ return CV_16UC3; }
if( gdalType == GDT_Int16 ){ return CV_16SC3; }
if( gdalType == GDT_UInt32 ){ return CV_32SC3; }
if( gdalType == GDT_Int32 ){ return CV_32SC3; }
if( gdalType == GDT_Float32 ){ return CV_32FC3; }
if( gdalType == GDT_Float64 ){ return CV_64FC3; }
return -1;
/// otherwise
default:
return -1;
}
}
/**
* Convert gdal type to opencv type
*/
int gdal2opencv( const GDALDataType& gdalType, const int& channels ){
switch( gdalType ){
/// UInt8
case GDT_Byte:
if( channels == 1 ){ return CV_8UC1; }
if( channels == 3 ){ return CV_8UC3; }
if( channels == 4 ){ return CV_8UC4; }
return -1;
/// UInt16
case GDT_UInt16:
if( channels == 1 ){ return CV_16UC1; }
if( channels == 3 ){ return CV_16UC3; }
if( channels == 4 ){ return CV_16UC4; }
return -1;
/// Int16
case GDT_Int16:
if( channels == 1 ){ return CV_16SC1; }
if( channels == 3 ){ return CV_16SC3; }
if( channels == 4 ){ return CV_16SC4; }
return -1;
/// UInt32
case GDT_UInt32:
case GDT_Int32:
if( channels == 1 ){ return CV_32SC1; }
if( channels == 3 ){ return CV_32SC3; }
if( channels == 4 ){ return CV_32SC4; }
return -1;
default:
std::cout << "Unknown GDAL Data Type" << std::endl;
std::cout << "Type: " << GDALGetDataTypeName(gdalType) << std::endl;
return -1;
}
return -1;
}
/**
* GDAL Decoder Constructor
*/
GdalDecoder::GdalDecoder(){
// set a dummy signature
m_signature="0";
for( size_t i=0; i<160; i++ ){
m_signature += "0";
}
/// Register the driver
GDALAllRegister();
m_driver = NULL;
m_dataset = NULL;
}
/**
* GDAL Decoder Destructor
*/
GdalDecoder::~GdalDecoder(){
if( m_dataset != NULL ){
close();
}
}
/**
* Convert data range
*/
double range_cast( const GDALDataType& gdalType,
const int& cvDepth,
const double& value )
{
// uint8 -> uint8
if( gdalType == GDT_Byte && cvDepth == CV_8U ){
return value;
}
// uint8 -> uint16
if( gdalType == GDT_Byte && (cvDepth == CV_16U || cvDepth == CV_16S)){
return (value*256);
}
// uint8 -> uint32
if( gdalType == GDT_Byte && (cvDepth == CV_32F || cvDepth == CV_32S)){
return (value*16777216);
}
// int16 -> uint8
if( (gdalType == GDT_UInt16 || gdalType == GDT_Int16) && cvDepth == CV_8U ){
return std::floor(value/256.0);
}
// int16 -> int16
if( (gdalType == GDT_UInt16 || gdalType == GDT_Int16) &&
( cvDepth == CV_16U || cvDepth == CV_16S )){
return value;
}
std::cout << GDALGetDataTypeName( gdalType ) << std::endl;
std::cout << "warning: unknown range cast requested." << std::endl;
return (value);
}
/**
* There are some better mpl techniques for doing this.
*/
void write_pixel( const double& pixelValue,
const GDALDataType& gdalType,
const int& gdalChannels,
Mat& image,
const int& row,
const int& col,
const int& channel ){
// convert the pixel
double newValue = range_cast(gdalType, image.depth(), pixelValue );
// input: 1 channel, output: 1 channel
if( gdalChannels == 1 && image.channels() == 1 ){
if( image.depth() == CV_8U ){ image.at<uchar>(row,col) = newValue; }
else if( image.depth() == CV_16U ){ image.at<unsigned short>(row,col) = newValue; }
else if( image.depth() == CV_16S ){ image.at<short>(row,col) = newValue; }
else if( image.depth() == CV_32S ){ image.at<int>(row,col) = newValue; }
else if( image.depth() == CV_32F ){ image.at<float>(row,col) = newValue; }
else if( image.depth() == CV_64F ){ image.at<double>(row,col) = newValue; }
else{ throw std::runtime_error("Unknown image depth, gdal: 1, img: 1"); }
}
// input: 1 channel, output: 3 channel
else if( gdalChannels == 1 && image.channels() == 3 ){
if( image.depth() == CV_8U ){ image.at<Vec3b>(row,col) = Vec3b(newValue,newValue,newValue); }
else if( image.depth() == CV_16U ){ image.at<Vec3s>(row,col) = Vec3s(newValue,newValue,newValue); }
else if( image.depth() == CV_16S ){ image.at<Vec3s>(row,col) = Vec3s(newValue,newValue,newValue); }
else if( image.depth() == CV_32S ){ image.at<Vec3i>(row,col) = Vec3i(newValue,newValue,newValue); }
else if( image.depth() == CV_32F ){ image.at<Vec3f>(row,col) = Vec3f(newValue,newValue,newValue); }
else if( image.depth() == CV_64F ){ image.at<Vec3d>(row,col) = Vec3d(newValue,newValue,newValue); }
else{ throw std::runtime_error("Unknown image depth, gdal:1, img: 3"); }
}
// input: 3 channel, output: 1 channel
else if( gdalChannels == 3 && image.channels() == 1 ){
if( image.depth() == CV_8U ){ image.at<uchar>(row,col) += (newValue/3.0); }
else{ throw std::runtime_error("Unknown image depth, gdal:3, img: 1"); }
}
// input: 4 channel, output: 1 channel
else if( gdalChannels == 4 && image.channels() == 1 ){
if( image.depth() == CV_8U ){ image.at<uchar>(row,col) = newValue; }
else{ throw std::runtime_error("Unknown image depth, gdal: 4, image: 1"); }
}
// input: 3 channel, output: 3 channel
else if( gdalChannels == 3 && image.channels() == 3 ){
if( image.depth() == CV_8U ){ image.at<Vec3b>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16U ){ image.at<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16S ){ image.at<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32S ){ image.at<Vec3i>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32F ){ image.at<Vec3f>(row,col)[channel] = newValue; }
else if( image.depth() == CV_64F ){ image.at<Vec3d>(row,col)[channel] = newValue; }
else{ throw std::runtime_error("Unknown image depth, gdal: 3, image: 3"); }
}
// input: 4 channel, output: 3 channel
else if( gdalChannels == 4 && image.channels() == 3 ){
if( channel >= 4 ){ return; }
else if( image.depth() == CV_8U && channel < 4 ){ image.at<Vec3b>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16U && channel < 4 ){ image.at<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_16S && channel < 4 ){ image.at<Vec3s>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32S && channel < 4 ){ image.at<Vec3i>(row,col)[channel] = newValue; }
else if( image.depth() == CV_32F && channel < 4 ){ image.at<Vec3f>(row,col)[channel] = newValue; }
else if( image.depth() == CV_64F && channel < 4 ){ image.at<Vec3d>(row,col)[channel] = newValue; }
else{ throw std::runtime_error("Unknown image depth, gdal: 4, image: 3"); }
}
// input: 4 channel, output: 4 channel
else if( gdalChannels == 4 && image.channels() == 4 ){
if( image.depth() == CV_8U ){ image.at<Vec4b>(row,col)[channel] = newValue; }
else{ throw std::runtime_error("Unknown image depth, gdal: 4, image: 4"); }
}
// otherwise, throw an error
else{
throw std::runtime_error("error: can't convert types.");
}
}
void write_ctable_pixel( const double& pixelValue,
const GDALDataType& gdalType,
GDALColorTable const* gdalColorTable,
Mat& image,
const int& y,
const int& x,
const int& c ){
if( gdalColorTable == NULL ){
write_pixel( pixelValue, gdalType, 1, image, y, x, c );
}
// if we are Grayscale, then do a straight conversion
if( gdalColorTable->GetPaletteInterpretation() == GPI_Gray ){
write_pixel( pixelValue, gdalType, 1, image, y, x, c );
}
// if we are rgb, then convert here
else if( gdalColorTable->GetPaletteInterpretation() == GPI_RGB ){
// get the pixel
short r = gdalColorTable->GetColorEntry( (int)pixelValue )->c1;
short g = gdalColorTable->GetColorEntry( (int)pixelValue )->c2;
short b = gdalColorTable->GetColorEntry( (int)pixelValue )->c3;
short a = gdalColorTable->GetColorEntry( (int)pixelValue )->c4;
write_pixel( r, gdalType, 4, image, y, x, 2 );
write_pixel( g, gdalType, 4, image, y, x, 1 );
write_pixel( b, gdalType, 4, image, y, x, 0 );
if( image.channels() > 3 ){
write_pixel( a, gdalType, 4, image, y, x, 1 );
}
}
// otherwise, set zeros
else{
write_pixel( pixelValue, gdalType, 1, image, y, x, c );
}
}
/**
* read data
*/
bool GdalDecoder::readData( Mat& img ){
// make sure the image is the proper size
if( img.size().height != m_height ){
return false;
}
if( img.size().width != m_width ){
return false;
}
// make sure the raster is alive
if( m_dataset == NULL || m_driver == NULL ){
return false;
}
// set the image to zero
img = 0;
// iterate over each raster band
// note that OpenCV does bgr rather than rgb
int nChannels = m_dataset->GetRasterCount();
GDALColorTable* gdalColorTable = NULL;
if( m_dataset->GetRasterBand(1)->GetColorTable() != NULL ){
gdalColorTable = m_dataset->GetRasterBand(1)->GetColorTable();
}
const GDALDataType gdalType = m_dataset->GetRasterBand(1)->GetRasterDataType();
int nRows, nCols;
if( nChannels > img.channels() ){
nChannels = img.channels();
}
for( int c = 0; c<nChannels; c++ ){
// get the GDAL Band
GDALRasterBand* band = m_dataset->GetRasterBand(c+1);
// make sure the image band has the same dimensions as the image
if( band->GetXSize() != m_width || band->GetYSize() != m_height ){ return false; }
// grab the raster size
nRows = band->GetYSize();
nCols = band->GetXSize();
// create a temporary scanline pointer to store data
double* scanline = new double[nCols];
// iterate over each row and column
for( int y=0; y<nRows; y++ ){
// get the entire row
band->RasterIO( GF_Read, 0, y, nCols, 1, scanline, nCols, 1, GDT_Float64, 0, 0);
// set inside the image
for( int x=0; x<nCols; x++ ){
// set depending on image types
// given boost, I would use enable_if to speed up. Avoid for now.
if( hasColorTable == false ){
write_pixel( scanline[x], gdalType, nChannels, img, y, x, c );
}
else{
write_ctable_pixel( scanline[x], gdalType, gdalColorTable, img, y, x, c );
}
}
}
// delete our temp pointer
delete [] scanline;
}
return true;
}
/**
* Read image header
*/
bool GdalDecoder::readHeader(){
// load the dataset
m_dataset = (GDALDataset*) GDALOpen( m_filename.c_str(), GA_ReadOnly);
// if dataset is null, then there was a problem
if( m_dataset == NULL ){
return false;
}
// make sure we have pixel data inside the raster
if( m_dataset->GetRasterCount() <= 0 ){
return false;
}
//extract the driver infomation
m_driver = m_dataset->GetDriver();
// if the driver failed, then exit
if( m_driver == NULL ){
return false;
}
// get the image dimensions
m_width = m_dataset->GetRasterXSize();
m_height= m_dataset->GetRasterYSize();
// make sure we have at least one band/channel
if( m_dataset->GetRasterCount() <= 0 ){
return false;
}
// check if we have a color palette
int tempType;
if( m_dataset->GetRasterBand(1)->GetColorInterpretation() == GCI_PaletteIndex ){
// remember that we have a color palette
hasColorTable = true;
// if the color tables does not exist, then we failed
if( m_dataset->GetRasterBand(1)->GetColorTable() == NULL ){
return false;
}
// otherwise, get the pixeltype
else{
// convert the palette interpretation to opencv type
tempType = gdalPaletteInterpretation2OpenCV( m_dataset->GetRasterBand(1)->GetColorTable()->GetPaletteInterpretation(),
m_dataset->GetRasterBand(1)->GetRasterDataType() );
if( tempType == -1 ){
return false;
}
m_type = tempType;
}
}
// otherwise, we have standard channels
else{
// remember that we don't have a color table
hasColorTable = false;
// convert the datatype to opencv
tempType = gdal2opencv( m_dataset->GetRasterBand(1)->GetRasterDataType(), m_dataset->GetRasterCount() );
if( tempType == -1 ){
return false;
}
m_type = tempType;
}
return true;
}
/**
* Close the module
*/
void GdalDecoder::close(){
GDALClose((GDALDatasetH)m_dataset);
m_dataset = NULL;
m_driver = NULL;
}
/**
* Create a new decoder
*/
ImageDecoder GdalDecoder::newDecoder()const{
return makePtr<GdalDecoder>();
}
/**
* Test the file signature
*/
bool GdalDecoder::checkSignature( const String& signature )const{
// look for NITF
std::string str = signature.c_str();
if( str.substr(0,4).find("NITF") != std::string::npos ){
return true;
}
// look for DTED
if( str.substr(140,4) == "DTED" ){
return true;
}
return false;
}
} /// End of cv Namespace
#endif /**< End of HAVE_GDAL Definition */