如何在OpenCV中将16位图像转换为8位图像?
我有一张16位的灰度图像,现在想把它转换成8位的灰度图像,这样我就可以在Python的OpenCV中使用一些功能,比如findContours
等。请问我该怎么在Python中做到这一点呢?
7 个回答
2
使用scipy.misc.bytescale将数据转换为8位非常简单。OpenCV的矩阵其实是一个numpy数组,所以bytescale正好能满足你的需求。
from scipy.misc import bytescale
img8 = bytescale(img16)
3
你可以在Python中使用NumPy,通过查找表来处理图像。
import numpy as np
def map_uint16_to_uint8(img, lower_bound=None, upper_bound=None):
'''
Map a 16-bit image trough a lookup table to convert it to 8-bit.
Parameters
----------
img: numpy.ndarray[np.uint16]
image that should be mapped
lower_bound: int, optional
lower bound of the range that should be mapped to ``[0, 255]``,
value must be in the range ``[0, 65535]`` and smaller than `upper_bound`
(defaults to ``numpy.min(img)``)
upper_bound: int, optional
upper bound of the range that should be mapped to ``[0, 255]``,
value must be in the range ``[0, 65535]`` and larger than `lower_bound`
(defaults to ``numpy.max(img)``)
Returns
-------
numpy.ndarray[uint8]
'''
if not(0 <= lower_bound < 2**16) and lower_bound is not None:
raise ValueError(
'"lower_bound" must be in the range [0, 65535]')
if not(0 <= upper_bound < 2**16) and upper_bound is not None:
raise ValueError(
'"upper_bound" must be in the range [0, 65535]')
if lower_bound is None:
lower_bound = np.min(img)
if upper_bound is None:
upper_bound = np.max(img)
if lower_bound >= upper_bound:
raise ValueError(
'"lower_bound" must be smaller than "upper_bound"')
lut = np.concatenate([
np.zeros(lower_bound, dtype=np.uint16),
np.linspace(0, 255, upper_bound - lower_bound).astype(np.uint16),
np.ones(2**16 - upper_bound, dtype=np.uint16) * 255
])
return lut[img].astype(np.uint8)
# Let's generate an example image (normally you would load the 16-bit image: cv2.imread(filename, cv2.IMREAD_UNCHANGED))
img = (np.random.random((100, 100)) * 2**16).astype(np.uint16)
# Convert it to 8-bit
map_uint16_to_uint8(img)
4
OpenCV 提供了一个叫 cv2.convertScaleAbs()
的函数。
image_8bit = cv2.convertScaleAbs(image, alpha=0.03)
这里的 Alpha 是一个可选的缩放因子。这个函数也适用于多通道的图像。
OpenCV 的 文档 里说:
它会进行缩放、计算绝对值,并把结果转换成 8 位的格式。
这个 convertScaleAbs 函数对输入数组的每个元素依次执行三步操作:缩放、取绝对值、转换成无符号的 8 位类型:
在 Stackoverflow 上还有其他信息:OpenCV:如何使用 convertScaleAbs() 函数
4
在Python中,你是可以做到的。为了得到你想要的结果,你需要根据你想把uint16的值映射到uint8的方式来选择方法。
比如说,
如果你写 img8 = (img16/256).astype('uint8')
,那么小于256的值会被映射到0。
如果你写 img8 = img16.astype('uint8')
,那么大于255的值会被映射到0。
在上面提到并纠正的LUT方法中,你需要定义映射关系。
28
你可以使用numpy的转换方法,因为OpenCV的mat其实就是一个numpy数组。
这个方法是有效的:
img8 = (img16/256).astype('uint8')