在OpenCV中对图像进行归一化会产生黑色图像?

48

我使用OpenCV编写了以下代码,使用NORM_L1对图像进行归一化。但输出的图像只是黑色的。如何解决?

import cv2
import numpy as np
import Image

img = cv2.imread('img7.jpg')
gray_image = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
a = np.asarray(gray_image)


dst = np.zeros(shape=(5,2))

b=cv2.normalize(a,dst,0,255,cv2.NORM_L1)


im = Image.fromarray(b)

im.save("img50.jpg")

cv2.waitKey(0)
cv2.destroyAllWindows()

3
将图像使用L1进行归一化的动机是什么? - Utkarsh Sinha
3个回答

84

如果您想将范围更改为[0,1],请确保输出数据类型为float

image = cv2.imread("lenacolor512.tiff", cv2.IMREAD_COLOR)  # uint8 image
norm_image = cv2.normalize(image, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)

3
Python要求我指定一个目标输入参数。在这种情况下,你可能想将norm_image初始化为image的副本,并将其作为dst传递进去。 - ckirksey3
6
你只需要将 dst 参数设置为 None,不需要增加多余的代码行。 - Dan Mašek

25
其他答案基于整个图像来进行图像归一化。但是,如果您的图像具有主导颜色(例如黑色),它将掩盖您要增强的特征,因为它将不会如此明显。为了避免这种限制,我们可以基于感兴趣的子区域(ROI)对图像进行归一化。实质上,我们将根据要增强的图像部分对图像进行归一化,而不是平等地处理每个像素。以这张地球图片为例:
输入图像 -> 基于整个图像的归一化

如果我们想通过基于整个图像的归一化来增强云层,则结果不会非常清晰,并且由于黑色背景而过度饱和。需要增强的特征将会丢失。因此,为了获得更好的结果,我们可以裁剪出一个感兴趣区域(ROI),基于ROI进行归一化,然后将归一化应用回原始图像中。假设我们裁剪出了绿色高亮显示的ROI:

这给我们带来了投资回报率{{ROI}}。

这个想法是计算ROI的平均值和标准差,然后根据下限和上限范围裁剪帧。此外,我们可以使用偏移量来动态调整剪辑强度。从这里开始,我们将原始图像归一化到这个新范围。这是结果:

之前->之后

代码

import cv2
import numpy as np

# Load image as grayscale and crop ROI
image = cv2.imread('1.png', 0)
x, y, w, h = 364, 633, 791, 273
ROI = image[y:y+h, x:x+w]

# Calculate mean and STD
mean, STD  = cv2.meanStdDev(ROI)

# Clip frame to lower and upper STD
offset = 0.2
clipped = np.clip(image, mean - offset*STD, mean + offset*STD).astype(np.uint8)

# Normalize to range
result = cv2.normalize(clipped, clipped, 0, 255, norm_type=cv2.NORM_MINMAX)

cv2.imshow('image', image)
cv2.imshow('ROI', ROI)
cv2.imshow('result', result)
cv2.waitKey()

基于整个图像和ROI特定部分进行归一化的区别可以通过将热力图应用于结果来可视化。请注意云的定义方式的差异。

输入图像->热力图

整张图片归一化 -> 热力图

基于ROI进行归一化 -> 热力图

热力图代码

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

image = cv2.imread('result.png', 0)
colormap = plt.get_cmap('inferno')
heatmap = (colormap(image) * 2**16).astype(np.uint16)[:,:,:3]
heatmap = cv2.cvtColor(heatmap, cv2.COLOR_RGB2BGR)

cv2.imshow('image', image)
cv2.imshow('heatmap', heatmap)
cv2.waitKey()

注意: ROI边界框坐标是使用如何在不猜测和检查的情况下获取ROI边界框坐标获得的,热力图代码来自如何使用Python OpenCV将灰度图像转换为热力图像


2
非常好的答案。只有一个小点需要注意。我注意到可以使用skimage rescale_intensity将np.clip和cv2.normalize组合成一个命令。请参见https://scikit-image.org/docs/dev/api/skimage.exposure.html#skimage.exposure.rescale_intensity。 - fmw42

19

使用NORM_L1对矩阵进行归一化时,您将每个像素值除以图像中所有像素的绝对值之和。因此,所有像素值都变得远小于1,导致生成一个黑色图像。尝试使用NORM_MINMAX代替NORM_L1。


3
NORM_MINMAX会发生什么? - Jeru Luke
7
最小像素值将被映射到最小输出值(alpha),最大像素值将被映射到最大输出值(beta)。我相信在它们之间的所有内容都会采用线性缩放。 - rsaxvc
2
NORM_MINMAX的计算方式类似于((像素值-alpha)/(beta-alpha))*beta。这与@rsaxvc的观点一致。 - bincob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接