试图模糊图像的最高方差点但代码中存在一些转换问题

Trying to blur highest variance point of an image but some conversion problem exist in code

我正在尝试模糊图像中的最高方差点。我在下面写了代码。第一部分找到图像的方差。我检查了图像的结果方差,它是正确的。 (我使用了 Lena 的图像)在第二部分中,我找到了最高的方差坐标并发送到这个找到高斯模糊的函数。当我执行这段代码时,它会抛出 "C:\Tmp\blur_highest_variance.py", line 66, in sigma=15) numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'subtract' output from dtype('float64') to dtype('uint8') with casting rule 'same_kind' 我尝试了一些类型之间的转换,但无济于事。你能告诉我一些方向吗?

import numpy as np
import matplotlib.pylab as plt    
from skimage import measure
from PIL import Image, ImageChops
import math
import cv2
from skimage.morphology import rectangle
import skimage.filters as filters

######################Calculate Variance#######################
# Variance = mean of square of image - square of mean of image
# See # see https://en.wikipedia.org/wiki/Variance

# read the image
# convert to 16-bits grayscale since mean filter below is limited 
# to single channel 8 or 16-bits, not float
# and variance will be larger than 8-bit range
img = cv2.imread(r".\lena_std512_512.jpg", cv2.IMREAD_GRAYSCALE).astype(np.uint16)

# compute square of image
img_sq = cv2.multiply(img, img)

# compute local mean in 5x5 rectangular region of each image
# note: python will give warning about slower performance when processing 16-bit images
region = rectangle(10,10)
mean_img = filters.rank.mean(img, selem=region)
mean_img_sq = filters.rank.mean(img_sq, selem=region)

# compute square of local mean of img
sq_mean_img = cv2.multiply(mean_img, mean_img)

# compute variance using float versions of images
var = cv2.add(mean_img_sq.astype(np.float32), -sq_mean_img.astype(np.float32))

# compute standard deviation and convert to 8-bit format
std = cv2.sqrt(var).clip(0,255).astype(np.uint8)

# multiply by 2 to make brighter as an example
cv2.imwrite('lena_std_local_variance.jpg',std)    

#################Gaussian Blur Function###############
def gaussian_mask(x, y, shape, amp=1, sigma=15):
    """
    Returns an array of shape, with values based on

    amp * exp(-((i-x)**2 +(j-y)**2) / (2 * sigma ** 2))

    :param x: float
    :param y: float
    :param shape: tuple
    :param amp: float
    :param sigma: float
    :return: array
    """
    xv, yv = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]))
    g = amp * np.exp(-((xv - x) ** 2 + (yv - y) ** 2) / (2 * sigma ** 2))
    return g

#################Find Gaussian Blur and Subtract###############

y, x = np.unravel_index(np.argmax(std), std.shape)       

std -= gaussian_mask(x, y,
                    shape=std.shape[:2],
                    amp=1,
                    sigma=15)

cv2.imwrite(r'.\gaussian\lena_std_local_variance.jpg',std)

错误消息告诉我们错误所在的行和原因:

Traceback (most recent call last):
File "C:\Tmp\blur_highest_variance.py", line 66, in sigma=15)
numpy.core._exceptions.UFuncTypeError: Cannot cast ufunc 'subtract' output from dtype('float64') to dtype('uint8') with casting rule 'same_kind'

使用中间变量调试代码更简单:
例如,使用名为 gmask:

的中间值
gmask = gaussian_mask(x, y,
                      shape=std.shape[:2],
                      amp=1,
                      sigma=15)

print('gmask.dtype = ' + str(gmask.dtype))  # gmask.dtype = float64
print('std.dtype = ' + str(std.dtype))  # std.dtype = uint8

我们真的不需要打印 dtype,我们可以使用调试器,但打印会说明错误的原因。
我们不能使用 -= 运算符从 uint8 中减去 float64 数组!

使用std = std - gmask,不是错误,而是结果的类型是float64


建议的解决方案:

gmask 转换为 uint8 并使用 cv2.subtract:

gmask = gaussian_mask(x, y,
                      shape=std.shape[:2],
                      amp=1,
                      sigma=15)

std = cv2.subtract(std, gmask.clip(0, 255).astype(np.uint8))

使用 cv2.subtract 是减去两个 uint8 矩阵的安全方法,因为它将结果限制为 [0, 255](包括溢出保护)。


结果(lena_std_local_variance.jpg):