印刷物からスキャンした画像の網点除去
古い写真のデジタル化を始めたのですが、卒業アルバムなどオフセット印刷による印刷物は細かい網状の点で色を表現していて、そのままスキャンすると撮像素子やディスプレイの解像度との関係でモアレ縞が発生したりして美しくありません。フォトショップなどの市販ソフトで網点の除去処理ができるようですが、PythonでOpenCVライブラリを使って市販ソフトと同じような処理ができないか試してみました。
import os import cv2 import glob import numpy as np import PIL from scipy.ndimage.filters import median_filter from tqdm import tqdm
def cv2pil(image): new_image = image.copy() if new_image.ndim == 2: pass elif new_image.shape[2] == 3: new_image = cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB) elif new_image.shape[2] == 4: new_image = cv2.cvtColor(new_image, cv2.COLOR_BGRA2RGBA) new_image = PIL.Image.fromarray(new_image) return new_image
def unsharp(image, sigma, strength): # Median filtering image_mf = median_filter(image, sigma) # Calculate the Laplacian lap = cv2.Laplacian(image_mf,cv2.CV_64F) # Calculate the sharpened image sharp = image - strength * lap # Saturate the pixels in either direction sharp[sharp>255] = 255 sharp[sharp<0] = 0 return sharp
files = sorted(glob.glob('tmp/20*.jpg'))
img = cv2.imread(files[1]) height, width, c = img.shape print(width, height)
10800 7200
解像度600dpiでスキャンした卒業アルバムです。サイズが大きすぎるので小さく切り取ってテストします。
x0 = 3400 y0 = 3500 w = 800 h = int(w / 16 * 9) x1 = min(x0 + w, width) y1 = min(y0 + h, height)
testimg = img[y0:y1, x0:x1, :] cv2pil(testimg)
ガウシアンフィルタでぼかして網点を平滑化してみます。
blimg = cv2.GaussianBlur(testimg, (3, 3), 0) cv2pil(blimg)
非局所平均フィルタという処理で、ノイズを除去します。
nlmimg = cv2.fastNlMeansDenoising(blimg, None, 7, 7, 21) cv2pil(nlmimg)
ボケた画像にエッジ強調をかけます。
shimg = np.zeros_like(nlmimg) for i in range(3): shimg[:,:,i] = unsharp(nlmimg[:,:,i], 4, 1.0) cv2pil(shimg)
時計の3時から6時あたりの目盛が消えかかってしまいましたが、まあこんなもんでしょうか。 以下、複数の画像を全部処理します。Core i7 8700のPCで1枚あたり1分以上かかる重たい処理です。
def process_img(img): blimg = cv2.GaussianBlur(img, (3, 3), 0) nlmimg = cv2.fastNlMeansDenoising(blimg, None, 7, 7, 21) shimg = np.zeros_like(nlmimg) for i in range(3): shimg[:,:,i] = unsharp(nlmimg[:,:,i], 4, 1.0) return shimg
folder = 'test' if not os.path.exists(folder): os.mkdir(folder)
for fpath in tqdm(files): img = cv2.imread(fpath) dst = process_img(img) fdst = os.path.join(folder, os.path.basename(fpath)) cv2.imwrite(fdst, dst)
100%|██████████| 33/33 [33:46<00:00, 61.40s/it]
他の処理例