タイル状の画像から最小単位を切り取る

2024年12月2日

プログラム

import cv2
import numpy as np

# 元画像とテンプレート画像のパス
image_path = "checkerboard.png"

# 元画像を読み込む
img = cv2.imread(image_path)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# テンプレート画像として使用する部分を指定して切り取る(例: 画像の左上を30%の範囲で切り取る)
height, width = gray_img.shape
cut_width = int(width * 0.30)
cut_height = int(height * 0.30)
template = gray_img[0:cut_height, 0:cut_width]

# テンプレートマッチングを実行
result = cv2.matchTemplate(gray_img, template, cv2.TM_CCOEFF_NORMED)

# マッチングのしきい値を0.99に設定
threshold = 0.99
loc = np.where(result >= threshold)

# 初期化
min_y_when_x_zero = float('inf')
min_x_when_y_zero = float('inf')

# 見つかった場所をチェック
for pt in zip(*loc[::-1]):
    if pt[0] == 0 and pt != (0, 0):
        min_y_when_x_zero = min(min_y_when_x_zero, pt[1])
    if pt[1] == 0 and pt != (0, 0):
        min_x_when_y_zero = min(min_x_when_y_zero, pt[0])

# 結果をプリント
print(f"Xがゼロのときの最小Yの座標 (excluding (0, 0)): {min_y_when_x_zero}")
print(f"Yがゼロのときの最小Xの座標 (excluding (0, 0)): {min_x_when_y_zero}")

# 指定された範囲を元画像から切り取る
if min_x_when_y_zero != float('inf') and min_y_when_x_zero != float('inf'):
    cropped_img = img[0:min_y_when_x_zero - 1, 0:min_x_when_y_zero - 1]

    # 切り取った画像を保存
    output_cropped_path = "output_cropped.png"
    cv2.imwrite(output_cropped_path, cropped_img)
    print(f"切り取った画像を保存しました: {output_cropped_path}")

# 見つかった場所を赤い矩形で囲む
for pt in zip(*loc[::-1]):
    if pt != (0, 0):
        cv2.rectangle(img, pt, (pt[0] + cut_width, pt[1] + cut_height), (0, 0, 255), 2)

# 結果を保存
output_path = "output_detected.png"
cv2.imwrite(output_path, img)

print(f"結果を保存しました: {output_path}")

複数ファイルと透明なファイル対応

import cv2
import numpy as np
import os
from glob import glob
from cvplus import cvt

def process_image(img, cut_percentage=0.30, matching_threshold=0.95,  inf=True):
    copied_image = img.copy()

    if len(img.shape) == 3 and img.shape[2] == 4:    
# アルファチャネルがある場合
    
    # アルファチャンネルを取得 
      alpha_channel = img[:, :, 3]
      # アルファチャンネルがすべて255か確認 
      if np.all(alpha_channel == 255):
          # アルファチャンネルを取り除く
        bgr_img = img[:, :, :3]
        _, white_img = cv2.threshold(bgr_img, 128, 255, cv2.THRESH_BINARY)

      else:
        b, g, r, a = cv2.split(img)
        white_img = cv2.merge((b, g, r, a))
        white_img[a <= 10] = [255, 255, 255, 255]  # 背景が透明な部分を白に変更
        white_img[a > 10] = [0, 0, 0, 255]  # 透明でない部分を黒に変更
    else:  # アルファチャネルがない場合
        _, white_img = cv2.threshold(img, 128, 255, cv2.THRESH_BINARY)

    gray_img = cv2.cvtColor(cv2.cvtColor(white_img, cv2.COLOR_BGRA2BGR), cv2.COLOR_BGR2GRAY)
    if np.all(gray_img == 0)  or np.all(gray_img == 255):
        return None, gray_img, copied_image

    height, width = gray_img.shape
    cut_width = int(width * cut_percentage)
    cut_height = int(height * cut_percentage)
    template = gray_img[0:cut_height, 0:cut_width]

    result = cv2.matchTemplate(gray_img, template, cv2.TM_CCOEFF_NORMED)
    loc = np.where(result >= matching_threshold)

    for pt in zip(*loc[::-1]):
        if pt != (0, 0):
            cv2.rectangle(copied_image, pt, (pt[0] + cut_width, pt[1] + cut_height), (0, 0, 255), 2)
    min_y_when_x_zero = float('inf')
    min_x_when_y_zero = float('inf')

    for pt in zip(*loc[::-1]):
        if pt[0] == 0 and pt != (0, 0) and pt[1] != 1:
            min_y_when_x_zero = min(min_y_when_x_zero, pt[1])
        if pt[1] == 0 and pt != (0, 0) and pt[0] != 1:
            min_x_when_y_zero = min(min_x_when_y_zero, pt[0])

    print(f"Xがゼロのときの最小Yの座標: {min_y_when_x_zero}")
    print(f"Yがゼロのときの最小Xの座標: {min_x_when_y_zero}")
    if min_y_when_x_zero == 2:
        min_y_when_x_zero = min_x_when_y_zero 
    if  inf==False:
        if min_x_when_y_zero ==  float('inf'):
           min_x_when_y_zero = width
        if min_y_when_x_zero ==  float('inf'):
           min_y_when_x_zero = height

    if min_x_when_y_zero != float('inf') and min_y_when_x_zero != float('inf'):
        cropped_img = white_img[0:min_y_when_x_zero - 1, 0:min_x_when_y_zero - 1]
    else:
        cropped_img =  None
        
    return cropped_img, gray_img, copied_image

def process_images(input_folder, output_folder):
    os.makedirs(output_folder, exist_ok=True)
    image_files = glob(os.path.join(input_folder, "*.png")) + glob(os.path.join(input_folder, "*.jpg")) + glob(os.path.join(input_folder, "*.jpeg"))
    for image_path in image_files:
        img = cvt.imread(image_path, cv2.IMREAD_UNCHANGED)
        thresholds = [0.99, 0.97, 0.95]
        cut_percentages = [1/3, 1/4, 1/5, 1/10, 1/20]

        for cut_percentage in cut_percentages:
          for threshold in thresholds:
            cropped_img, gray_img, copied_image = process_image(img, cut_percentage=cut_percentage, matching_threshold=threshold, inf=True)
            if cropped_img is not None:
                break 
          if cropped_img is not None:
              break 

        base_name = os.path.basename(image_path)
        name, ext = os.path.splitext(base_name)

        output_detected_path = os.path.join(output_folder, f"{name}_detected{ext}")
        cvt.imwrite(output_detected_path, copied_image)
        print(f"結果を保存しました: {output_detected_path}")


        output_white_bg_path = os.path.join(output_folder, f"{name}_white_bg{ext}")
        cvt.imwrite(output_white_bg_path, gray_img)
        print(f"背景が白になった画像を保存しました: {output_white_bg_path}")
        
        if cropped_img is not None:
            output_cropped_path = os.path.join(output_folder, f"{name}_cropped{ext}")
            cvt.imwrite(output_cropped_path, cropped_img)
            print(f"切り取った画像を保存しました: {output_cropped_path}")

if __name__ == "__main__":
    input_folder = r"d:\wagara"
    output_folder = r"Y:\lin"
    process_images(input_folder, output_folder)

Python,画像

Posted by eightban