0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

python Sobelフィルタ + アンシャープマスク(先鋭化)

0
Posted at

🔳用途

・画像の、エッジ部分を強調して出す。
・線形のフィルタリングなので、Sobelフィルタ単体よりも、よりエッジの値が上がる。

🔳結果

・フィルタなし画像
スクリーンショット 2025-12-13 13.06.09(2).png

・フィルタあり画像
スクリーンショット 2025-12-13 13.06.09(2)のコピー.png

🔳ソースコード


# ================================
# 1セル完結テンプレ(Colab)
# Driveマウント -> 画像選択 -> 表示 -> Sobel -> Unsharp -> Sobel(先鋭後)
# OpenCVなし / NumPy + PIL + matplotlib + ipywidgets
# ================================

#--- install
!pip -q install ipywidgets

import os, glob 
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import ipywidgets as widgets
from IPython.display import display, clear_output

from google.colab import drive
drive.mount('/content/drive')

# ========= ここを必要なら変更 =========
# Drive内のどこから探すか(重いならフォルダを絞る)
# ROOT = "/content/drive/MyDrive"
ROOT = "/content/drive/MyDrive/画像フィルタ_25-26_001"
# =====================================

# ---------- 画像処理 基本関数 ----------
def to_gray_np(img_np):
  """img_np: (H,W,3) or (H,W) 0..255 -> float32 gray"""
  if img_np.ndim == 2:
    return img_np.astype(np.float32)
  r, g, b = img_np[..., 0], img_np[..., 1], img_np[..., 2]
  return (0.299*r + 0.587*g + 0.114*b).astype(np.float32)

def conv2d_gray(gray, kernel):
  gray = gray.astype(np.float32)
  k = np.array(kernel, dtype=np.float32)
  kh, kw = k.shape
  pad_h, pad_w = kh//2, kw//2
  padded = np.pad(gray, ((pad_h, pad_h), (pad_w, pad_w)), mode="edge")
  out = np.zeros_like(gray, dtype=np.float32)

  for y in range(out.shape[0]):
    for x in range(out.shape[1]):
      region = padded[y:y+kh, x:x+kw]
      out[y, x] = np.sum(region * k)
  return out

def sobel(gray):
  """gray: (H,W) float32"""
  Kx = np.array([[-1, 0, 1],
                 [-2, 0, 2],
                 [-1, 0, 1]], dtype=np.float32)
  Ky = np.array([[1, 2, 1],
                 [0, 0, 0],
                 [-1, -2, -1]], dtype=np.float32)
  gx = conv2d_gray(gray, Kx)
  gy = conv2d_gray(gray, Ky)
  mag = np.sqrt(gx*gx + gy*gy)
  mag = mag / (mag.max() + 1e-8) * 255.0
  return gx, gy,mag.astype(np.uint8)

def gaussian_kernel_1d(radius, sigma):
  x = np.arange(-radius, radius+1, dtype=np.float32)
  k = np.exp(-(x*x)/(2*sigma*sigma))
  k /= (k.sum() + 1e-8)
  return k

def conv1d_h(gray, k):
  r = len(k) // 2
  padded = np.pad(gray, ((0,0),(r,r)), mode="edge")
  out = np.zeros_like(gray, dtype=np.float32)
  for y in range(gray.shape[0]):
    for x in range(gray.shape[1]):
      out[y,x] = np.sum(padded[y, x:x+2*r+1] * k)
  return out

def conv1d_v(gray, k):
    r = len(k)//2
    padded = np.pad(gray, ((r,r),(0,0)), mode="edge")
    out = np.zeros_like(gray, dtype=np.float32)
    for y in range(gray.shape[0]):
        for x in range(gray.shape[1]):
            out[y,x] = np.sum(padded[y:y+2*r+1, x] * k)
    return out

def gaussian_blur(gray, radius=2, sigma=1.2):
    k = gaussian_kernel_1d(radius, sigma)
    tmp = conv1d_h(gray.astype(np.float32), k)
    out = conv1d_v(tmp, k)
    return out

def unsharp_mask(gray, radius=2, sigma=1.2, amount=1.2, threshold=2.0):
    g = gray.astype(np.float32)
    blur = gaussian_blur(g, radius=radius, sigma=sigma)
    detail = g - blur
    if threshold > 0:
        mask = np.abs(detail) >= threshold
        detail = detail * mask
    sharp = g + amount * detail
    sharp = np.clip(sharp, 0, 255)
    return sharp.astype(np.uint8), blur.astype(np.uint8)

def show_results(img_rgb, gray, edge, blur, sharp, edge_sharp, title_path=""):
    h, w = img_rgb.shape[:2]
    disp_w = min(900, w)
    disp_h = int(disp_w * h / w)
    img_disp = Image.fromarray(img_rgb).resize((disp_w, disp_h))

    plt.figure(figsize=(18,5))
    plt.subplot(1,5,1); plt.title("Original"); plt.imshow(img_disp); plt.axis("off")
    plt.subplot(1,5,2); plt.title("Gray"); plt.imshow(gray, cmap="gray"); plt.axis("off")
    plt.subplot(1,5,3); plt.title("Sobel"); plt.imshow(edge, cmap="gray"); plt.axis("off")
    plt.subplot(1,5,4); plt.title("Unsharp"); plt.imshow(sharp, cmap="gray"); plt.axis("off")
    plt.subplot(1,5,5); plt.title("Sobel on Unsharp"); plt.imshow(edge_sharp, cmap="gray"); plt.axis("off")
    plt.suptitle(title_path, y=1.02, fontsize=11)
    plt.show()

# ---------- Drive内の画像を収集 ----------
exts = ("*.png","*.jpg","*.jpeg","*.bmp","*.webp")
paths = []
for e in exts:
  paths += glob.glob(os.path.join(ROOT, "**", e), recursive=True)
paths = sorted(paths)

if len(paths) == 0:
  raise FileNotFoundError(f"画像が見つからないよ。ROOT={ROOT} を画像フォルダに変更してね。")

MAX_LIST = 2000
paths_view = paths[:MAX_LIST] 

# ---------- UI ----------
dd = widgets.Dropdown(
    options=paths_view,
    description="Image:",
    layout=widgets.Layout(width="95%")
)

radius = widgets.IntSlider(value=2, min=1, max=8, step=1, description="radius")
sigma  = widgets.FloatSlider(value=1.2, min=0.5, max=5.0, step=0.1, description="sigma")
amount = widgets.FloatSlider(value=1.2, min=0.0, max=3.0, step=0.1, description="amount")
thresh = widgets.FloatSlider(value=2.0, min=0.0, max=20.0, step=0.5, description="threshold")

btn_run = widgets.Button(description="Run", button_style="success")
out = widgets.Output()

def run_process(_):
    with out:
        clear_output(wait=True)
        path = dd.value
        img = np.array(Image.open(path).convert("RGB"))
        gray = to_gray_np(img)

        _, _, edge = sobel(gray)
        sharp, blur = unsharp_mask(gray, radius=radius.value, sigma=sigma.value, amount=amount.value, threshold=thresh.value)
        _, _, edge_sharp = sobel(sharp.astype(np.float32))

        show_results(img, gray, edge, blur, sharp, edge_sharp, title_path=path)
        print(f"Loaded: {path}")
        print(f"Params: radius={radius.value}, sigma={sigma.value}, amount={amount.value}, threshold={thresh.value}")
        if len(paths) > MAX_LIST:
            print(f"※画像が多いので表示候補は先頭 {MAX_LIST} 件だけ。ROOTを絞ると探しやすいよ。")

btn_run.on_click(run_process)

display(dd, widgets.HBox([btn_run]), widgets.VBox([radius, sigma, amount, thresh]), out)

# 初回自動実行
run_process(None)


  

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?