LINE notifyのモジュール化
別のメソッドでも使えるようにモジュール化する
また
message = 'ファイルパス自動取得テスト'
の部分は
他のプログラムで
生成された文字列を受け取って実行するようにコードを変更する
vim line_notify.py
で
import requests import os from PIL import Image from io import BytesIO from utils import load_config, get_latest_directory, get_image_files def resize_image_if_needed(image_data, max_size=3 * 1024 * 1024): if len(image_data) > max_size: image = Image.open(BytesIO(image_data)) new_size = (image.width // 2, image.height // 2) image = image.resize(new_size, Image.LANCZOS) output = BytesIO() image_format = image.format if image.format else 'JPEG' image.save(output, format=image_format) return output.getvalue() return image_data def send_line_notify(message, config_path='config.json'): # 設定ファイルを読み込む config = load_config(config_path) # 設定ファイルからトークンとディレクトリパスを取得 token = config['token'] base_path = config['image_file_path'] # 最新のpredictディレクトリを取得 latest_dir = get_latest_directory(base_path) image_files = get_image_files(latest_dir) url = 'https://notify-api.line.me/api/notify' headers = {'Authorization': f"Bearer {token}"} params = {'message': message} # 最新のpredictディレクトリ内の全ての画像ファイルに対してLINE Notify APIにリクエストを送信 for image_file_path in image_files: with open(image_file_path, 'rb') as img_file: img_data = img_file.read() img_data = resize_image_if_needed(img_data) # ファイルデータをバイトデータとして用意 files = {'imageFile': BytesIO(img_data)} files['imageFile'].name = os.path.basename(image_file_path) # LINE Notify APIにリクエストを送信 res = requests.post(url, headers=headers, params=params, files=files) # レスポンスを出力 print(f"File: {image_file_path}") print(res.status_code) print(res.text)
とりあえずこれを使えるかテストする
import argparse import json import cv2 from ultralytics import YOLO from collections import defaultdict # コマンドライン引数の解析 parser = argparse.ArgumentParser(description="YOLOv8 Object Detection") parser.add_argument('image_path', type=str, help='Path to the input image file') args = parser.parse_args() # ラベルマッピングファイルのパス label_mapping_path = 'label_mapping.json' # JSONファイルからクラスラベルのマッピングを読み込み with open(label_mapping_path, 'r', encoding='utf-8') as f: label_mapping = json.load(f) # YOLOv8モデルのロード model = YOLO('inventory_model/best.pt') # ここで適切なモデルを選択 # 画像のロード image = cv2.imread(args.image_path) # 画像の検出 results = model(image, save=True, conf=0.2, iou=0.5) # 検出結果の取得 detections = results[0] # 最初の結果を取得 classes = detections.boxes.cls # 検出物体のカウント object_counts = defaultdict(int) for cls in classes: class_label = model.names[int(cls)] if class_label in label_mapping: label = label_mapping[class_label] else: label = class_label object_counts[label] += 1 # 検出結果の表示 for label, count in object_counts.items(): print(f'{label}: {count}個')
の中で呼び出すようにする
import argparse import json import cv2 from ultralytics import YOLO from collections import defaultdict from line_notify import send_line_notify # インポートを追加 # コマンドライン引数の解析 parser = argparse.ArgumentParser(description="YOLOv8 Object Detection") parser.add_argument('image_path', type=str, help='Path to the input image file') args = parser.parse_args() # ラベルマッピングファイルのパス label_mapping_path = 'label_mapping.json' # JSONファイルからクラスラベルのマッピングを読み込み with open(label_mapping_path, 'r', encoding='utf-8') as f: label_mapping = json.load(f) # YOLOv8モデルのロード model = YOLO('inventory_model/best.pt') # ここで適切なモデルを選択 # 画像のロード image = cv2.imread(args.image_path) # 画像の検出 results = model(image, save=True, conf=0.2, iou=0.5) # 検出結果の取得 detections = results[0] # 最初の結果を取得 classes = detections.boxes.cls # 検出物体のカウント object_counts = defaultdict(int) for cls in classes: class_label = model.names[int(cls)] if class_label in label_mapping: label = label_mapping[class_label] else: label = class_label object_counts[label] += 1 # 検出結果のメッセージ生成 message_lines = [f'{label}: {count}個' for label, count in object_counts.items()] message = '\n'.join(message_lines) # 検出結果の表示 for line in message_lines: print(line) # LINE Notifyにメッセージを送信 send_line_notify(message)
これを
python count_inventory_terminal.py data_bak/Baskulin4.jpg
で実行すると
0: 640x512 1 baskulin, 125.4ms Speed: 7.7ms preprocess, 125.4ms inference, 7.6ms postprocess per image at shape (1, 3, 640, 512) Results saved to runs/detect/predict4 バスクリン: 1個 File: runs/detect/predict4/image0.jpg 200 {"status":200,"message":"ok"}
となり画像つきメッセージが送信される
次は在庫の数が1以下のものをリストにして送信するようにする
import argparse import json import cv2 from ultralytics import YOLO from collections import defaultdict from line_notify import send_line_notify # インポートを追加 # コマンドライン引数の解析 parser = argparse.ArgumentParser(description="YOLOv8 Object Detection") parser.add_argument('image_path', type=str, help='Path to the input image file') args = parser.parse_args() # ラベルマッピングファイルのパス label_mapping_path = 'label_mapping.json' # JSONファイルからクラスラベルのマッピングを読み込み with open(label_mapping_path, 'r', encoding='utf-8') as f: label_mapping = json.load(f) # YOLOv8モデルのロード model = YOLO('inventory_model/best.pt') # ここで適切なモデルを選択 # 画像のロード image = cv2.imread(args.image_path) # 画像の検出 results = model(image, save=True, conf=0.2, iou=0.5) # 検出結果の取得 detections = results[0] # 最初の結果を取得 classes = detections.boxes.cls # 検出物体のカウント object_counts = defaultdict(int) for cls in classes: class_label = model.names[int(cls)] if class_label in label_mapping: label = label_mapping[class_label] else: label = class_label object_counts[label] += 1 # 検出結果のフィルタリング(1以下のもの) filtered_object_counts = {label: count for label, count in object_counts.items() if count <= 1} # フィルタリングされた検出結果のメッセージ生成 message_lines = [f'{label}: {count}個' for label, count in filtered_object_counts.items()] message = '\n'.join(message_lines) # 検出結果の表示 for line in message_lines: print(line) # LINE Notifyにメッセージを送信(フィルタリングされた結果のみ) if message: send_line_notify(message) else: print("No objects with counts of 1 or less detected.")
これで今度は
python count_inventory_terminal.py data_bak/potato_starch1.jpg
として検出されない時には
0: 640x512 (no detections), 123.0ms Speed: 5.7ms preprocess, 123.0ms inference, 5.4ms postprocess per image at shape (1, 3, 640, 512) Results saved to runs/detect/predict6 No objects with counts of 1 or less detected.
となって
LINE送信はされなくなる
今回の画像はモデルの学習不足のためか
片栗粉の検出ができなかったので
それを認識できない場合のテストに使った
しかし、画像読み取りエラーなどを考慮し
今後何らかのアクションを取るようにした方が良いかもしれない
エラーログ以外のものを考えるようにする
また、送信するタイミングは、在庫数が1以下になった時に送るようにしました。
この場合、画像が検出できなかったりした時に判定ができないため
今後の課題とします
解決方法としては、検出結果をDBへ格納しておき
実行したタイムスタンプも記録、検出結果が0の時にはアラートを飛ばすなどがありそうです
とりあえず、ターミナル実行のみの状態なので
今後はどこから画像を撮ってくるのか、またwebカメラで行うのか、それとも
ラズパイゼロなどで撮影した画像を使うのか、それを考えてからまた改良していこうと思います