🔹 フェーズ1:画像処理の基礎(OpenCV中心)【Week 1〜8】
🔹 フェーズ2:ディープラーニングによる画像認識【Week 9〜16】
🔹 フェーズ3:農業現場応用プロジェクト制作【Week 17〜24】
週 | タイトル | 内容概要 | ノートブック例 |
Week 1 | 画像ファイルの読み込みと表示 | OpenCV入門、画像のRGB/BGR変換、チャンネル操作 | week01_opencv_intro.ipynb |
Week 2 | 基本的な画像処理 | グレースケール変換、ぼかし(平滑化)、閾値処理 | week02_opencv_processing.ipynb |
Week 3 | 輪郭検出と形状解析 | Canny、輪郭抽出、面積・外接矩形の計算 | week03_contour_analysis.ipynb |
Week 4 | ヒストグラム解析 | 明るさ/色の分布、ヒストグラム平坦化 | week04_histogram_analysis.ipynb |
Week 5 | 幾何変換 | リサイズ、回転、アフィン・射影変換 | week05_geometry_transforms.ipynb |
Week 6 | マスキングと領域抽出 | ROI抽出、条件付き抽出(色・閾値) | week06_masking_roi.ipynb |
Week 7 | 画像合成と透過処理 | 加重合成、透明背景合成(アルファチャンネル) | week07_compositing.ipynb |
Week 8 | 応用演習①:葉面積の測定 | 例:作物の葉を二値化→面積計算 | week08_leaf_area_calc.ipynb |
週 | タイトル | 内容概要 | ノートブック例 |
Week 9 | CNN基礎① | Convolutionの仕組み、MaxPoolingなど | week09_cnn_intro.ipynb |
Week 10 | CNN基礎② | kerasによるシンプルなCNN構築(分類) | week10_cnn_build.ipynb |
Week 11 | データ拡張と前処理 | 回転・反転・拡張による学習改善 | week11_data_aug.ipynb |
Week 12 | 転移学習(ResNet等) | 学習済みモデルの応用とfine-tuning | week12_transfer_learning.ipynb |
Week 13 | 精度評価と混同行列 | 精度/再現率/F1・Confusion Matrixの表示 | week13_model_eval.ipynb |
Week 14 | 物体検出①:YOLOv5入門 | 基本構造と使い方 | week14_yolo_intro.ipynb |
Week 15 | 物体検出②:農業応用 | 害虫・病斑・果実検出など | week15_yolo_agri.ipynb |
Week 16 | 応用演習②:病気画像分類 | 農研機構データを使った病気識別 | week16_disease_classify.ipynb |
📁 ディレクトリ構成例(テンプレート)
agri_vision_project/
├── data/
│ ├── train/
│ ├── test/
├── notebooks/
│ ├── 01_preprocessing.ipynb
│ ├── 02_model_training.ipynb
│ ├── 03_evaluation.ipynb
│ └── 04_inference_streamlit.ipynb
├── README.md
├── requirements.txt
└── app/
└── inference_ui.py
🔹 各ノートブックの内容
01_preprocessing.ipynb
・画像のリサイズ、正規化、ラベルエンコードなど
02_model_training.ipynb
・CNNまたはYOLOベースのモデル訓練(分類 or 検出)
03_evaluation.ipynb
・テスト画像での精度評価、誤分類の視覚化
04_inference_streamlit.ipynb
・アップロード画像をその場で判定・結果を表示する簡易UI構築
以下のような形で実装を進められます:
Image Processing Week1 3
# Week 1: OpenCVによる画像の読み込みと表示
import cv2
import matplotlib.pyplot as plt
# 画像の読み込み(BGR形式)
img = cv2.imread('sample_leaf.jpg')
# BGR → RGB に変換
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 画像表示
plt.imshow(img_rgb)
plt.title("Original Image (RGB)")
plt.axis('off')
plt.show()
# 画像サイズの取得
height, width, channels = img.shape
print(f"Size: {width}x{height}, Channels: {channels}")
# Week 2: グレースケール変換とぼかし処理
# グレースケールに変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray, cmap='gray')
plt.title("Grayscale")
plt.axis('off')
plt.show()
# ぼかし(GaussianBlur)
blur = cv2.GaussianBlur(gray, (7, 7), 0)
plt.imshow(blur, cmap='gray')
plt.title("Gaussian Blur")
plt.axis('off')
plt.show()
# 二値化(大津の方法)
_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
plt.imshow(thresh, cmap='gray')
plt.title("Otsu Threshold")
plt.axis('off')
plt.show()
# Week 3: 輪郭検出と形状の分析
# 輪郭検出
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 輪郭の描画
img_contour = img.copy()
cv2.drawContours(img_contour, contours, -1, (0, 255, 0), 2)
plt.imshow(cv2.cvtColor(img_contour, cv2.COLOR_BGR2RGB))
plt.title("Contours")
plt.axis('off')
plt.show()
# 面積や外接矩形の計算(1つ目の輪郭を例に)
if contours:
cnt = contours[0]
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
print(f"Area: {area}, Bounding Box: x={x}, y={y}, w={w}, h={h}")
Week 1:OpenCVを使った画像読み込み・表示
Week 2:グレースケール変換、ぼかし、大津の二値化
Week 3:輪郭検出と面積・外接矩形の計算
# Week 4: ヒストグラム解析と画像の調整
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 画像読み込み(グレースケール)
img = cv2.imread('sample_leaf.jpg', cv2.IMREAD_GRAYSCALE)
# ヒストグラムの計算
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
plt.plot(hist)
plt.title('Histogram of Grayscale Image')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.show()
# ヒストグラム均等化(画像のコントラスト向上)
img_eq = cv2.equalizeHist(img)
# 変更後の画像のヒストグラム
hist_eq = cv2.calcHist([img_eq], [0], None, [256], [0, 256])
plt.plot(hist_eq, color='r')
plt.title('Histogram after Equalization')
plt.xlabel('Pixel Value')
plt.ylabel('Frequency')
plt.show()
# 結果表示
plt.subplot(1, 2, 1)
plt.imshow(img, cmap='gray')
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(img_eq, cmap='gray')
plt.title('Equalized Image')
plt.axis('off')
plt.show()
# 演習問題:
# 1. 画像のコントラストを向上させるために他の手法を試してみてください(例:ガンマ補正)。
# 2. 異なる画像(例:農場画像)を使ってヒストグラムの分析を行い、結果を比較してみてください。
# 3. ヒストグラム均等化後の画像で、主観的なコントラストの変化を評価してください。
01_data_cleaning.ipynb:欠損処理、外れ値除去、型変換
02_feature_engineering.ipynb:生育条件に関連する特徴量作成
03_model_training.ipynb:回帰モデル or 分類モデルの訓練
04_evaluation.ipynb:RMSE / F1スコア / 可視化
05_dashboard_streamlit.ipynb:簡易ダッシュボード構築用スクリプト作成
# 01_data_cleaning.ipynb
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# データの読み込み
df = pd.read_csv('agriculture_data.csv')
# 欠損値処理(欠損値の確認)
print(df.isnull().sum())
# 欠損値を平均で埋める
df.fillna(df.mean(), inplace=True)
# 外れ値除去(例:3σルールを使用)
for column in df.select_dtypes(include=[np.number]):
mean = df[column].mean()
std = df[column].std()
df = df[(df[column] > mean - 3*std) & (df[column] < mean + 3*std)]
# 型変換(例:日付をdatetime型に)
df['date'] = pd.to_datetime(df['date'])
# データの確認
print(df.head())
# 欠損値処理後の確認
print(df.isnull().sum())
# 02_feature_engineering.ipynb
import pandas as pd
# データの読み込み
df = pd.read_csv('cleaned_agriculture_data.csv')
# 生育条件に関連する特徴量作成
# 例: 温度と湿度の組み合わせから生育適正度を計算
df['growth_index'] = df['temperature'] * df['humidity']
# 例: 日照時間から新しい特徴量「日照効率」を作成
df['sunlight_efficiency'] = df['sunlight'] / df['temperature']
# 例: 収穫量の予測のために「降水量 × 土壌水分」の特徴量
df['water_availability'] = df['precipitation'] * df['soil_moisture']
# 特徴量の確認
print(df[['growth_index', 'sunlight_efficiency', 'water_availability']].head())
# 03_model_training.ipynb
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import LabelEncoder
# データの読み込み
df = pd.read_csv('engineered_agriculture_data.csv')
# 特徴量とターゲット変数の設定(例:収穫量の予測)
X = df.drop(['harvest_yield'], axis=1)
y = df['harvest_yield']
# データの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# モデルの訓練(回帰モデル)
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# 予測
y_pred = model.predict(X_test)
# 評価(RMSE)
rmse = mean_squared_error(y_test, y_pred, squared=False)
print(f"RMSE: {rmse}")
# 04_evaluation.ipynb
import matplotlib.pyplot as plt
from sklearn.metrics import f1_score, confusion_matrix
# モデルの評価結果(例:回帰モデルのRMSE, 分類モデルのF1スコア)
# ここではF1スコアを計算する場合を例とする(分類の場合)
y_pred_class = (y_pred > 0.5).astype(int) # 予測値を0-1に変換
# F1スコア
f1 = f1_score(y_test, y_pred_class)
print(f"F1 Score: {f1}")
# 混同行列の可視化
conf_matrix = confusion_matrix(y_test, y_pred_class)
plt.matshow(conf_matrix, cmap='coolwarm')
plt.title('Confusion Matrix')
plt.colorbar()
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
# 回帰モデルの場合のRMSE可視化
plt.scatter(y_test, y_pred, alpha=0.5)
plt.xlabel('True Values')
plt.ylabel('Predictions')
plt.title('True vs Predicted')
plt.show()
# 05_dashboard_streamlit.ipynb
import streamlit as st
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
# データの読み込み
df = pd.read_csv('engineered_agriculture_data.csv')
# 特徴量とターゲット変数の設定
X = df.drop(['harvest_yield'], axis=1)
y = df['harvest_yield']
# モデル訓練
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X, y)
# Streamlitダッシュボードの構築
st.title('農業データ解析ダッシュボード')
st.write("このダッシュボードでは、農業データに基づいた収穫予測を行います。")
# ユーザーインターフェイスの設定
temperature = st.slider("Temperature (°C)", min_value=0, max_value=40, value=25)
humidity = st.slider("Humidity (%)", min_value=0, max_value=100, value=60)
# 特徴量の入力(ここでは単純な例を使用)
input_data = pd.DataFrame({
'temperature': [temperature],
'humidity': [humidity],
'sunlight': [5], # 仮のデータ
'soil_moisture': [30] # 仮のデータ
})
# 予測値の表示
prediction = model.predict(input_data)
st.write(f"予測収穫量: {prediction[0]:.2f} kg")
これで、5つのノートブックがそれぞれ完成です。
01_data_cleaning.ipynb :欠損値の処理や外れ値除去、型変換を行う
02_feature_engineering.ipynb :生育条件に基づく特徴量を作成
03_model_training.ipynb :回帰モデル(ランダムフォレスト)を訓練
04_evaluation.ipynb :RMSEやF1スコアでモデルを評価、結果を可視化
05_dashboard_streamlit.ipynb :簡易的なStreamlitダッシュボードで収穫予測
# week01_opencv_intro.ipynb:OpenCV 基本操作
import cv2
import matplotlib.pyplot as plt
import numpy as np
# Google Colab環境用: サンプル画像のアップロード
from google.colab import files
uploaded = files.upload()
# 画像読み込み
image_path = list(uploaded.keys())[0] # 最初にアップロードされたファイル
img = cv2.imread(image_path)
# OpenCV は BGR → RGB に変換して表示する必要あり
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 画像表示
plt.figure(figsize=(8, 6))
plt.imshow(img_rgb)
plt.title("📷 入力画像")
plt.axis("off")
plt.show()
# --- 基本操作 ---
# グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# リサイズ
resized = cv2.resize(img_rgb, (200, 200))
# 回転(中心回転)
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, 45, 1.0) # 45度回転
rotated = cv2.warpAffine(img_rgb, matrix, (w, h))
# 画像の保存(例)
cv2.imwrite("rotated_image.jpg", rotated)
# --- 表示まとめ ---
fig, ax = plt.subplots(1, 3, figsize=(15, 4))
ax[0].imshow(gray, cmap='gray')
ax[0].set_title("Grayscale")
ax[1].imshow(resized)
ax[1].set_title("Resized")
ax[2].imshow(rotated)
ax[2].set_title("Rotated (45°)")
for a in ax:
a.axis("off")
plt.tight_layout()
plt.show()
# week02_opencv_processing.ipynb:OpenCV 前処理編
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# ファイルアップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. 平滑化(ぼかし処理)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 2. しきい値処理(二値化)
_, binary = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)
# 3. Canny エッジ検出
edges = cv2.Canny(blur, 50, 150)
# 4. 輪郭検出
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contour_img = img_rgb.copy()
cv2.drawContours(contour_img, contours, -1, (255, 0, 0), 2)
# 画像表示まとめ
titles = ["グレースケール", "ぼかし処理", "二値化", "エッジ検出", "輪郭抽出"]
images = [gray, blur, binary, edges, contour_img]
plt.figure(figsize=(15, 8))
for i in range(5):
plt.subplot(2, 3, i+1)
if i < 4:
plt.imshow(images[i], cmap='gray')
else:
plt.imshow(images[i])
plt.title(titles[i])
plt.axis("off")
plt.tight_layout()
plt.show()
# week03_contour_analysis.ipynb:輪郭解析と特徴量抽出
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# ファイルアップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 前処理:ぼかし + 二値化
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, binary = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)
# 輪郭抽出
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 輪郭ごとの特徴量抽出
features = []
annotated_img = img_rgb.copy()
for i, cnt in enumerate(contours):
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True)
# バウンディングボックス
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = float(w) / h
# ラベル描画
cv2.rectangle(annotated_img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(annotated_img, f"#{i+1}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
features.append({
"ID": i+1,
"Area": area,
"Perimeter": perimeter,
"AspectRatio": aspect_ratio,
"BoundingBox": (x, y, w, h)
})
# 結果表示
plt.figure(figsize=(8, 6))
plt.imshow(annotated_img)
plt.title("輪郭と特徴量の可視化")
plt.axis("off")
plt.show()
# 特徴量テーブル表示
import pandas as pd
df_features = pd.DataFrame(features)
df_features
# week04_histogram_analysis.ipynb:色ヒストグラムとマスク処理
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# --- 色ヒストグラムの表示(RGB別) ---
colors = ('r', 'g', 'b')
plt.figure(figsize=(10, 4))
for i, col in enumerate(colors):
hist = cv2.calcHist([img_rgb], [i], None, [256], [0, 256])
plt.plot(hist, color=col)
plt.xlim([0, 256])
plt.title("RGBヒストグラム")
plt.xlabel("画素値")
plt.ylabel("出現頻度")
plt.grid(True)
plt.show()
# --- HSV変換と色抽出(例:緑) ---
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# 緑の範囲を指定
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
# マスク作成と適用
mask = cv2.inRange(hsv, lower_green, upper_green)
result = cv2.bitwise_and(img_rgb, img_rgb, mask=mask)
# 結果表示
fig, ax = plt.subplots(1, 3, figsize=(16, 5))
ax[0].imshow(img_rgb)
ax[0].set_title("元画像")
ax[1].imshow(mask, cmap='gray')
ax[1].set_title("緑色マスク")
ax[2].imshow(result)
ax[2].set_title("抽出結果(緑)")
for a in ax:
a.axis("off")
plt.tight_layout()
plt.show()
# week05_geometry_transforms.ipynb:幾何変換・リサイズ・切り出し・回転
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# --- 1. リサイズ ---
resized = cv2.resize(img_rgb, (300, 300))
# --- 2. 切り出し(ROI)---
h, w = img_rgb.shape[:2]
roi = img_rgb[h//4:h//4*3, w//4:w//4*3]
# --- 3. 回転(中心回転) ---
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, angle=45, scale=1.0)
rotated = cv2.warpAffine(img_rgb, matrix, (w, h))
# --- 4. アフィン変換 ---
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[60, 70], [220, 50], [70, 250]])
M_affine = cv2.getAffineTransform(pts1, pts2)
affine = cv2.warpAffine(img_rgb, M_affine, (w, h))
# --- 表示 ---
titles = ['Original', 'Resized', 'ROI', 'Rotated', 'Affine Transform']
images = [img_rgb, resized, roi, rotated, affine]
plt.figure(figsize=(16, 8))
for i in range(5):
plt.subplot(2, 3, i+1)
plt.imshow(images[i])
plt.title(titles[i])
plt.axis('off')
plt.tight_layout()
plt.show()
# week06_masking_roi.ipynb:色と輪郭を用いたROIマスク抽出
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# --- 緑色の範囲をマスク ---
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# --- 輪郭抽出 ---
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# --- 最大輪郭を抽出(最も大きな葉など) ---
max_contour = max(contours, key=cv2.contourArea)
roi_mask = np.zeros_like(mask)
cv2.drawContours(roi_mask, [max_contour], -1, 255, -1) # 塗りつぶし
# --- マスクを用いてROI画像を抽出 ---
roi_extracted = cv2.bitwise_and(img_rgb, img_rgb, mask=roi_mask)
# --- ヒストグラム(ROI部分のみ) ---
masked_pixels = cv2.bitwise_and(hsv, hsv, mask=roi_mask)
hist_hue = cv2.calcHist([masked_pixels], [0], roi_mask, [180], [0, 180])
plt.figure(figsize=(14, 6))
plt.subplot(1, 3, 1)
plt.imshow(img_rgb)
plt.title("元画像")
plt.axis("off")
plt.subplot(1, 3, 2)
plt.imshow(roi_extracted)
plt.title("ROI抽出(最大葉)")
plt.axis("off")
plt.subplot(1, 3, 3)
plt.plot(hist_hue, color='g')
plt.title("ROI部分の色相ヒストグラム")
plt.xlabel("Hue (色相)")
plt.ylabel("出現頻度")
plt.grid(True)
plt.tight_layout()
plt.show()
# week07_compositing.ipynb:色・形状の統合特徴量をCSVとして抽出
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード(複数対応)
uploaded = files.upload()
data = []
for filename in uploaded.keys():
img = cv2.imread(filename)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# 色マスク(緑)
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# 輪郭検出
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 100: # 小さいノイズ除去
continue
# 輪郭マスク
roi_mask = np.zeros_like(mask)
cv2.drawContours(roi_mask, [cnt], -1, 255, -1)
# ROI領域のHSV取得
roi_pixels = cv2.bitwise_and(hsv, hsv, mask=roi_mask)
# 平均HSV値
mean_val = cv2.mean(roi_pixels, mask=roi_mask)
mean_h, mean_s, mean_v = mean_val[:3]
# 輪郭の外接矩形
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = w / h
# データに追加
data.append({
'filename': filename,
'area': area,
'mean_h': mean_h,
'mean_s': mean_s,
'mean_v': mean_v,
'aspect_ratio': aspect_ratio
})
# --- DataFrame化してCSV保存 ---
df = pd.DataFrame(data)
df.to_csv("roi_features.csv", index=False)
files.download("roi_features.csv")
# --- 表示 ---
print("抽出された特徴量(上位5件):")
print(df.head())
# --- 可視化(面積 vs 色相) ---
plt.figure(figsize=(8, 6))
plt.scatter(df['area'], df['mean_h'], c=df['mean_h'], cmap='hsv', s=30)
plt.xlabel("面積")
plt.ylabel("平均色相 (Hue)")
plt.title("面積 vs 色相")
plt.grid(True)
plt.colorbar(label='Hue')
plt.show()
# week08_leaf_area_calc.ipynb:葉面積をピクセル単位&cm²単位で測定
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# --- 画像アップロード ---
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# --- 緑色マスクの作成 ---
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# --- 輪郭抽出 ---
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# --- 面積測定(最大輪郭) ---
leaf_contour = max(contours, key=cv2.contourArea)
area_px = cv2.contourArea(leaf_contour)
# --- 実寸換算:キャリブレーション(仮に1px = 0.05cmとする) ---
px_to_cm = 0.05 # 1ピクセル = 0.05cm
area_cm2 = (area_px * (px_to_cm ** 2))
# --- 結果表示 ---
img_contour = img_rgb.copy()
cv2.drawContours(img_contour, [leaf_contour], -1, (255, 0, 0), 2)
plt.figure(figsize=(8, 6))
plt.imshow(img_contour)
plt.title(f"面積: {area_px:.1f} px² / 約 {area_cm2:.2f} cm²")
plt.axis("off")
plt.show()
# --- 数値出力 ---
print(f"ピクセル面積: {area_px:.1f} px²")
print(f"実寸換算面積: {area_cm2:.2f} cm² (換算係数: {px_to_cm}cm/pixel)")
# week09_cnn_intro.ipynb:KerasでCNNモデルを構築し、葉画像を分類
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# --- Google Drive連携(データフォルダを置く) ---
from google.colab import drive
drive.mount('/content/drive')
# --- データの準備 ---
# フォルダ構成:
# /content/drive/MyDrive/datasets/leaf_images/
# ├── healthy/
# └── diseased/
data_dir = '/content/drive/MyDrive/datasets/leaf_images/'
# --- データ拡張と前処理 ---
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2,
rotation_range=20,
zoom_range=0.2,
horizontal_flip=True
)
train_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='training'
)
val_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='validation'
)
# --- CNNモデル構築 ---
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
layers.MaxPooling2D(2, 2),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(128, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
# --- コンパイルと学習 ---
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(train_data, epochs=10, validation_data=val_data)
# --- 学習曲線の可視化 ---
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Val Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.title('CNN Accuracy')
plt.show()
# week10_cnn_build.ipynb:CNN構築・保存・テスト予測まで
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import os
# --- Google Drive マウント(データとモデル保存用) ---
from google.colab import drive
drive.mount('/content/drive')
# --- ディレクトリ設定 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/' # healthy / diseased のサブフォルダを持つ
model_save_path = '/content/drive/MyDrive/models/leaf_cnn_model.h5'
# --- データ前処理 ---
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2
)
train_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='training'
)
val_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='validation'
)
# --- CNN構築 ---
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
layers.MaxPooling2D(2, 2),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(128, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.3),
layers.Dense(1, activation='sigmoid')
])
# --- コンパイル・学習 ---
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(train_data, epochs=10, validation_data=val_data)
# --- モデル保存 ---
model.save(model_save_path)
print(f"✅ モデル保存完了:{model_save_path}")
# --- 学習曲線表示 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.legend()
plt.title('CNN Accuracy')
plt.show()
# --- 保存モデル読み込み ---
model = load_model(model_save_path)
# --- 単一画像による予測 ---
test_img_path = '/content/drive/MyDrive/test_leaf.jpg'
img = image.load_img(test_img_path, target_size=(128, 128))
img_array = image.img_to_array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0)
prediction = model.predict(img_array)[0][0]
label = "Diseased" if prediction > 0.5 else "Healthy"
plt.imshow(img)
plt.title(f"予測: {label}(スコア: {prediction:.2f})")
plt.axis('off')
plt.show()
# week11_data_aug.ipynb:データ拡張による画像分類精度向上
import os
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing import image
# --- 拡張前の画像を確認 ---
img_path = '/content/drive/MyDrive/datasets/leaf_images/healthy/001.jpg'
img = image.load_img(img_path, target_size=(128, 128))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
plt.imshow(img.astype("uint8"))
plt.title("Original Image")
plt.axis('off')
plt.show()
# --- ImageDataGenerator による拡張 ---
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
# --- 拡張画像の表示 ---
aug_iter = datagen.flow(img_array, batch_size=1)
plt.figure(figsize=(12, 6))
for i in range(6):
plt.subplot(2, 3, i+1)
batch = next(aug_iter)
image_aug = batch[0].astype("uint8")
plt.imshow(image_aug)
plt.axis('off')
plt.title(f'Augmented {i+1}')
plt.suptitle('🌿 Image Augmentation Samples')
plt.show()
# week12_transfer_learning.ipynb:学習済みモデルを活用した葉の分類
import tensorflow as tf
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
# --- Google Drive をマウント(データ/保存先用) ---
from google.colab import drive
drive.mount('/content/drive')
# --- データセット準備 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/' # healthy / diseased フォルダあり
img_size = (160, 160)
batch_size = 32
datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)
train_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='training'
)
val_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='validation'
)
# --- 転移学習モデル構築 ---
base_model = MobileNetV2(input_shape=img_size + (3,), include_top=False, weights='imagenet')
base_model.trainable = False # 転移学習の基本:特徴抽出部分は固定
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(64, activation='relu')(x)
output = Dense(1, activation='sigmoid')(x)
model = Model(inputs=base_model.input, outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# --- 学習 ---
history = model.fit(train_gen, validation_data=val_gen, epochs=5)
# --- 学習結果の可視化 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.legend()
plt.title('Transfer Learning Accuracy')
plt.show()
# week13_model_eval.ipynb:モデルの評価と評価指標の計算
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, classification_report
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# --- モデル読み込み ---
model_path = '/content/drive/MyDrive/models/leaf_cnn_model.h5'
model = load_model(model_path)
# --- データ準備 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/'
img_size = (160, 160)
batch_size = 32
datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)
val_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='validation'
)
# --- モデル評価 ---
loss, accuracy = model.evaluate(val_gen)
print(f"Loss: {loss:.4f}, Accuracy: {accuracy:.4f}")
# --- 混同行列の計算 ---
val_pred = model.predict(val_gen)
val_pred = (val_pred > 0.5).astype("int32") # 0.5で2クラス分類
true_labels = val_gen.classes
cm = confusion_matrix(true_labels, val_pred)
# --- 混同行列の表示 ---
plt.figure(figsize=(8, 6))
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
plt.colorbar()
classes = ['Healthy', 'Diseased']
plt.xticks(np.arange(len(classes)), classes, rotation=45)
plt.yticks(np.arange(len(classes)), classes)
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.tight_layout()
plt.show()
# --- 分類レポート ---
report = classification_report(true_labels, val_pred, target_names=classes)
print(report)
# week14_yolo_intro.ipynb:YOLO(You Only Look Once)を用いた物体検出
import cv2
import numpy as np
import matplotlib.pyplot as plt
# --- YOLOの設定 ---
yolo_cfg = '/content/drive/MyDrive/yolov3/yolov3.cfg'
yolo_weights = '/content/drive/MyDrive/yolov3/yolov3.weights'
yolo_names = '/content/drive/MyDrive/yolov3/coco.names'
# --- YOLOネットワークの読み込み ---
net = cv2.dnn.readNetFromDarknet(yolo_cfg, yolo_weights)
layer_names = net.getLayerNames()
output_layers = [layer_names[i-1] for i in net.getUnconnectedOutLayers()]
# --- 画像読み込みと前処理 ---
image_path = '/content/drive/MyDrive/datasets/test_image.jpg'
img = cv2.imread(image_path)
height, width, channels = img.shape
# YOLO用に画像の前処理
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
# --- 物体検出の実行 ---
outs = net.forward(output_layers)
# --- 検出結果の解析 ---
class_ids = []
confidences = []
boxes = []
conf_threshold = 0.5 # 信頼度の閾値
nms_threshold = 0.4 # 非最大抑制の閾値
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# バウンディングボックスの座標
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# --- 非最大抑制(NMS)の適用 ---
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
# --- 結果の描画 ---
font = cv2.FONT_HERSHEY_SIMPLEX
colors = np.random.uniform(0, 255, size=(len(class_ids), 3))
for i in range(len(boxes)):
if i in indices:
x, y, w, h = boxes[i]
label = str(class_ids[i])
confidence = confidences[i]
color = colors[i]
# バウンディングボックスとラベルの描画
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, f'{label} {confidence:.2f}', (x, y - 10), font, 0.5, color, 2)
# 結果の表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('YOLO Object Detection')
plt.axis('off')
plt.show()
# week15_yolo_agri.ipynb:農業向けYOLOを用いた作物検出
import cv2
import numpy as np
import matplotlib.pyplot as plt
# --- YOLOの設定(農業専用のデータセット) ---
yolo_cfg = '/content/drive/MyDrive/yolov3/yolov3_agri.cfg'
yolo_weights = '/content/drive/MyDrive/yolov3/yolov3_agri.weights'
yolo_names = '/content/drive/MyDrive/yolov3/agri_classes.names'
# --- YOLOネットワークの読み込み ---
net = cv2.dnn.readNetFromDarknet(yolo_cfg, yolo_weights)
layer_names = net.getLayerNames()
output_layers = [layer_names[i-1] for i in net.getUnconnectedOutLayers()]
# --- 農業画像の読み込み ---
image_path = '/content/drive/MyDrive/datasets/agri_test_image.jpg' # 農作物の画像
img = cv2.imread(image_path)
height, width, channels = img.shape
# YOLO用に画像の前処理
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
# --- 物体検出の実行 ---
outs = net.forward(output_layers)
# --- 検出結果の解析 ---
class_ids = []
confidences = []
boxes = []
conf_threshold = 0.5 # 信頼度の閾値
nms_threshold = 0.4 # 非最大抑制の閾値
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# バウンディングボックスの座標
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# --- 非最大抑制(NMS)の適用 ---
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
# --- 結果の描画 ---
font = cv2.FONT_HERSHEY_SIMPLEX
colors = np.random.uniform(0, 255, size=(len(class_ids), 3))
# 農業特有のクラス名
with open(yolo_names, 'r') as f:
classes = f.read().strip().split('\n')
for i in range(len(boxes)):
if i in indices:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
confidence = confidences[i]
color = colors[i]
# バウンディングボックスとラベルの描画
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, f'{label} {confidence:.2f}', (x, y - 10), font, 0.5, color, 2)
# 結果の表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('YOLO Object Detection - Agriculture')
plt.axis('off')
plt.show()
# week16_disease_classify.ipynb:病気画像分類(例:葉の病害)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from sklearn.metrics import classification_report, confusion_matrix
# --- データセットの準備 ---
# ディレクトリ構成(例):
# /content/drive/MyDrive/datasets/leaf_disease/
# ├── train/
# │ ├── healthy/
# │ └── diseased/
# └── val/
# ├── healthy/
# └── diseased/
train_dir = '/content/drive/MyDrive/datasets/leaf_disease/train'
val_dir = '/content/drive/MyDrive/datasets/leaf_disease/val'
img_size = (128, 128)
batch_size = 32
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary'
)
val_generator = val_datagen.flow_from_directory(
val_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary'
)
# --- モデル構築(CNN) ---
model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
MaxPooling2D(2, 2),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(2, 2),
Flatten(),
Dense(128, activation='relu'),
Dropout(0.3),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# --- モデル学習 ---
history = model.fit(
train_generator,
epochs=10,
validation_data=val_generator
)
# --- 学習の可視化 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Disease Classification Accuracy')
plt.legend()
plt.show()
# --- モデル評価 ---
val_generator.reset()
preds = model.predict(val_generator, verbose=1)
y_pred = (preds > 0.5).astype('int32').flatten()
y_true = val_generator.classes
print(classification_report(y_true, y_pred))
print("Confusion Matrix:")
print(confusion_matrix(y_true, y_pred))
以下が、画像処理の入門として構成されたノートブック week01_opencv_intro.ipynb の内容です。
この週では、 OpenCV を用いた画像の基本操作(読み込み・表示・加工) を通して、農業画像処理への第一歩を学びます。
week01_opencv_intro.ipynb
# week01_opencv_intro.ipynb:OpenCV 基本操作
import cv2
import matplotlib.pyplot as plt
import numpy as np
# Google Colab環境用: サンプル画像のアップロード
from google.colab import files
uploaded = files.upload()
# 画像読み込み
image_path = list(uploaded.keys())[0] # 最初にアップロードされたファイル
img = cv2.imread(image_path)
# OpenCV は BGR → RGB に変換して表示する必要あり
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 画像表示
plt.figure(figsize=(8, 6))
plt.imshow(img_rgb)
plt.title("📷 入力画像")
plt.axis("off")
plt.show()
# --- 基本操作 ---
# グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# リサイズ
resized = cv2.resize(img_rgb, (200, 200))
# 回転(中心回転)
(h, w) = img.shape[:2]
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, 45, 1.0) # 45度回転
rotated = cv2.warpAffine(img_rgb, matrix, (w, h))
# 画像の保存(例)
cv2.imwrite("rotated_image.jpg", rotated)
# --- 表示まとめ ---
fig, ax = plt.subplots(1, 3, figsize=(15, 4))
ax[0].imshow(gray, cmap='gray')
ax[0].set_title("Grayscale")
ax[1].imshow(resized)
ax[1].set_title("Resized")
ax[2].imshow(rotated)
ax[2].set_title("Rotated (45°)")
for a in ax:
a.axis("off")
plt.tight_layout()
plt.show()
機能 | 説明 |
cv2.imread() | 画像ファイルの読み込み |
cv2.cvtColor() | カラーモード変換(BGR→RGB, グレースケールなど) |
cv2.resize() | 任意サイズへの画像リサイズ |
cv2.getRotationMatrix2D() + warpAffine() | 画像の回転 |
cv2.imwrite() | 加工画像の保存 |
以下は、OpenCV を使った画像の前処理テクニックを扱うノートブック week02_opencv_processing.ipynb の内容です。
この週では、 農業画像処理の基礎的なフィルタリング・しきい値処理・輪郭抽出 など、次週以降の分析や分類処理に繋がる重要な前処理手法を学びます。
week02_opencv_processing.ipynb
# week02_opencv_processing.ipynb:OpenCV 前処理編
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# ファイルアップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# グレースケール変換
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 1. 平滑化(ぼかし処理)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# 2. しきい値処理(二値化)
_, binary = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)
# 3. Canny エッジ検出
edges = cv2.Canny(blur, 50, 150)
# 4. 輪郭検出
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contour_img = img_rgb.copy()
cv2.drawContours(contour_img, contours, -1, (255, 0, 0), 2)
# 画像表示まとめ
titles = ["グレースケール", "ぼかし処理", "二値化", "エッジ検出", "輪郭抽出"]
images = [gray, blur, binary, edges, contour_img]
plt.figure(figsize=(15, 8))
for i in range(5):
plt.subplot(2, 3, i+1)
if i < 4:
plt.imshow(images[i], cmap='gray')
else:
plt.imshow(images[i])
plt.title(titles[i])
plt.axis("off")
plt.tight_layout()
plt.show()
処理手法 内容・活用例
次週(Week03)では、抽出した輪郭やエリアに対する「定量評価」(面積・周囲長・形状特徴)や「色のヒストグラム分析」などを扱います。
こちらが、輪郭や領域の定量的な解析にフォーカスしたノートブック week03_contour_analysis.ipynb の内容です。
この週では、 OpenCVで得た輪郭に対する面積・周囲長・アスペクト比・ヒストグラム などの特徴量抽出を行い、画像からの定量データ取得を体験します。
week03_contour_analysis.ipynb
# week03_contour_analysis.ipynb:輪郭解析と特徴量抽出
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# ファイルアップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 前処理:ぼかし + 二値化
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, binary = cv2.threshold(blur, 100, 255, cv2.THRESH_BINARY)
# 輪郭抽出
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 輪郭ごとの特徴量抽出
features = []
annotated_img = img_rgb.copy()
for i, cnt in enumerate(contours):
area = cv2.contourArea(cnt)
perimeter = cv2.arcLength(cnt, True)
# バウンディングボックス
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = float(w) / h
# ラベル描画
cv2.rectangle(annotated_img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(annotated_img, f"#{i+1}", (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
features.append({
"ID": i+1,
"Area": area,
"Perimeter": perimeter,
"AspectRatio": aspect_ratio,
"BoundingBox": (x, y, w, h)
})
# 結果表示
plt.figure(figsize=(8, 6))
plt.imshow(annotated_img)
plt.title("輪郭と特徴量の可視化")
plt.axis("off")
plt.show()
# 特徴量テーブル表示
import pandas as pd
df_features = pd.DataFrame(features)
df_features
機能・処理 | 説明 |
cv2.contourArea() | 面積の計算 |
cv2.arcLength() | 周囲長の計算 |
cv2.boundingRect() | バウンディングボックスとアスペクト比 |
cv2.putText() | 輪郭にIDを表示する |
次週(Week04)は、ヒストグラムや色空間の変換を使って、より高度な 色解析・領域分割 へと進みます。
以下は、色解析とヒストグラムを中心に学ぶノートブック week04_histogram_analysis.ipynb の内容です。
この週では、 画像内の色情報の可視化・ヒストグラムの解析・しきい値処理や色抽出の応用 を学び、葉の健康状態や作物の熟度判定などへの応用の基礎を固めます。
week04_histogram_analysis.ipynb
# week04_histogram_analysis.ipynb:色ヒストグラムとマスク処理
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# --- 色ヒストグラムの表示(RGB別) ---
colors = ('r', 'g', 'b')
plt.figure(figsize=(10, 4))
for i, col in enumerate(colors):
hist = cv2.calcHist([img_rgb], [i], None, [256], [0, 256])
plt.plot(hist, color=col)
plt.xlim([0, 256])
plt.title("RGBヒストグラム")
plt.xlabel("画素値")
plt.ylabel("出現頻度")
plt.grid(True)
plt.show()
# --- HSV変換と色抽出(例:緑) ---
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# 緑の範囲を指定
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
# マスク作成と適用
mask = cv2.inRange(hsv, lower_green, upper_green)
result = cv2.bitwise_and(img_rgb, img_rgb, mask=mask)
# 結果表示
fig, ax = plt.subplots(1, 3, figsize=(16, 5))
ax[0].imshow(img_rgb)
ax[0].set_title("元画像")
ax[1].imshow(mask, cmap='gray')
ax[1].set_title("緑色マスク")
ax[2].imshow(result)
ax[2].set_title("抽出結果(緑)")
for a in ax:
a.axis("off")
plt.tight_layout()
plt.show()
説明 | |
cv2.calcHist() | 色ヒストグラムの算出(R, G, B) |
cv2.cvtColor() | RGB → HSV 変換(色抽出しやすい) |
cv2.inRange() | 指定色の範囲をマスクとして抽出 |
cv2.bitwise_and() | マスクに基づく色領域の抽出 |
次週(Week05)は、色情報と輪郭・面積などの 統合特徴量分析 を通じて、「分類」や「異常検知」につながる応用を学びます。
以下は、画像の幾何変換やリサイズ・回転・切り出しを扱うノートブック week05_geometry_transforms.ipynb の内容です。
この週では、 画像前処理の一環としての座標操作やアフィン変換の基礎 を身につけ、データ拡張や部分領域の抽出(ROI)などの応用を可能にします。
week05_geometry_transforms.ipynb
# week05_geometry_transforms.ipynb:幾何変換・リサイズ・切り出し・回転
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# --- 1. リサイズ ---
resized = cv2.resize(img_rgb, (300, 300))
# --- 2. 切り出し(ROI)---
h, w = img_rgb.shape[:2]
roi = img_rgb[h//4:h//4*3, w//4:w//4*3]
# --- 3. 回転(中心回転) ---
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, angle=45, scale=1.0)
rotated = cv2.warpAffine(img_rgb, matrix, (w, h))
# --- 4. アフィン変換 ---
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
pts2 = np.float32([[60, 70], [220, 50], [70, 250]])
M_affine = cv2.getAffineTransform(pts1, pts2)
affine = cv2.warpAffine(img_rgb, M_affine, (w, h))
# --- 表示 ---
titles = ['Original', 'Resized', 'ROI', 'Rotated', 'Affine Transform']
images = [img_rgb, resized, roi, rotated, affine]
plt.figure(figsize=(16, 8))
for i in range(5):
plt.subplot(2, 3, i+1)
plt.imshow(images[i])
plt.title(titles[i])
plt.axis('off')
plt.tight_layout()
plt.show()
処理内容 | 使用関数 | 応用例 |
リサイズ | cv2.resize | 学習用データの統一 |
ROI抽出 | 配列スライス | 葉や果実の一部だけを分析 |
回転 | getRotationMatrix2D , warpAffine | データ拡張、角度補正 |
アフィン変換 | getAffineTransform , warpAffine | 視点補正、形状変形 |
次週(Week06)では、こうした前処理された画像から特徴を抽出し、 機械学習モデルや分類アルゴリズム に接続していきます。
こちらが、画像内の特定領域(ROI)を マスク処理 によって抽出・分析するためのノートブック week06_masking_roi.ipynb の内容です。
この週では、 色抽出+輪郭+マスクの組み合わせ により、「分析対象を限定して処理する」実践力を養います。
week06_masking_roi.ipynb
# week06_masking_roi.ipynb:色と輪郭を用いたROIマスク抽出
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# --- 緑色の範囲をマスク ---
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# --- 輪郭抽出 ---
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# --- 最大輪郭を抽出(最も大きな葉など) ---
max_contour = max(contours, key=cv2.contourArea)
roi_mask = np.zeros_like(mask)
cv2.drawContours(roi_mask, [max_contour], -1, 255, -1) # 塗りつぶし
# --- マスクを用いてROI画像を抽出 ---
roi_extracted = cv2.bitwise_and(img_rgb, img_rgb, mask=roi_mask)
# --- ヒストグラム(ROI部分のみ) ---
masked_pixels = cv2.bitwise_and(hsv, hsv, mask=roi_mask)
hist_hue = cv2.calcHist([masked_pixels], [0], roi_mask, [180], [0, 180])
plt.figure(figsize=(14, 6))
plt.subplot(1, 3, 1)
plt.imshow(img_rgb)
plt.title("元画像")
plt.axis("off")
plt.subplot(1, 3, 2)
plt.imshow(roi_extracted)
plt.title("ROI抽出(最大葉)")
plt.axis("off")
plt.subplot(1, 3, 3)
plt.plot(hist_hue, color='g')
plt.title("ROI部分の色相ヒストグラム")
plt.xlabel("Hue (色相)")
plt.ylabel("出現頻度")
plt.grid(True)
plt.tight_layout()
plt.show()
説明 | |
色範囲指定マスク | 緑色など特定範囲をマスクで抽出 |
最大輪郭の抽出 | 面積最大の葉や果実の選別 |
マスク付きbitwise_and処理 | ROIのみ可視化・処理対象とする |
ROI領域のヒストグラム解析 | ROI限定の色情報・形状情報取得 |
次週(Week07)では、こうして抽出したROIからの 統合特徴量(色 + 形状)分析 を行い、分類・クラスタリングなどの機械学習応用へ接続していきます。
こちらが、画像から抽出した 色・形状・面積などの特徴量を統合して分析 するためのノートブック week07_compositing.ipynb の内容です。
この週では、ROI単位で得られた情報を 構造化データとして整形・比較 することで、分類や時系列分析への土台を作ります。
week07_compositing.ipynb
# week07_compositing.ipynb:色・形状の統合特徴量をCSVとして抽出
import cv2
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from google.colab import files
# 画像アップロード(複数対応)
uploaded = files.upload()
data = []
for filename in uploaded.keys():
img = cv2.imread(filename)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# 色マスク(緑)
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# 輪郭検出
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 100: # 小さいノイズ除去
continue
# 輪郭マスク
roi_mask = np.zeros_like(mask)
cv2.drawContours(roi_mask, [cnt], -1, 255, -1)
# ROI領域のHSV取得
roi_pixels = cv2.bitwise_and(hsv, hsv, mask=roi_mask)
# 平均HSV値
mean_val = cv2.mean(roi_pixels, mask=roi_mask)
mean_h, mean_s, mean_v = mean_val[:3]
# 輪郭の外接矩形
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = w / h
# データに追加
data.append({
'filename': filename,
'area': area,
'mean_h': mean_h,
'mean_s': mean_s,
'mean_v': mean_v,
'aspect_ratio': aspect_ratio
})
# --- DataFrame化してCSV保存 ---
df = pd.DataFrame(data)
df.to_csv("roi_features.csv", index=False)
files.download("roi_features.csv")
# --- 表示 ---
print("抽出された特徴量(上位5件):")
print(df.head())
# --- 可視化(面積 vs 色相) ---
plt.figure(figsize=(8, 6))
plt.scatter(df['area'], df['mean_h'], c=df['mean_h'], cmap='hsv', s=30)
plt.xlabel("面積")
plt.ylabel("平均色相 (Hue)")
plt.title("面積 vs 色相")
plt.grid(True)
plt.colorbar(label='Hue')
plt.show()
要素 | 内容 |
色特徴 | 平均 Hue / Saturation / Value |
形状特徴 | 面積・アスペクト比(縦横比) |
多画像対応 | 複数ファイルをまとめて処理 |
CSV化 | 構造化データとして出力・学習準備 |
散布図分析 | 異常検知やクラスタリングの布石に |
🌾 農業応用の例 | |
複数の果実・葉の 特徴量を一括取得 → 健康度や熟度の比較 |
色と形状を統合したクラスタリング・分類モデルへの応用
作物の成長追跡(時系列の平均色相・面積など)
次週(Week08)は、この特徴量CSVを使った機械学習入門(分類やクラスタリング)に発展させていきます。必要であれば続きもお渡しできます!
こちらが、画像から葉の 面積をより高精度に算出 するためのノートブック week08_leaf_area_calc.ipynb の内容です。
この週では、 実寸換算・輪郭面積の厳密取得 を通じて、農業現場で重要な「葉面積の定量評価」を可能にします。
week08_leaf_area_calc.ipynb
# week08_leaf_area_calc.ipynb:葉面積をピクセル単位&cm²単位で測定
import cv2
import numpy as np
import matplotlib.pyplot as plt
from google.colab import files
# --- 画像アップロード ---
uploaded = files.upload()
image_path = list(uploaded.keys())[0]
img = cv2.imread(image_path)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
# --- 緑色マスクの作成 ---
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)
# --- 輪郭抽出 ---
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# --- 面積測定(最大輪郭) ---
leaf_contour = max(contours, key=cv2.contourArea)
area_px = cv2.contourArea(leaf_contour)
# --- 実寸換算:キャリブレーション(仮に1px = 0.05cmとする) ---
px_to_cm = 0.05 # 1ピクセル = 0.05cm
area_cm2 = (area_px * (px_to_cm ** 2))
# --- 結果表示 ---
img_contour = img_rgb.copy()
cv2.drawContours(img_contour, [leaf_contour], -1, (255, 0, 0), 2)
plt.figure(figsize=(8, 6))
plt.imshow(img_contour)
plt.title(f"面積: {area_px:.1f} px² / 約 {area_cm2:.2f} cm²")
plt.axis("off")
plt.show()
# --- 数値出力 ---
print(f"ピクセル面積: {area_px:.1f} px²")
print(f"実寸換算面積: {area_cm2:.2f} cm² (換算係数: {px_to_cm}cm/pixel)")
内容 | 説明 |
色マスク+輪郭抽出 | 葉部分を特定し、最大輪郭を抽出 |
輪郭面積(ピクセル) | cv2.contourArea() を使用 |
実寸換算 | キャリブレーション係数に基づく |
視覚化と定量評価 | 作物ごとの成長・健康評価へ応用可 |
肥料・環境条件との関係性分析に活用
次週(Week09)は、葉面積や色などの定量データを活用して、 分類や異常検知(機械学習入門) へと進んでいきます。
こちらが、画像分類の基礎として CNN(畳み込みニューラルネットワーク) を使ったモデル構築の入門ノートブック
week09_cnn_intro.ipynb の内容です。
この週では、葉の画像を使って「病気 or 健康」などの 2クラス分類タスク に取り組み、 画像→判定 のパイプライン構築を体験します。
week09_cnn_intro.ipynb
# week09_cnn_intro.ipynb:KerasでCNNモデルを構築し、葉画像を分類
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# --- Google Drive連携(データフォルダを置く) ---
from google.colab import drive
drive.mount('/content/drive')
# --- データの準備 ---
# フォルダ構成:
# /content/drive/MyDrive/datasets/leaf_images/
# ├── healthy/
# └── diseased/
data_dir = '/content/drive/MyDrive/datasets/leaf_images/'
# --- データ拡張と前処理 ---
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2,
rotation_range=20,
zoom_range=0.2,
horizontal_flip=True
)
train_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='training'
)
val_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='validation'
)
# --- CNNモデル構築 ---
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
layers.MaxPooling2D(2, 2),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(128, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
# --- コンパイルと学習 ---
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(train_data, epochs=10, validation_data=val_data)
# --- 学習曲線の可視化 ---
plt.plot(history.history['accuracy'], label='Train Accuracy')
plt.plot(history.history['val_accuracy'], label='Val Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend()
plt.title('CNN Accuracy')
plt.show()
項目 | 内容 |
CNNの構造 | 畳み込み → プーリング → Flatten → 全結合 |
データ前処理 | 正規化・データ拡張(回転・ズーム) |
分類の実行 | 健康葉 vs 病葉など |
モデル評価 | 精度と損失の可視化 |
こちらが、画像分類モデルを本格構築・保存し、今後の応用(ダッシュボードやアプリ連携)に備えるノートブック
week10_cnn_build.ipynb の内容です。
この週では、 モデルの再構成・保存・読み込み までの流れを体験し、実運用につながる技術を身につけます。
week10_cnn_build.ipynb
# week10_cnn_build.ipynb:CNN構築・保存・テスト予測まで
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, models
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import os
# --- Google Drive マウント(データとモデル保存用) ---
from google.colab import drive
drive.mount('/content/drive')
# --- ディレクトリ設定 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/' # healthy / diseased のサブフォルダを持つ
model_save_path = '/content/drive/MyDrive/models/leaf_cnn_model.h5'
# --- データ前処理 ---
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2
)
train_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='training'
)
val_data = datagen.flow_from_directory(
data_dir,
target_size=(128, 128),
batch_size=32,
class_mode='binary',
subset='validation'
)
# --- CNN構築 ---
model = models.Sequential([
layers.Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
layers.MaxPooling2D(2, 2),
layers.Conv2D(64, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(128, (3, 3), activation='relu'),
layers.MaxPooling2D(2, 2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.3),
layers.Dense(1, activation='sigmoid')
])
# --- コンパイル・学習 ---
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
history = model.fit(train_data, epochs=10, validation_data=val_data)
# --- モデル保存 ---
model.save(model_save_path)
print(f"✅ モデル保存完了:{model_save_path}")
# --- 学習曲線表示 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.legend()
plt.title('CNN Accuracy')
plt.show()
# --- 保存モデル読み込み ---
model = load_model(model_save_path)
# --- 単一画像による予測 ---
test_img_path = '/content/drive/MyDrive/test_leaf.jpg'
img = image.load_img(test_img_path, target_size=(128, 128))
img_array = image.img_to_array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0)
prediction = model.predict(img_array)[0][0]
label = "Diseased" if prediction > 0.5 else "Healthy"
plt.imshow(img)
plt.title(f"予測: {label}(スコア: {prediction:.2f})")
plt.axis('off')
plt.show()
項目 | 内容 |
CNN構成の強化 | Dropout追加などで汎化能力アップ |
モデルの保存・再利用 | .h5 形式で保存し、Streamlit等へ展開可能 |
単一画像の分類テスト | 実運用を想定したスクリプト形式 |
次回(Week11)は、 Streamlitでダッシュボード化 して、農業現場で使える予測アプリを作ります。
必要であれば week11_streamlit_dashboard.ipynb もご提供しますので、お気軽にどうぞ!
こちらが、 データ拡張(Data Augmentation) にフォーカスしたノートブック
week11_data_aug.ipynb の内容です。
この週では、農業データにありがちな「データ不足・偏り」を解消するため、 Keras の ImageDataGenerator を用いた拡張 や、 Albumentationsライブラリの利用 にも触れていきます。
week11_data_aug.ipynb
# week11_data_aug.ipynb:データ拡張による画像分類精度向上
import os
import matplotlib.pyplot as plt
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.preprocessing import image
# --- 拡張前の画像を確認 ---
img_path = '/content/drive/MyDrive/datasets/leaf_images/healthy/001.jpg'
img = image.load_img(img_path, target_size=(128, 128))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
plt.imshow(img.astype("uint8"))
plt.title("Original Image")
plt.axis('off')
plt.show()
# --- ImageDataGenerator による拡張 ---
datagen = ImageDataGenerator(
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
# --- 拡張画像の表示 ---
aug_iter = datagen.flow(img_array, batch_size=1)
plt.figure(figsize=(12, 6))
for i in range(6):
plt.subplot(2, 3, i+1)
batch = next(aug_iter)
image_aug = batch[0].astype("uint8")
plt.imshow(image_aug)
plt.axis('off')
plt.title(f'Augmented {i+1}')
plt.suptitle('🌿 Image Augmentation Samples')
plt.show()
# インストール(初回のみ)
!pip install -q albumentations
import albumentations as A
from PIL import Image
# --- 拡張パイプライン ---
transform = A.Compose([
A.RandomCrop(width=120, height=120),
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.2),
A.Rotate(limit=40),
A.Resize(128, 128)
])
# --- 拡張適用 ---
img_pil = Image.open(img_path).convert("RGB")
img_np = np.array(img_pil)
augmented = transform(image=img_np)['image']
plt.imshow(augmented)
plt.title("Albumentations Example")
plt.axis('off')
plt.show()
技術 | 内容 |
ImageDataGenerator | Keras標準のリアルタイム拡張 |
Albumentations | 高速で多彩な拡張手法(Pytorchにも対応) |
実装パターン | 可視化/保存/訓練データへの導入方法 |
こちらが、 転移学習(Transfer Learning) を使って、学習済みモデル(例:MobileNetV2)を農業の画像分類に応用するノートブック
week12_transfer_learning.ipynb の内容です。
week12_transfer_learning.ipynb
# week12_transfer_learning.ipynb:学習済みモデルを活用した葉の分類
import tensorflow as tf
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Dense, GlobalAveragePooling2D
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import matplotlib.pyplot as plt
# --- Google Drive をマウント(データ/保存先用) ---
from google.colab import drive
drive.mount('/content/drive')
# --- データセット準備 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/' # healthy / diseased フォルダあり
img_size = (160, 160)
batch_size = 32
datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)
train_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='training'
)
val_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='validation'
)
# --- 転移学習モデル構築 ---
base_model = MobileNetV2(input_shape=img_size + (3,), include_top=False, weights='imagenet')
base_model.trainable = False # 転移学習の基本:特徴抽出部分は固定
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(64, activation='relu')(x)
output = Dense(1, activation='sigmoid')(x)
model = Model(inputs=base_model.input, outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# --- 学習 ---
history = model.fit(train_gen, validation_data=val_gen, epochs=5)
# --- 学習結果の可視化 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.legend()
plt.title('Transfer Learning Accuracy')
plt.show()
🔄 Fine-tuning(オプション)
# --- base_modelの一部を再学習可能に ---
base_model.trainable = True
# 最後の数層のみ再学習する(例:100層以降)
fine_tune_at = 100
for layer in base_model.layers[:fine_tune_at]:
layer.trainable = False
model.compile(optimizer=tf.keras.optimizers.Adam(1e-5),
loss='binary_crossentropy',
metrics=['accuracy'])
fine_tune_epochs = 5
history_fine = model.fit(train_gen, validation_data=val_gen, epochs=fine_tune_epochs)
# --- 再学習後の精度表示 ---
plt.plot(history_fine.history['accuracy'], label='Train Acc (fine)')
plt.plot(history_fine.history['val_accuracy'], label='Val Acc (fine)')
plt.legend()
plt.title('Fine-Tuning Accuracy')
plt.show()
項目 | 内容 |
転移学習の基本 | 学習済みCNN(MobileNetV2)の利用方法 |
特徴抽出 vs 再学習 | base_modelのtrainableの切替 |
少ないデータでの高精度 | 実用性・少量データの応用に強い |
# week13_model_eval.ipynb:モデルの評価と評価指標の計算
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix, classification_report
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# --- モデル読み込み ---
model_path = '/content/drive/MyDrive/models/leaf_cnn_model.h5'
model = load_model(model_path)
# --- データ準備 ---
data_dir = '/content/drive/MyDrive/datasets/leaf_images/'
img_size = (160, 160)
batch_size = 32
datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)
val_gen = datagen.flow_from_directory(
data_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary',
subset='validation'
)
# --- モデル評価 ---
loss, accuracy = model.evaluate(val_gen)
print(f"Loss: {loss:.4f}, Accuracy: {accuracy:.4f}")
# --- 混同行列の計算 ---
val_pred = model.predict(val_gen)
val_pred = (val_pred > 0.5).astype("int32") # 0.5で2クラス分類
true_labels = val_gen.classes
cm = confusion_matrix(true_labels, val_pred)
# --- 混同行列の表示 ---
plt.figure(figsize=(8, 6))
plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
plt.title('Confusion Matrix')
plt.colorbar()
classes = ['Healthy', 'Diseased']
plt.xticks(np.arange(len(classes)), classes, rotation=45)
plt.yticks(np.arange(len(classes)), classes)
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.tight_layout()
plt.show()
# --- 分類レポート ---
report = classification_report(true_labels, val_pred, target_names=classes)
print(report)
評価項目 | 内容 |
評価指標 | Loss , Accuracy などの基本評価指標 |
混同行列 | モデルの誤分類を視覚化して理解 |
分類レポート | 精度、再現率、F1スコアを一目で確認 |
2クラス分類 | threshold を用いた分類の精度評価 |
次は、評価結果を元に、 モデルの改善 や 再学習 に向けたアクションプランを考えます。
こちらが、 YOLO(You Only Look Once)による物体検出 に関するノートブック、
week14_yolo_intro.ipynb の内容です。
# week14_yolo_intro.ipynb:YOLO(You Only Look Once)を用いた物体検出
import cv2
import numpy as np
import matplotlib.pyplot as plt
# --- YOLOの設定 ---
yolo_cfg = '/content/drive/MyDrive/yolov3/yolov3.cfg'
yolo_weights = '/content/drive/MyDrive/yolov3/yolov3.weights'
yolo_names = '/content/drive/MyDrive/yolov3/coco.names'
# --- YOLOネットワークの読み込み ---
net = cv2.dnn.readNetFromDarknet(yolo_cfg, yolo_weights)
layer_names = net.getLayerNames()
output_layers = [layer_names[i-1] for i in net.getUnconnectedOutLayers()]
# --- 画像読み込みと前処理 ---
image_path = '/content/drive/MyDrive/datasets/test_image.jpg'
img = cv2.imread(image_path)
height, width, channels = img.shape
# YOLO用に画像の前処理
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
# --- 物体検出の実行 ---
outs = net.forward(output_layers)
# --- 検出結果の解析 ---
class_ids = []
confidences = []
boxes = []
conf_threshold = 0.5 # 信頼度の閾値
nms_threshold = 0.4 # 非最大抑制の閾値
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# バウンディングボックスの座標
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# --- 非最大抑制(NMS)の適用 ---
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
# --- 結果の描画 ---
font = cv2.FONT_HERSHEY_SIMPLEX
colors = np.random.uniform(0, 255, size=(len(class_ids), 3))
for i in range(len(boxes)):
if i in indices:
x, y, w, h = boxes[i]
label = str(class_ids[i])
confidence = confidences[i]
color = colors[i]
# バウンディングボックスとラベルの描画
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, f'{label} {confidence:.2f}', (x, y - 10), font, 0.5, color, 2)
# 結果の表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('YOLO Object Detection')
plt.axis('off')
plt.show()
詳細 | |
YOLOの基本 | 物体検出モデル(YOLO)の読み込みと実行方法 |
物体検出の流れ | 画像の前処理、物体検出、バウンディングボックスの生成 |
NMS(非最大抑制) | 重複した検出結果を減らす手法 |
画像処理の実践 | OpenCVを用いた物体検出結果の可視化 |
このノートブックを基に、次週は YOLOを使った訓練 や 農業データへの適用 について進めることができます!
こちらが、 農業向けYOLO物体検出モデル に特化したノートブック、
week15_yolo_agri.ipynb の内容です。
# week15_yolo_agri.ipynb:農業向けYOLOを用いた作物検出
import cv2
import numpy as np
import matplotlib.pyplot as plt
# --- YOLOの設定(農業専用のデータセット) ---
yolo_cfg = '/content/drive/MyDrive/yolov3/yolov3_agri.cfg'
yolo_weights = '/content/drive/MyDrive/yolov3/yolov3_agri.weights'
yolo_names = '/content/drive/MyDrive/yolov3/agri_classes.names'
# --- YOLOネットワークの読み込み ---
net = cv2.dnn.readNetFromDarknet(yolo_cfg, yolo_weights)
layer_names = net.getLayerNames()
output_layers = [layer_names[i-1] for i in net.getUnconnectedOutLayers()]
# --- 農業画像の読み込み ---
image_path = '/content/drive/MyDrive/datasets/agri_test_image.jpg' # 農作物の画像
img = cv2.imread(image_path)
height, width, channels = img.shape
# YOLO用に画像の前処理
blob = cv2.dnn.blobFromImage(img, 0.00392, (416, 416), (0, 0, 0), True, crop=False)
net.setInput(blob)
# --- 物体検出の実行 ---
outs = net.forward(output_layers)
# --- 検出結果の解析 ---
class_ids = []
confidences = []
boxes = []
conf_threshold = 0.5 # 信頼度の閾値
nms_threshold = 0.4 # 非最大抑制の閾値
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > conf_threshold:
center_x = int(detection[0] * width)
center_y = int(detection[1] * height)
w = int(detection[2] * width)
h = int(detection[3] * height)
# バウンディングボックスの座標
x = int(center_x - w / 2)
y = int(center_y - h / 2)
boxes.append([x, y, w, h])
confidences.append(float(confidence))
class_ids.append(class_id)
# --- 非最大抑制(NMS)の適用 ---
indices = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
# --- 結果の描画 ---
font = cv2.FONT_HERSHEY_SIMPLEX
colors = np.random.uniform(0, 255, size=(len(class_ids), 3))
# 農業特有のクラス名
with open(yolo_names, 'r') as f:
classes = f.read().strip().split('\n')
for i in range(len(boxes)):
if i in indices:
x, y, w, h = boxes[i]
label = str(classes[class_ids[i]])
confidence = confidences[i]
color = colors[i]
# バウンディングボックスとラベルの描画
cv2.rectangle(img, (x, y), (x + w, y + h), color, 2)
cv2.putText(img, f'{label} {confidence:.2f}', (x, y - 10), font, 0.5, color, 2)
# 結果の表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('YOLO Object Detection - Agriculture')
plt.axis('off')
plt.show()
内容 | 詳細 |
農業特有のクラス | 農作物や病害虫、作物の状態などを識別するクラス |
カスタマイズされたYOLOモデル | 農業向けにトレーニングされたYOLOモデルの使い方 |
農業画像の物体検出 | 農作物、果物、病害の検出とバウンディングボックスの描画 |
次は、さらに高精度な YOLOモデルの再学習 や、 異常検出 のアプローチについても進めることができます。
こちらが、 作物の病害分類 に特化したノートブック、
week16_disease_classify.ipynb の内容です。
# week16_disease_classify.ipynb:病気画像分類(例:葉の病害)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from sklearn.metrics import classification_report, confusion_matrix
# --- データセットの準備 ---
# ディレクトリ構成(例):
# /content/drive/MyDrive/datasets/leaf_disease/
# ├── train/
# │ ├── healthy/
# │ └── diseased/
# └── val/
# ├── healthy/
# └── diseased/
train_dir = '/content/drive/MyDrive/datasets/leaf_disease/train'
val_dir = '/content/drive/MyDrive/datasets/leaf_disease/val'
img_size = (128, 128)
batch_size = 32
train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary'
)
val_generator = val_datagen.flow_from_directory(
val_dir,
target_size=img_size,
batch_size=batch_size,
class_mode='binary'
)
# --- モデル構築(CNN) ---
model = Sequential([
Conv2D(32, (3, 3), activation='relu', input_shape=(128, 128, 3)),
MaxPooling2D(2, 2),
Conv2D(64, (3, 3), activation='relu'),
MaxPooling2D(2, 2),
Flatten(),
Dense(128, activation='relu'),
Dropout(0.3),
Dense(1, activation='sigmoid')
])
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
# --- モデル学習 ---
history = model.fit(
train_generator,
epochs=10,
validation_data=val_generator
)
# --- 学習の可視化 ---
plt.plot(history.history['accuracy'], label='Train Acc')
plt.plot(history.history['val_accuracy'], label='Val Acc')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.title('Disease Classification Accuracy')
plt.legend()
plt.show()
# --- モデル評価 ---
val_generator.reset()
preds = model.predict(val_generator, verbose=1)
y_pred = (preds > 0.5).astype('int32').flatten()
y_true = val_generator.classes
print(classification_report(y_true, y_pred))
print("Confusion Matrix:")
print(confusion_matrix(y_true, y_pred))
学習項目 | 説明 |
CNN構築の基礎 | 畳み込み、プーリング、全結合層などの使い方 |
データ前処理 | ImageDataGeneratorでの画像スケーリング |
学習・評価・可視化 | 精度のグラフ描画、分類レポート、混同行列 |
クラス分類 | 健康な葉 vs 病害を受けた葉の分類 |