6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Tellus画像処理の練習メモ:緑地を切り出してGeoJSONを生成してみる

Last updated at Posted at 2019-04-01

Tellus-APiの方はだいたい理解できたので、次はJupyterNoteでの画像処理に挑戦してみました。
Pythonでの画像処理は初めてなので、pashango2さんのエントリ「PIL/Pillow チートシート」を参考にしました。なお、緑地抽出に適した処理手順はわからないので、色々と施行錯誤しながら緑地抽出を試みてみました。

##画像処理

####1.元画像の読み込み
ASNARO-01「東京北部2016/05/05」から、適度に緑地があるが場所(東京メトロ王子駅付近)をスクリーンショットし、その画像(/tellus_data/に保存されている)をJupyterNoteに読み込みました。

from PIL import Image, ImageFilter ,ImageOps,ImageMath,ImageChops
import numpy as np
import matplotlib.pyplot as plt
im = Image.open('../../tellus_data/20190331-222445.tiff','r')
plt.imshow(np.array(im))
im.save('../data/20190331-222445_0.png')

001.png

####2.画素の強調
元画像がくすんでいるので、同じ画像を4回ソフトライトして画素を強調してみました。

def _blend_f(img1, img2, func):
    blend_eval = "convert(func(float(a), float(b)), 'L')"
    bands = [
        ImageMath.eval(
            blend_eval,
            a=a,
            b=b,
            func=func
        )
        for a, b in zip(img1.split(), img2.split())
    ]
    return Image.merge(img1.mode, bands)

def _soft_light(a, b):
    _cl = (a / 255) ** ((255 - b) / 128) * 255
    _ch = (a / 255) ** (128 / b) * 255
    return _cl * (b < 128) + _ch * (b >= 128)
im2=_blend_f(im, im, _soft_light)
plt.imshow(np.array(im2))
im3=_blend_f(im2, im2, _soft_light)
plt.imshow(np.array(im3))

03.png

####3.ポスタライズ
ポスタライズで色を単純化しました。

im4=im3.convert("RGB")
im5=ImageOps.posterize(im4, 4)
plt.imshow(np.array(im5))
im5.save('../data/20190331-222445_2.png')

04.png

####4.ガンマ補正
ガンマ補正で照度、彩度を調整しました。

def gamma_table(gamma_r, gamma_g, gamma_b, gain_r=1.0, gain_g=1.0, gain_b=1.0):
    r_tbl = [min(255, int((x / 255.) ** (1. / gamma_r) * gain_r * 255.)) for x in range(256)]
    g_tbl = [min(255, int((x / 255.) ** (1. / gamma_g) * gain_g * 255.)) for x in range(256)]
    b_tbl = [min(255, int((x / 255.) ** (1. / gamma_b) * gain_b * 255.)) for x in range(256)]
    return r_tbl + g_tbl + b_tbl

im6=im5.point(lambda x: x * 2.0)
plt.imshow(np.array(im6))
im6.save('../data/20190331-222445_3.png')

05.png

####5.画素の強調
もう一度ソフトライトを行いました。

im7=_blend_f(im6, im6, _soft_light)
plt.imshow(np.array(im7))
im7.save('../data/20190331-222445_4.png')

07.png

####6.色抽出その1
OpenCVをロードし、緑系の色のピクセルを抽出しました。

!pip install opencv-python
import cv2
image_file = '../data/20190331-222445_4.png'
im8 = cv2.imread(image_file)
hsv = cv2.cvtColor(im8, cv2.COLOR_BGR2HSV)
lower = np.array([32, 64, 32])
upper = np.array([180, 255, 120])
img_mask = cv2.inRange(hsv, lower, upper)
im9 = cv2.bitwise_and(im8, im8, mask=img_mask)
plt.imshow(np.array(im9))
cv2.imwrite('../data/20190331-222445_5.png', im9)

08.png

####7.色抽出その2
上記では黄緑系統の色が抽出されなかったので、再度、色抽出を行いました。

lower = np.array([32, 64, 32])
upper = np.array([200, 255, 120])
img_mask = cv2.inRange(im8, lower, upper)
im10 = cv2.bitwise_and(im8, im8, mask=img_mask)
plt.imshow(np.array(im10))
cv2.imwrite('../data/20190331-222445_6.png', im10)

09.png

####8.色抽出結果の合成
6、7で抽出した画像を合成しました。

im11 = Image.open('../data/20190331-222445_5.png','r')
im12 = Image.open('../data/20190331-222445_6.png','r')
im13=ImageChops.lighter(im11, im12)
plt.imshow(np.array(im13))
im13.save('../data/20190331-222445_7.png')

10.png

####9.ピクセルの再抽出
画像のピクセルを検査し、RGBのうちG値が最も高いピクセルを抽出しました。

size=im13.size
im14 = Image.new('RGB',size)
for x in range(size[0]):
    for y in range(size[1]):
        r,g,b = im13.getpixel((x,y))
        if( g > r and g > b):
            im14.putpixel((x,y),(r,g,b))
        else:
            im14.putpixel((x,y),(0,0,0))
plt.imshow(np.array(im14))
im14.save('../data/20190331-222445_8.png')

11.png

####10.収縮・膨張
収縮・膨張により、ピクセル上のごみを処理し、その後、ガンマ補正を行いました。

im15=im14.filter(ImageFilter.MinFilter()) 
im16=im15.filter(ImageFilter.MaxFilter()) 
plt.imshow(np.array(im16))
im16.save('../data/20190331-222445_9.png')
im17=im16.point(lambda x: x * 2.0)
plt.imshow(np.array(im17))
im17.save('../data/20190331-222445_10.png')

12.png

####11.減色処理
色を黒を含め、4色に減色処理しました。

def sub_color(src, K):
    Z = src.reshape((-1,3))
    Z = np.float32(Z)
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    ret, label, center = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
    center = np.uint8(center)
    res = center[label.flatten()]
    return res.reshape((src.shape))
im19 = cv2.imread('../data/20190331-222445_11.png') 
im20 = sub_color(im19, 4)
plt.imshow(np.array(im20))
cv2.imwrite('../data/20190331-222445_12.png', im20)

13.png

####12.色の分離
11で緑3色に減色したので、各色のピクセルを抽出して分離し、2値画像に変換しました。
なお、この処理は使い慣れたJavaで実施しました。

####13.ベクター化
上記の2値画像をpotraceでSVGに変換しました。

####14.SVG->GeoJSON生成
svg2geojsonにより、SVGをGeoJSONに変換しました。
Tellusに生成したGeoJSONを読み込ませた画面を示しますが、そこそこうまく抽出できています。ただ、感覚的ですが、5%ほど、緑地の抽出漏れがある感じです。


##雑感

  • 光学画像からでもそこそこの精度で緑地が抽出でき、GeoJSONを生成できることがわかりました。また、林相と画像の色相に関係性があるようなら、林相区分等の自動処理も可能かもと思いました。
  • 自動処理で緑地を抽出するとなると、処理手順や抽出の閾値を色々と検討・工夫する必要があると感じました。
  • ベクター化やGeoJSON生成には外部ライブラリを使用しましたが、独自に処理を実装するか機能拡張した方がスムーズかもと感じました。
6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?