在網路上下載了很照片, 想放到電視當背景, 網路上很多都是直立式圖片, 但電視是橫的, 所以想自動把直的套用溶解特效:

確認效果OK 之後, 使用下列的提示詞取得自動轉換用的腳本:
give me a python script to do it , auto detect 這種左右側背景都是黑色的圖片, 生成左右側溶解特效.
* python script 可以透過 args 指定 width, width default value=20 (px).
* default scan current folder *.jpg
* able to assign --cwd to change currect workding dir
增加特效參數的 Python 腳本
這是一支擴充後的程式碼。我們新增了 --effect 參數,讓你可以自由切換不同的邊緣處理特效,包含預設的溶解,以及模糊與透明度漸層,達到讓圖片邊緣不那麼明顯的目的。
功能特色
- 支援三種邊緣特效:溶解、模糊、透明度漸層。
- 可透過參數自由切換特效類型與特效寬度。
- 自動偵測左右黑邊並套用特效。
程式碼
請將 dissolve.py 更新為以下內容:
Python
import argparse
import glob
import os
import cv2
import numpy as np
def add_edge_effect(image_path, width, effect_type, ease=2.0):
img = cv2.imread(image_path)
if img is None:
return
h, w, c = img.shape
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
col_max = np.max(gray, axis=0)
black_threshold = 10
# 額外往黑色區域內縮的 pixel 數,確保 fade 從純黑出發,
# 避免 black_threshold 漏掉的暗色邊緣像素形成亮線。
border_padding = 3
left_border = 0
for x in range(w // 2):
if col_max[x] > black_threshold:
# 往左退 border_padding,但不超出純黑區域(col_max == 0 的最後一個位置)
left_border = max(0, x - border_padding)
break
right_border = w - 1
for x in range(w - 1, w // 2, -1):
if col_max[x] > black_threshold:
right_border = x
break
right_border = min(w, right_border + 1 + border_padding)
if right_border > w:
right_border = w
output = img.copy()
if effect_type == 'dissolve':
np.random.seed(42)
if left_border > 0:
start_x = left_border
end_x = min(w, left_border + width)
for y in range(h):
for x in range(start_x, end_x):
prob = (x - start_x) / width
if np.random.rand() > prob:
output[y, x] = [0, 0, 0]
if right_border < w:
start_x = max(0, right_border - width)
end_x = right_border
for y in range(h):
for x in range(start_x, end_x):
prob = (end_x - x) / width
if np.random.rand() > prob:
output[y, x] = [0, 0, 0]
elif effect_type == 'blur':
# 對整張圖進行模糊,然後利用遮罩將邊緣與原圖融合
blurred = cv2.GaussianBlur(img, (width * 2 + 1, width * 2 + 1), 0)
if left_border > 0:
start_x = left_border
end_x = min(w, left_border + width)
for x in range(start_x, end_x):
alpha = (x - start_x) / width
output[:, x] = (
img[:, x] * alpha + blurred[:, x] * (1 - alpha)
).astype(np.uint8)
if right_border < w:
start_x = max(0, right_border - width)
end_x = right_border
for x in range(start_x, end_x):
alpha = (end_x - x) / width
output[:, x] = (
img[:, x] * alpha + blurred[:, x] * (1 - alpha)
).astype(np.uint8)
elif effect_type == 'fade':
# t 為正規化位置 (0.0 ~ 1.0):
# 左側:t=0 → 外側(黑色),t=1 → 內側(內容)
# 右側:t=0 → 內側(內容),t=1 → 外側(黑色)
#
# alpha 曲線:alpha = 1 - t^ease
# ease=1 → 線性等差(舊行為)
# ease=2 → 二次方,靠近內側(t=0)斜率趨近 0,初期幾乎不變暗
# ease 越大 → 越晚才開始大幅變暗,更接近「突然消失」
#
# 右側示意(ease=2):
# t: 0.0 0.3 0.5 0.7 1.0
# alpha: 1.00 0.91 0.75 0.51 0.00
# vs 線性: 1.00 0.70 0.50 0.30 0.00
if left_border > 0:
start_x = left_border
end_x = min(w, left_border + width) + 1
for x in range(start_x, end_x):
t = (end_x - x) / width # t=1 → 外側(黑),t=0 → 內側
alpha = 1.0 - t ** ease # 內側(t=0)斜率=0,初期減少慢
alpha = max(0.0, min(1.0, alpha))
output[:, x] = np.round(img[:, x] * alpha).astype(np.uint8)
if right_border < w:
start_x = max(0, right_border - width) - 1
end_x = right_border
for x in range(start_x, end_x):
t = (x - start_x) / width # t=0 → 內側(內容),t=1 → 外側(黑)
alpha = 1.0 - t ** ease # 內側(t=0)斜率=0,初期減少慢
alpha = max(0.0, min(1.0, alpha))
output[:, x] = np.round(img[:, x] * alpha).astype(np.uint8)
dir_name, file_name = os.path.split(image_path)
output_dir = os.path.join(dir_name, 'output')
os.makedirs(output_dir, exist_ok=True)
output_path = os.path.join(output_dir, file_name)
cv2.imwrite(output_path, output)
print(f'已處理完成:{output_path}')
def main():
parser = argparse.ArgumentParser(
description='自動偵測左右黑邊並產生邊緣漸層特效。'
)
parser.add_argument(
'--width', type=int, default=20, help='特效寬度,預設為 20 像素。'
)
parser.add_argument(
'--cwd', type=str, default='.', help='工作目錄路徑,預設為目前資料夾。'
)
parser.add_argument(
'--effect',
type=str,
default='dissolve',
choices=['dissolve', 'blur', 'fade'],
help='特效類型:dissolve 溶解、blur 模糊、fade 透明度漸層。',
)
parser.add_argument(
'--ease',
type=float,
default=2.0,
help='fade 曲線指數(僅 fade 有效)。1.0=線性等差,2.0=二次方(預設),值越大內側保持亮度越久。',
)
args = parser.parse_args()
target_dir = args.cwd
if not os.path.isdir(target_dir):
print(f'錯誤:找不到目錄 {target_dir}')
return
# 同時支援 jpg 與 png,優先找 png(make_wallpaper.py 的無損暫存格式)
files = glob.glob(os.path.join(target_dir, '*.png'))
if not files:
files = glob.glob(os.path.join(target_dir, '*.jpg'))
if not files:
print('在指定目錄中找不到任何 jpg 或 png 圖片。')
return
for file_path in sorted(files):
add_edge_effect(file_path, args.width, args.effect, args.ease)
if __name__ == '__main__':
main()
使用方式
你可以透過 --effect 參數指定想要的特效類型:
預設溶解特效:
Bash
python dissolve.py
使用模糊特效:
Bash
python dissolve.py --effect blur --width 15
使用漸層淡出特效:
Bash
python dissolve.py --effect fade --width 20
錯誤處理
Error message:
File c:\cleanyourtv\themes\leejueun\wallpapers\dissolve.py", line 3, in <module>
import cv2
ModuleNotFoundError: No module named 'cv2'
處理模組錯誤
這代表你的電腦沒有安裝 OpenCV 套件。請注意,在 pip 裡面安裝 OpenCV 的套件名稱不是 cv2,而是 opencv-python。
解決步驟
請在終端機中輸入以下指令來安裝正確的套件:
Bash
python -m pip install opencv-python
安裝完成之後,再次執行你的 Python 腳本即可正常運作。
實際在電視上的執行結果

解釋 fade 原理與實際效果, 並說明像素內容的變化:
# t 為正規化位置 (0.0 ~ 1.0):
# 左側:t=0 → 外側(黑色),t=1 → 內側(內容)
# 右側:t=0 → 內側(內容),t=1 → 外側(黑色)
#
# alpha 曲線:alpha = 1 - t^ease
# ease=1 → 線性等差(舊行為)
# ease=2 → 二次方,靠近內側(t=0)斜率趨近 0,初期幾乎不變暗
# ease 越大 → 越晚才開始大幅變暗,更接近「突然消失」
#
# 右側示意(ease=2):
# t: 0.0 0.3 0.5 0.7 1.0
# alpha: 1.00 0.91 0.75 0.51 0.00
# vs 線性: 1.00 0.70 0.50 0.30 0.00
for x in range(start_x, end_x):
t = (x - start_x) / width # t=0 → 內側(內容),t=1 → 外側(黑)
alpha = 1.0 - t ** ease # 內側(t=0)斜率=0,初期減少慢
alpha = max(0.0, min(1.0, alpha))
output[:, x] = np.round(img[:, x] * alpha).astype(np.uint8)
這是右側 fade的程式碼。逐行拆解:
變數意義
假設 width = 60,右側內容邊界 right_border = 1365:
start_x = max(0, right_border - width) = 1305
end_x = right_border = 1365
alpha 的變化
alpha = (end_x - x) / width
x │ end_x - x │ alpha │ 意義
---
1305(start_x) │ 1365 - 1305 = 60 │ 60/60 = 1.0 │ 原始亮度
1325(中間) │ 1365 - 1325 = 40 │ 40/60 ≈ 0.67 │ 67% 亮度
1344 │ 1365 - 1344 = 21 │ 21/60 = 0.35 │ 35% 亮度
1364(end_x-1) │ 1365 - 1364 = 1 │ 1/60 ≈ 0.017 │ 幾乎全黑
📌 alpha 從 1 → 0,越往右越暗。
像素乘法
output[:, x] = (img[:, x] * alpha).astype(np.uint8)
以某個像素 img[y, x] = [180, 120, 90](B, G, R)為例:
x │ alpha │ B │ G │ R
---
1305 │ 1.00 │ 180×1.00 = 180 │ 120×1.00 = 120 │ 90×1.00 = 90
1325 │ 0.67 │ 180×0.67 = 120 │ 120×0.67 = 80 │ 90×0.67 = 60
1344 │ 0.35 │ 180×0.35 = 63 │ 120×0.35 = 42 │ 90×0.35 = 31
1364 │ 0.017 │ 180×0.017 = 3 │ 120×0.017 = 2 │ 90×0.017 = 1
│ x ≥ 1365(end_x 以外):完全不觸碰,保持原值
整體視覺效果示意
x: 1200 1305 1365 1500
──────┬─────────────────┬──────
content │ fade zone │ black
──────┴─────────────────┴──────
alpha: 1.0 ──────────► 0.0
brightness: ████▓▓▓▓▒▒▒▒░░░░▒▒░░░░
↑
幾乎純黑,接黑色背景
注意事項
astype(np.uint8) 會截斷小數,不是四捨五入:
180 * (1/60) = 3.0 → 3 ✅
120 * (1/60) = 2.0 → 2 ✅
90 * (21/60) = 31.5 → 31 ⚠️ 損失 0.5,累積後在低亮度區可能產生色差
直接使用等差, 會造成視覺上產生出一條較亮的白線

改用 曲線:alpha = 1 – t^ease , 效果就好很多.
make_wallpaper.py 參考:
https://stackoverflow.max-everyday.com/2026/11/make_wallpaper/