opencv 如何在Python中将PNG图像中的选中图案替换为透明图案?

8nuwlpux  于 2022-11-15  发布在  Python
关注(0)|答案(3)|浏览(239)

我试图用Python脚本删除一些PNG中带有透明颜色(alpha通道)的方格背景(在Adobe Illustrator和Photoshop中表示透明背景)。
首先,我使用 * 模板匹配 *:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img_rgb = cv2.imread('testimages/fake1.png', cv2.IMREAD_UNCHANGED)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('pattern.png', 0)

w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)

for pt in zip(*loc[::-1]):
    if len(img_rgb[0][0]) == 3:
        # add alpha channel
        rgba = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2RGBA)
        rgba[:, :, 3] = 255 # default not transparent
        img_rgb = rgba
    # replace the area with a transparent rectangle
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (255, 255, 255, 0), -1) 

cv2.imwrite('result.png', img_rgb)

源图像:fake1.png文件

模式模板:模式.png

输出:result.png(灰色区域实际上是透明的;放大一点以便查看)

我知道这种方法有问题,因为在某些情况下,模板无法完全识别,因为部分图案被PNG图像中的图形隐藏。

**我的问题是:**如何通过FFT滤波使用OpenCV?完美匹配这样的模式?

参考文献:

bogh5gae

bogh5gae1#

这里有一种在Python/OpenCV中实现这一点的方法,只需对校验颜色范围设置阈值。
输入:

import cv2
import numpy as np

# read input
img = cv2.imread("fake.png")

# threshold on checks
low = (230,230,230)
high = (255,255,255)
mask = cv2.inRange(img, low, high)

# invert alpha
alpha = 255 - mask

# convert img to BGRA
result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
result[:,:,3] = alpha

# save output
cv2.imwrite('fake_transparent.png', result)

cv2.imshow('img', img)
cv2.imshow('mask', mask)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

下载生成的图像,看看它实际上是透明的。

fdbelqdn

fdbelqdn2#

这里 有 一 种 在 Python/OpenCV/Numpy 中 使用 DFT 来 处理 图像 的 方法 。 我们 需要 知道 棋盘 图案 的 大小 ( 亮 或 暗 的 正方 形 大小 ) 。

  • 读取 输入
  • 独立 通道
  • 对 每个 通道 应用 DFT
  • 将 原点 从 每个 通道 的 左上 角 移 到 中心
  • 从 每个 通道 提取 幅度 和 相位 图像
  • 定义 棋盘 图案 大小
  • 创建 大小 相同 的 黑白 棋盘 图像
  • 对 棋盘 格 图像 应用 类似 的 DFT 处理
  • 从 对数 中 获得 频谱 ( 幅度 )
  • 设置 光谱 阈值 以 形成 遮罩
  • 将 模板 中 的 DC 中心 点 归 零
  • 选项 : 如果 需要 , 应用 形态 学 扩张 来 加厚 白点 。 但 在 这里 似乎 不 需要
  • 反转 蒙版 , 使 背景 为 白色 , 点 为 黑色
  • 将 遮罩 转换 为 0 到 1 的 范围 并 生成 2 个 通道
  • 将 双 通道 遮罩 应用 于 中心 偏移 DFT 通道
  • 将 每个 遮罩 图像 的 中心 移 回 左上 方
  • 执行 IDFT 以 在 每个 通道 上 从 复数 域 返回 到 实数 域
  • 将 生成 的 通道 合并 回 BGR 图像 , 作为 最终 重建 图像
  • 储存 结果

输入 :

import numpy as np
import cv2
import math

# read input 
# note: opencv fft only works on grayscale
img = cv2.imread('fake.png')
hh, ww = img.shape[:2]

# separate channels
b,g,r = cv2.split(img)

# convert images to floats and do dft saving as complex output
dft_b = cv2.dft(np.float32(b), flags = cv2.DFT_COMPLEX_OUTPUT)
dft_g = cv2.dft(np.float32(g), flags = cv2.DFT_COMPLEX_OUTPUT)
dft_r = cv2.dft(np.float32(r), flags = cv2.DFT_COMPLEX_OUTPUT)

# apply shift of origin from upper left corner to center of image
dft_b_shift = np.fft.fftshift(dft_b)
dft_g_shift = np.fft.fftshift(dft_g)
dft_r_shift = np.fft.fftshift(dft_r)

# extract magnitude and phase images
mag_b, phase_b = cv2.cartToPolar(dft_b_shift[:,:,0], dft_b_shift[:,:,1])
mag_g, phase_g = cv2.cartToPolar(dft_g_shift[:,:,0], dft_g_shift[:,:,1])
mag_r, phase_r = cv2.cartToPolar(dft_r_shift[:,:,0], dft_r_shift[:,:,1])

# set check size (size of either dark or light square)
check_size = 15

# create checkerboard pattern
white = np.full((check_size,check_size), 255, dtype=np.uint8)
black = np.full((check_size,check_size), 0, dtype=np.uint8)
checks1 = np.hstack([white,black])
checks2 = np.hstack([black,white])
checks3 = np.vstack([checks1,checks2])
numht = math.ceil(hh / (2*check_size))
numwd = math.ceil(ww / (2*check_size))
checks = np.tile(checks3, (numht,numwd))
checks = checks[0:hh, 0:ww]

# apply dft to checkerboard pattern
dft_c = cv2.dft(np.float32(checks), flags = cv2.DFT_COMPLEX_OUTPUT)
dft_c_shift = np.fft.fftshift(dft_c)
mag_c, phase_c = cv2.cartToPolar(dft_c_shift[:,:,0], dft_c_shift[:,:,1])

# get spectrum from magnitude (add tiny amount to avoid divide by zero error)
spec = np.log(mag_c + 0.00000001)

# theshold spectrum
mask = cv2.threshold(spec, 1, 255, cv2.THRESH_BINARY)[1]

# mask DC point (center spot)
centx = int(ww/2)
centy = int(hh/2)
dot = np.zeros((3,3), dtype=np.uint8)
mask[centy-1:centy+2, centx-1:centx+2] = dot

# If needed do morphology dilate by small amount. 
# But does not seem to be needed in this case

# invert mask
mask = 255 - mask

# apply mask to real and imaginary components
mask1 = (mask/255).astype(np.float32)
mask2 = cv2.merge([mask1,mask1])
complex_b = dft_b_shift*mask2
complex_g = dft_g_shift*mask2
complex_r = dft_r_shift*mask2

# shift origin from center to upper left corner
complex_ishift_b = np.fft.ifftshift(complex_b)
complex_ishift_g = np.fft.ifftshift(complex_g)
complex_ishift_r = np.fft.ifftshift(complex_r)

# do idft with normalization saving as real output and crop to original size
img_notch_b = cv2.idft(complex_ishift_b, flags=cv2.DFT_SCALE+cv2.DFT_REAL_OUTPUT)
img_notch_b = img_notch_b.clip(0,255).astype(np.uint8)
img_notch_b = img_notch_b[0:hh, 0:ww]
img_notch_g = cv2.idft(complex_ishift_g, flags=cv2.DFT_SCALE+cv2.DFT_REAL_OUTPUT)
img_notch_g = img_notch_g.clip(0,255).astype(np.uint8)
img_notch_g = img_notch_g[0:hh, 0:ww]
img_notch_r = cv2.idft(complex_ishift_r, flags=cv2.DFT_SCALE+cv2.DFT_REAL_OUTPUT)
img_notch_r = img_notch_r.clip(0,255).astype(np.uint8)
img_notch_r = img_notch_r[0:hh, 0:ww]

# combine b,g,r components
img_notch = cv2.merge([img_notch_b, img_notch_g, img_notch_r])

# write result to disk
cv2.imwrite("fake_checks.png", checks)
cv2.imwrite("fake_spectrum.png", (255*spec).clip(0,255).astype(np.uint8))
cv2.imwrite("fake_mask.png", mask)
cv2.imwrite("fake_notched.png", img_notch)

# show results
cv2.imshow("ORIGINAL", img)
cv2.imshow("CHECKS", checks)
cv2.imshow("SPECTRUM", spec)
cv2.imshow("MASK", mask)
cv2.imshow("NOTCH", img_notch)
cv2.waitKey(0)
cv2.destroyAllWindows()

中 的 每 一 个
棋盘 图像 :

光谱 棋盘 格 :

遮罩 :


语言
结果 ( 陷 波 滤波 图像 ) :


指令 集
结果 中 的 棋盘 格 图案 从 原始 图案 中 减轻 , 但 在 仔细 检查 后 仍 存在 。
从 这里 开始 , 我们 需要 在 白色 背景 上 设置 阈值 , 并 反转 以 生成 alpha 通道 的 图像 。 然后 将 图像 转换 为 4 BGRA , 并 将 alpha 通道 插入 到 BGRA 图像 中 , 正如 我 在 下面 的 另 一 个 答案 中 所 描述 的 。

yb3bgrhw

yb3bgrhw3#

既然你正在处理的是透明背景的PNG,那么你可以尝试提取没有方格的部分,而不是试图检测方格背景。这可以通过对所有像素进行颜色检查来实现。你可以使用opencv的inRange()函数。我将在下面链接一个StackOverflow链接,它试图检测图像上的暗点。Inrange example

相关问题