OpenCV 5.0.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 5.0.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
GDALによる地理空間ラスタファイルの読み込み

前のチュートリアル: アプリケーションへのトラックバーの追加
次のチュートリアル: OpenCVによる動画入力と類似度測定

原著者Marvin Smith
互換性OpenCV >= 3.0

地理空間ラスタデータは、地理情報システム(GIS)や写真測量で多用される成果物である。ラスタデータは典型的には画像や数値標高モデル(DEM)を表現できる。GIS画像を読み込むための標準ライブラリは、Geographic Data Abstraction Library (GDAL) である。この例では、OpenCVのネイティブ関数を用いてGISラスタ形式を読み込む手法を示す。さらに、OpenCVがこのデータを新規かつ興味深い目的にどのように活用できるかの例も示す。

目的

このチュートリアルの主な目的:

  • OpenCVの imread を使って衛星画像を読み込む方法。
  • OpenCVの imread を使ってSRTM数値標高モデルを読み込む方法。
  • 画像とDEMの両方のコーナー座標が与えられたとき、標高データを画像に対応付けて各ピクセルの標高を求める。
  • 地形ヒートマップの基本的で実装しやすい例を示す。
  • DEMデータをオルソ補正画像と組み合わせて用いる基本的な例を示す。

これらの目標を実現するため、以下のコードでは数値標高モデルとサンフランシスコのGeoTiff画像を入力として受け取る。画像とDEMデータを処理し、画像の地形ヒートマップを生成するとともに、湾の水位が10メートル、50メートル、100メートル上昇した場合に影響を受ける都市の領域をラベル付けする。

コード

/*
* gdal_image.cpp -- Load GIS data into OpenCV Containers using the Geospatial Data Abstraction Library
*/
// OpenCV Headers
#include "opencv2/core.hpp"
// C++ Standard Libraries
#include <cmath>
#include <iostream>
#include <stdexcept>
#include <vector>
using namespace std;
// define the corner points
// Note that GDAL library can natively determine this
cv::Point2d tl( -122.441017, 37.815664 );
cv::Point2d tr( -122.370919, 37.815311 );
cv::Point2d bl( -122.441533, 37.747167 );
cv::Point2d br( -122.3715, 37.746814 );
// determine dem corners
cv::Point2d dem_bl( -122.0, 38);
cv::Point2d dem_tr( -123.0, 37);
// range of the heat map colors
std::vector<std::pair<cv::Vec3b,double> > color_range;
// List of all function prototypes
cv::Point2d lerp( const cv::Point2d&, const cv::Point2d&, const double& );
cv::Vec3b get_dem_color( const double& );
cv::Point2d world2dem( const cv::Point2d&, const cv::Size&);
cv::Point2d pixel2world( const int&, const int&, const cv::Size& );
void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r );
/*
* Linear Interpolation
* p1 - Point 1
* p2 - Point 2
* t - Ratio from Point 1 to Point 2
*/
cv::Point2d lerp( cv::Point2d const& p1, cv::Point2d const& p2, const double& t ){
return cv::Point2d( ((1-t)*p1.x) + (t*p2.x),
((1-t)*p1.y) + (t*p2.y));
}
/*
* Interpolate Colors
*/
template <typename DATATYPE, int N>
cv::Vec<DATATYPE,N> const& maxColor,
double const& t ){
for( int i=0; i<N; i++ ){
output[i] = (uchar)(((1-t)*minColor[i]) + (t * maxColor[i]));
}
return output;
}
/*
* Compute the dem color
*/
cv::Vec3b get_dem_color( const double& elevation ){
// if the elevation is below the minimum, return the minimum
if( elevation < color_range[0].second ){
return color_range[0].first;
}
// if the elevation is above the maximum, return the maximum
if( elevation > color_range.back().second ){
return color_range.back().first;
}
// otherwise, find the proper starting index
int idx=0;
double t = 0;
for( int x=0; x<(int)(color_range.size()-1); x++ ){
// if the current elevation is below the next item, then use the current
// two colors as our range
if( elevation < color_range[x+1].second ){
idx=x;
t = (color_range[x+1].second - elevation)/
(color_range[x+1].second - color_range[x].second);
break;
}
}
// interpolate the color
return lerp( color_range[idx].first, color_range[idx+1].first, t);
}
/*
* Given a pixel coordinate and the size of the input image, compute the pixel location
* on the DEM image.
*/
cv::Point2d world2dem( cv::Point2d const& coordinate, const cv::Size& dem_size ){
// relate this to the dem points
// ASSUMING THAT DEM DATA IS ORTHORECTIFIED
double demRatioX = ((dem_tr.x - coordinate.x)/(dem_tr.x - dem_bl.x));
double demRatioY = 1-((dem_tr.y - coordinate.y)/(dem_tr.y - dem_bl.y));
cv::Point2d output;
output.x = demRatioX * dem_size.width;
output.y = demRatioY * dem_size.height;
return output;
}
/*
* Convert a pixel coordinate to world coordinates
*/
cv::Point2d pixel2world( const int& x, const int& y, const cv::Size& size ){
// compute the ratio of the pixel location to its dimension
double rx = (double)x / size.width;
double ry = (double)y / size.height;
// compute LERP of each coordinate
cv::Point2d rightSide = lerp(tr, br, ry);
cv::Point2d leftSide = lerp(tl, bl, ry);
// compute the actual Lat/Lon coordinate of the interpolated coordinate
return lerp( leftSide, rightSide, rx );
}
/*
* Add color to a specific pixel color value
*/
void add_color( cv::Vec3b& pix, const uchar& b, const uchar& g, const uchar& r ){
if( pix[0] + b < 255 && pix[0] + b >= 0 ){ pix[0] += b; }
if( pix[1] + g < 255 && pix[1] + g >= 0 ){ pix[1] += g; }
if( pix[2] + r < 255 && pix[2] + r >= 0 ){ pix[2] += r; }
}
/*
* Main Function
*/
int main( int argc, char* argv[] ){
/*
* Check input arguments
*/
if( argc < 3 ){
cout << "usage: " << argv[0] << " <image_name> <dem_model_name>" << endl;
return -1;
}
// load the image (note that we don't have the projection information. You will
// need to load that yourself or use the full GDAL driver. The values are pre-defined
// at the top of this file
// load the dem model
// create our output products
cv::Mat output_dem( image.size(), CV_8UC3 );
cv::Mat output_dem_flood( image.size(), CV_8UC3 );
// for sanity sake, make sure GDAL Loads it as a signed short
if( dem.type() != CV_16SC1 ){ throw std::runtime_error("DEM image type must be CV_16SC1"); }
// define the color range to create our output DEM heat map
// Pair format ( Color, elevation ); Push from low to high
// Note: This would be perfect for a configuration file, but is here for a working demo.
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 188, 154, 46), -1));
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 110, 220, 110), 0.25));
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 150, 250, 230), 20));
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 160, 220, 200), 75));
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 220, 190, 170), 100));
color_range.push_back( std::pair<cv::Vec3b,double>(cv::Vec3b( 250, 180, 140), 200));
// define a minimum elevation
double minElevation = -10;
// iterate over each pixel in the image, computing the dem point
for( int y=0; y<image.rows; y++ ){
for( int x=0; x<image.cols; x++ ){
// convert the pixel coordinate to lat/lon coordinates
cv::Point2d coordinate = pixel2world( x, y, image.size() );
// compute the dem image pixel coordinate from lat/lon
cv::Point2d dem_coordinate = world2dem( coordinate, dem.size() );
// extract the elevation
double dz;
if( dem_coordinate.x >= 0 && dem_coordinate.y >= 0 &&
dem_coordinate.x < dem.cols && dem_coordinate.y < dem.rows ){
dz = dem.at<short>(dem_coordinate);
}else{
dz = minElevation;
}
// write the pixel value to the file
output_dem_flood.at<cv::Vec3b>(y,x) = image.at<cv::Vec3b>(y,x);
// compute the color for the heat map output
cv::Vec3b actualColor = get_dem_color(dz);
output_dem.at<cv::Vec3b>(y,x) = actualColor;
// show effect of a 10 meter increase in ocean levels
if( dz < 10 ){
add_color( output_dem_flood.at<cv::Vec3b>(y,x), 90, 0, 0 );
}
// show effect of a 50 meter increase in ocean levels
else if( dz < 50 ){
add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 90, 0 );
}
// show effect of a 100 meter increase in ocean levels
else if( dz < 100 ){
add_color( output_dem_flood.at<cv::Vec3b>(y,x), 0, 0, 90 );
}
}}
// print our heat map
cv::imwrite( "heat-map.jpg" , output_dem );
// print the flooding effect image
cv::imwrite( "flooded.jpg", output_dem_flood);
return 0;
}
Comma-separated Matrix Initializer.
Definition mat.hpp:964
MatSize size
Definition mat.hpp:2511
_Tp & at(int i0=0)
Returns a reference to the specified array element.
int cols
Definition mat.hpp:2488
int rows
the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
Definition mat.hpp:2488
int type() const
Returns the type of a matrix element.
_Tp y
y coordinate of the point
Definition types.hpp:202
_Tp x
x coordinate of the point
Definition types.hpp:201
Template class for specifying the size of an image or rectangle.
Definition types.hpp:338
_Tp height
the height
Definition types.hpp:366
_Tp width
the width
Definition types.hpp:365
Template class for short numerical vectors, a partial case of Matx.
Definition matx.hpp:379
Point_< double > Point2d
Definition types.hpp:208
uint8_t uchar
Definition interface.h:35
#define CV_16SC1
Definition interface.h:95
#define CV_8UC3
Definition interface.h:79
@ IMREAD_ANYDEPTH
If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it t...
Definition imgcodecs.hpp:74
@ IMREAD_LOAD_GDAL
If set, use the gdal driver for loading the image.
Definition imgcodecs.hpp:76
@ IMREAD_COLOR
Same as IMREAD_COLOR_BGR.
Definition imgcodecs.hpp:73
bool imwrite(const String &filename, InputArray img, const std::vector< int > &params=std::vector< int >())
Saves an image to a specified file.
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
int main(int argc, char *argv[])
Definition highgui_qt.cpp:3
GOpaque< Size > size(const GMat &src)
Gets dimensions from Mat.
STL namespace.

GDALを使ってラスタデータを読み込む方法

このデモはデフォルトのOpenCV imread 関数を使用する。主な違いは、GDALに画像を読み込ませるために適切なフラグを使用しなければならない点である。

数値標高モデルを読み込む際は、各ピクセルの実際の数値が本質的に重要であり、スケーリングや切り捨てを行ってはならない。例えば画像データでは、値1のdoubleとして表されるピクセルは、値255のunsigned charとして表されるピクセルと同じ見た目になる。地形データでは、ピクセル値は標高(メートル単位)を表す。OpenCVがネイティブ値を保持することを保証するには、imread でGDALフラグをANYDEPTHフラグとともに使用する。

// load the dem model

読み込もうとしているDEMモデルの型が事前に分かっている場合は、assertやその他の仕組みを使ってMat::type() またはMat::depth() をテストするのが安全策となる。NASAやDODの仕様書には、各種標高モデルの入力型が記載されている。主要な型であるSRTMとDTEDは、いずれもsigned shortである。

注意

緯度・経度(地理)座標は通常避けるべきである

地理座標系は球面座標系であり、これをデカルト数学とともに用いるのは技術的には正しくない。このデモでは可読性を高めるためにこれらを使用しており、要点を示すには十分な精度である。より適切な座標系はUniversal Transverse Mercator(UTM)である。

コーナー座標を求める

画像のコーナー座標を求める簡単な方法の一つは、コマンドラインツール gdalinfo を使用することである。オルソ補正済みで投影情報を含む画像については、USGS EarthExplorer を使用できる。

\f$> gdalinfo N37W123.hgt
Driver: SRTMHGT/SRTMHGT File Format
Files: N37W123.hgt
Size is 3601, 3601
Coordinate System is:
GEOGCS["WGS 84",
DATUM["WGS_1984",
... more output ...
Corner Coordinates:
Upper Left (-123.0001389, 38.0001389) (123d 0' 0.50"W, 38d 0' 0.50"N)
Lower Left (-123.0001389, 36.9998611) (123d 0' 0.50"W, 36d59'59.50"N)
Upper Right (-121.9998611, 38.0001389) (121d59'59.50"W, 38d 0' 0.50"N)
Lower Right (-121.9998611, 36.9998611) (121d59'59.50"W, 36d59'59.50"N)
Center (-122.5000000, 37.5000000) (122d30' 0.00"W, 37d30' 0.00"N)
... more output ...

結果

以下はプログラムの出力である。最初の画像を入力として使用する。DEMモデルについては、USGSにあるSRTMファイルをここからダウンロードする。 http://dds.cr.usgs.gov/srtm/version2_1/SRTM1/Region_04/N37W123.hgt.zip

Input Image
Heat Map
Heat Map Overlay