gdal-image.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * gdal_image.cpp -- Load GIS data into OpenCV Containers using the Geospatial Data Abstraction Library
  3. */
  4. // OpenCV Headers
  5. #include "opencv2/core.hpp"
  6. #include "opencv2/imgproc.hpp"
  7. #include "opencv2/highgui.hpp"
  8. // C++ Standard Libraries
  9. #include <cmath>
  10. #include <iostream>
  11. #include <stdexcept>
  12. #include <vector>
  13. using namespace std;
  14. // define the corner points
  15. // Note that GDAL library can natively determine this
  16. cv::Point2d tl( -122.441017, 37.815664 );
  17. cv::Point2d tr( -122.370919, 37.815311 );
  18. cv::Point2d bl( -122.441533, 37.747167 );
  19. cv::Point2d br( -122.3715, 37.746814 );
  20. // determine dem corners
  21. cv::Point2d dem_bl( -122.0, 38);
  22. cv::Point2d dem_tr( -123.0, 37);
  23. // range of the heat map colors
  24. std::vector<std::pair<cv::Vec3b,double> > color_range;
  25. // List of all function prototypes
  26. cv::Point2d lerp( const cv::Point2d&, const cv::Point2d&, const double& );
  27. cv::Vec3b get_dem_color( const double& );
  28. cv::Point2d world2dem( const cv::Point2d&, const cv::Size&);
  29. cv::Point2d pixel2world( const int&, const int&, const cv::Size& );
  30. void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r );
  31. /*
  32. * Linear Interpolation
  33. * p1 - Point 1
  34. * p2 - Point 2
  35. * t - Ratio from Point 1 to Point 2
  36. */
  37. cv::Point2d lerp( cv::Point2d const& p1, cv::Point2d const& p2, const double& t ){
  38. return cv::Point2d( ((1-t)*p1.x) + (t*p2.x),
  39. ((1-t)*p1.y) + (t*p2.y));
  40. }
  41. /*
  42. * Interpolate Colors
  43. */
  44. template <typename DATATYPE, int N>
  45. cv::Vec<DATATYPE,N> lerp( cv::Vec<DATATYPE,N> const& minColor,
  46. cv::Vec<DATATYPE,N> const& maxColor,
  47. double const& t ){
  48. cv::Vec<DATATYPE,N> output;
  49. for( int i=0; i<N; i++ ){
  50. output[i] = (uchar)(((1-t)*minColor[i]) + (t * maxColor[i]));
  51. }
  52. return output;
  53. }
  54. /*
  55. * Compute the dem color
  56. */
  57. cv::Vec3b get_dem_color( const double& elevation ){
  58. // if the elevation is below the minimum, return the minimum
  59. if( elevation < color_range[0].second ){
  60. return color_range[0].first;
  61. }
  62. // if the elevation is above the maximum, return the maximum
  63. if( elevation > color_range.back().second ){
  64. return color_range.back().first;
  65. }
  66. // otherwise, find the proper starting index
  67. int idx=0;
  68. double t = 0;
  69. for( int x=0; x<(int)(color_range.size()-1); x++ ){
  70. // if the current elevation is below the next item, then use the current
  71. // two colors as our range
  72. if( elevation < color_range[x+1].second ){
  73. idx=x;
  74. t = (color_range[x+1].second - elevation)/
  75. (color_range[x+1].second - color_range[x].second);
  76. break;
  77. }
  78. }
  79. // interpolate the color
  80. return lerp( color_range[idx].first, color_range[idx+1].first, t);
  81. }
  82. /*
  83. * Given a pixel coordinate and the size of the input image, compute the pixel location
  84. * on the DEM image.
  85. */
  86. cv::Point2d world2dem( cv::Point2d const& coordinate, const cv::Size& dem_size ){
  87. // relate this to the dem points
  88. // ASSUMING THAT DEM DATA IS ORTHORECTIFIED
  89. double demRatioX = ((dem_tr.x - coordinate.x)/(dem_tr.x - dem_bl.x));
  90. double demRatioY = 1-((dem_tr.y - coordinate.y)/(dem_tr.y - dem_bl.y));
  91. cv::Point2d output;
  92. output.x = demRatioX * dem_size.width;
  93. output.y = demRatioY * dem_size.height;
  94. return output;
  95. }
  96. /*
  97. * Convert a pixel coordinate to world coordinates
  98. */
  99. cv::Point2d pixel2world( const int& x, const int& y, const cv::Size& size ){
  100. // compute the ratio of the pixel location to its dimension
  101. double rx = (double)x / size.width;
  102. double ry = (double)y / size.height;
  103. // compute LERP of each coordinate
  104. cv::Point2d rightSide = lerp(tr, br, ry);
  105. cv::Point2d leftSide = lerp(tl, bl, ry);
  106. // compute the actual Lat/Lon coordinate of the interpolated coordinate
  107. return lerp( leftSide, rightSide, rx );
  108. }
  109. /*
  110. * Add color to a specific pixel color value
  111. */
  112. void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r ){
  113. if( pix[0] + b < 255 && pix[0] + b >= 0 ){ pix[0] += b; }
  114. if( pix[1] + g < 255 && pix[1] + g >= 0 ){ pix[1] += g; }
  115. if( pix[2] + r < 255 && pix[2] + r >= 0 ){ pix[2] += r; }
  116. }
  117. /*
  118. * Main Function
  119. */
  120. int main( int argc, char* argv[] ){
  121. /*
  122. * Check input arguments
  123. */
  124. if( argc < 3 ){
  125. cout << "usage: " << argv[0] << " <image_name> <dem_model_name>" << endl;
  126. return -1;
  127. }
  128. // load the image (note that we don't have the projection information. You will
  129. // need to load that yourself or use the full GDAL driver. The values are pre-defined
  130. // at the top of this file
  131. //![load1]
  132. cv::Mat image = cv::imread(argv[1], cv::IMREAD_LOAD_GDAL | cv::IMREAD_COLOR );
  133. //![load1]
  134. //![load2]
  135. // load the dem model
  136. cv::Mat dem = cv::imread(argv[2], cv::IMREAD_LOAD_GDAL | cv::IMREAD_ANYDEPTH );
  137. //![load2]
  138. // create our output products
  139. cv::Mat output_dem( image.size(), CV_8UC3 );
  140. cv::Mat output_dem_flood( image.size(), CV_8UC3 );
  141. // for sanity sake, make sure GDAL Loads it as a signed short
  142. if( dem.type() != CV_16SC1 ){ throw std::runtime_error("DEM image type must be CV_16SC1"); }
  143. // define the color range to create our output DEM heat map
  144. // Pair format ( Color, elevation ); Push from low to high
  145. // Note: This would be perfect for a configuration file, but is here for a working demo.
  146. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 188, 154, 46), -1));
  147. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 110, 220, 110), 0.25));
  148. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 150, 250, 230), 20));
  149. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 160, 220, 200), 75));
  150. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 220, 190, 170), 100));
  151. color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 250, 180, 140), 200));
  152. // define a minimum elevation
  153. double minElevation = -10;
  154. // iterate over each pixel in the image, computing the dem point
  155. for( int y=0; y<image.rows; y++ ){
  156. for( int x=0; x<image.cols; x++ ){
  157. // convert the pixel coordinate to lat/lon coordinates
  158. cv::Point2d coordinate = pixel2world( x, y, image.size() );
  159. // compute the dem image pixel coordinate from lat/lon
  160. cv::Point2d dem_coordinate = world2dem( coordinate, dem.size() );
  161. // extract the elevation
  162. double dz;
  163. if( dem_coordinate.x >= 0 && dem_coordinate.y >= 0 &&
  164. dem_coordinate.x < dem.cols && dem_coordinate.y < dem.rows ){
  165. dz = dem.at<short>(dem_coordinate);
  166. }else{
  167. dz = minElevation;
  168. }
  169. // write the pixel value to the file
  170. output_dem_flood.at<cv::Vec3b>(y,x) = image.at<cv::Vec3b>(y,x);
  171. // compute the color for the heat map output
  172. cv::Vec3b actualColor = get_dem_color(dz);
  173. output_dem.at<cv::Vec3b>(y,x) = actualColor;
  174. // show effect of a 10 meter increase in ocean levels
  175. if( dz < 10 ){
  176. add_color( output_dem_flood.at<cv::Vec3b>(y,x), 90, 0, 0 );
  177. }
  178. // show effect of a 50 meter increase in ocean levels
  179. else if( dz < 50 ){
  180. add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 90, 0 );
  181. }
  182. // show effect of a 100 meter increase in ocean levels
  183. else if( dz < 100 ){
  184. add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 0, 90 );
  185. }
  186. }}
  187. // print our heat map
  188. cv::imwrite( "heat-map.jpg" , output_dem );
  189. // print the flooding effect image
  190. cv::imwrite( "flooded.jpg", output_dem_flood);
  191. return 0;
  192. }