数値解析法及び演習 第十四回

画像解析
  1. 画像の表現
  2. 4バイトデータの入出力
  3. 1バイトデータの入出力
  4. 複数ファイルのオープン
  5. 全地球の温度上昇トレンド
  6. 今日の宿題

[1] 画像の表現


[2] 4バイトデータの入出力

今回用いる白黒画像は多数の1バイトデータからなります.1バイトのデータ(8個の0と1)をそのまま書き込んだファイルをバイナリファイルと呼びます.2の8乗=256個の整数を表現できます.一方で今まで扱ってきた,人間が読めるファイルは「テキストファイル」といって区別されます.シフトJIS(macのデフォルト文字コード)では,半角文字で1バイト,全角文字で2バイトのメモリを使います.256は3桁ですから,半角の数字で表現した場合3バイト必要になります.したがって,テキストでファイルに書き出した場合とバイナリで書き出した場合とで,必要な記憶容量が3倍も異なることになります.桁数が多くなればもっと差が大きくなります.

今回あつかうデータはバイナリファイルのため,バイナリファイルの読み書きを行う必要があります.次のプログラムは,0.1から1.0までの10個の単精度実数を生成し,それをバイナリ形式でファイル10.datに書き込むプログラムです.

      implicit none
      integer :: i
      real :: x
      open(1,file='10.dat',access='direct',form='unformatted',recl=4)

      do i=1,10
         x=0.1*i
         write(1,rec=i) x
      enddo

      end

ここで,form='unformatted'でバイナリ形式を指定し,recl=4で一つのデータ(レコード)の大きさ(今の場合単精度実数=4バイト=32ビット)を指定します.access='direct'は,直接参照ファイルであることを指定します.ファイル中の場所を指定することで,特定の数値に直接アクセスします.今までは'sequential'となっていて,ファイルの先頭から順番にアクセスすることが暗に指定されていました.またwrite文では,rec=iでレコード番号を指定します.このプログラムを実行し,生成された10.datのサイズが40バイトになっていることを確認しましょう.

先ほど生成したデータを読み込んで画面に表示するプログラムは以下になります.

      implicit none
      integer :: i
      real :: x
      open(1,file='10.dat',access='direct',form='unformatted',recl=4)

      do i=1,10
       	 read(1,rec=i) x
	 write(*,*) x
      enddo

      end

実行して結果を確認しましょう.


[3] 1バイトデータの入出力

今回扱う画像は白黒256階調なので,各ピクセルは1バイトのデータで表現されます.FORTRANでは符号なし1バイトデータを扱う形式が用意されていません.integer(1)は1バイト整数なのですが,符号がついて-128-+127の範囲となります.

0から255までの整数をバイナリデータとしてファイルに書き出し,そのデータを読み出して0から255までの整数を画面に表示するプログラムは以下になります.ファイルに書き出す際は,127よりも大きな数は対応する補数(マイナスの数)に変換します.参考までに.読み出す際には変換前の変数j(-128から+127)も表示されます.128からは,以前に教えた規則通りに二進数から10進数に変換され,jはマイナスとなります.

      implicit none
      
      integer :: i, k
      integer(1) :: j

      open(1,file='256.dat',access='direct',form='unformatted',recl=1)

      do i=0,255      ! 画像データは0-255なので0からループを開始する
      if( i > 127 ) then
         j = i - 256    ! 128以上の数については補数に変換する
         else
         j = i
      endif
         write(1,rec=i+1) j   ! rec番号は1から始まるのでiに1を足す
      enddo
    
      do i=1,256
        read(1,rec=i) j   ! 0から127, -128から-1の数がバイナリで格納されている
      
           if( j < 0 ) then
	      k = j + 256
           else
	      k = j
           endif
		   
           write(*,*) i, j, k
		   
      enddo

      end

練習問題1

このデータは,1980年-2000年における全地球の表面気温のデータです.360x180=64800個の1バイトデータ(0-255)が並んだファイルが21個あります.0が-15度,255が25度に対応します.それぞれのデータは地球上の経度(-179-180),緯度(0-179)における温度です.注意してほしいのは,二次元的にデータが並んでいるわけではなく,一次元的にデータは並んでいることです.データの頭は北極(緯度0)であり,まずは経度方向に360個のデータが並びます.361番目のデータは緯度1,経度-179度(西経179度)となります.360個のデータの組みが180個並んでいる,という構造になってます.

ダウンロードしたら

  mv ~/Downloads/data-2/* ./
とすることで現在のディレクトリの中にデータファイルが移動します.lsで確認してください.

display -depth 8 -size 360x180 gray:conv1980.raw を実行すると,1980年における温度分布が画像として表示されます.黒が低温,白が高温です.

次に,以下のプログラムを実行し,数値(0-255)が画面に表示されることを確認してください.

      implicit none
      integer :: i,k
      integer(1) :: j

      open(1,file='conv1980.raw',access='direct',form='unformatted',recl=1)

      do i=1,64800
         read(1,rec=i) j
         if( j < 0 ) then
            k = j + 256
         else
            k = j 
         endif
         write(*,*) i,k
      enddo

      end
    

[4] 複数ファイルのオープン

今回は,1980年から2000年までの21年分のデータを解析し,その温度上昇のトレンドが地域ごとにどう分布しているかを調べます.

そのためには,ファイルを21個開かないといけません.正直にプログラムに記述してもいいのですが効率が悪いので,文字処理のテクニックを用いてファイルを操作します.次のプログラムを実行すると,21個のファイルを順番にオープンしてそのファイル名が画面に表示されます.

      implicit none
      integer :: i,num,nyear,j, k
      character :: name*12,year*4  ! 文字を格納する変数を宣言 12,4は文字数
      integer(1) ::  m

      nyear=21

      do i=1,nyear

         num=1979+i
         write(year,'(I4)') num  ! 整数numを文字型に変換しyearに代入
         name='conv'//year//'.raw' ! conv, year, .rawからファイル名nameを生成


         open(10+i,file=name,access='direct',form='unformatted',recl=1)
         ! ここでnameには上で作られた文字列が代入される
      
         do j = 1, 64800
           read(10+i,rec=j) m
           if( m < 0 ) then
              k = m + 256
           else
              k = m
           endif
           write(*,*) num,j,k
         enddo

      enddo

      end


[5] 全地球の温度上昇トレンド

さてここまでくると,全地球の温度変化を解析できるようになりました.以下の手順を踏んで,地球上のどこで温暖化がより進行しているのか解析しましょう.

練習問題2

  1. 21個のファイルからそれぞれ64800個のデータを読み込み,0-255の整数から温度に変換し,結果を配列 T(21,64800)に格納する.
  2. 64800個すべての地点において最小二乗法を用いることにより温度変化の傾き(℃/年)を算出する.
  3. 2で算出した数値を256階調の整数に変換する.0から0.1(℃/年)までを,0から255までの整数に対応させる.0より小さい値には0,0.1よりも大きい値には255を対応させる.こうすることで,温度がより上昇している地域は白っぽく見えることになる.
  4. 64800個の0から255までの整数を-128から+127に変換し,バイナリファイルplus.rawに書き込む.
  5. プログラムの大まかな構造は以下のようになるでしょう.
    
          implicit none
          integer(1) :: m  ! バイナリデータを読み込むための変数
          integer :: num, nyear  ! ファイルを読み込む際の変数
          integer :: i, k, j
          character :: name*12,year*4
          real :: T(21,64800)  ! 温度を格納する配列 21年 x 64800点
          real :: xsum, ysum, x2sum, xysum ! 最小自乗法で登場する4種の和
          real :: A !  傾きを格納する変数
    
        
        
          open(1,file='plus.raw',access='direct',form='unformatted',recl=1) ! 書き込むファイルのオープン
    
          nyear = 21
    
        do i=1,nyear
          
             num=1979+i
             write(year,'(I4)') num
             name='conv'//year//'.raw'
             write(*,*) name
          
             open(10+i,file=name,access='direct',form='unformatted',recl=1) !各年のファイルのオープン
    
          do j = 1, 64800
    
        
     ! 10+i番のファイルから数値(0-255)を64800個読み込む.読み出し方は
          read(10+i,rec=j) m
              if( m < 0 ) then
              k = m + 256
              else
              k = m
              endif
    
        T(i,j) = k*??? + ???  ! 配列T(i,j)に温度データ(-15度から+25度)が代入される.???は適当な数値で書き換える(kは0から255)
        
          enddo
          
        enddo
    
    
        do j = 1, 64800    !  場所の番号
    
           do i = 1, nyear  !  年
    
        
    ! この2重ループで,最小二乗法により64800個の傾きを算出する
        ! 最小二乗法における x が年(1980-2000) y が温度Tとなっている.
        
           enddo
    
    
        !    ここで温度上昇の傾き A から0-255の整数 k に変換する
      
    
                if( k > 127 ) then
                  m = k - 256  !  128以上の整数は補数に変換
                 else
                  m = k
             endif
            write(1,rec=j) m  ! 画像ファイルとして書き出し
    
       enddo
    
      
  6. display -depth 8 -size 360x180 gray:plus.raw  を実行する.

画像がだせたら,以下の点を観察してください.

練習問題3

  1. 画像から,温度が上昇している地域がどこか読み取る
  2. 陸域と海洋でどちらがムラが多いか?それはなぜか?

[5] 今日の宿題(1点)

プログラムおよび練習問題3の答えを送ること. 〆切は1/24.

追加の宿題(1点)

  1. もっとも温度上昇の傾きが大きい場所の緯度と経度を求める.どこの国か? 緯度経度の数値と国名(陸上でなければ海の名前)を送付すること.(注意)画像の横方向の中心が経度0度である.左右の端は東経西経180度.



日程表へ戻る <<