如何在OpenCV中将16位图像转换为8位图像?

时间:2014-08-25 12:24:00

标签: python opencv image-processing

我有一个16位灰度图像,我希望将它转换为OpenCV中的8位灰度图像,以便python使用它具有各种功能(如findContours等)。是可以在python中完成它还是我必须切换到C ++?

7 个答案:

答案 0 :(得分:12)

你可以使用numpy转换方法,因为OpenCV mat是一个numpy数组。

这有效:

img8 = (img16/256).astype('uint8')

答案 1 :(得分:2)

您可以使用NumPy在Python中通过查找表映射图像来完成此操作。

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)

答案 2 :(得分:2)

使用scipy.misc.bytescale转换为8位确实很容易。 OpenCV矩阵是一个numpy数组,因此字节比例将完全满足您的要求。

from scipy.misc import bytescale
img8 = bytescale(img16)

答案 3 :(得分:1)

scipy中的代码(现已弃用):

def bytescaling(data, cmin=None, cmax=None, high=255, low=0):
    """
    Converting the input image to uint8 dtype and scaling
    the range to ``(low, high)`` (default 0-255). If the input image already has 
    dtype uint8, no scaling is done.
    :param data: 16-bit image data array
    :param cmin: bias scaling of small values (def: data.min())
    :param cmax: bias scaling of large values (def: data.max())
    :param high: scale max value to high. (def: 255)
    :param low: scale min value to low. (def: 0)
    :return: 8-bit image data array
    """
    if data.dtype == np.uint8:
        return data

    if high > 255:
        high = 255
    if low < 0:
        low = 0
    if high < low:
        raise ValueError("`high` should be greater than or equal to `low`.")

    if cmin is None:
        cmin = data.min()
    if cmax is None:
        cmax = data.max()

    cscale = cmax - cmin
    if cscale == 0:
        cscale = 1

    scale = float(high - low) / cscale
    bytedata = (data - cmin) * scale + low
    return (bytedata.clip(low, high) + 0.5).astype(np.uint8)

答案 4 :(得分:0)

是的,您可以使用Python。要获得预期的结果,请根据您希望从uint16映射到uint8的值来选择一种方法。

例如,

如果您执行img8 = (img16/256).astype('uint8'),则将256以下的值映射为0

如果您进行的img8 = img16.astype('uint8')值大于255,则映射为0

在上述和更正的LUT方法中,您必须定义映射。

答案 5 :(得分:0)

Opencv提供功能cv2.convertScaleAbs()

image_8bit = cv2.convertScaleAbs(image, alpha=0.03)

Alpha只是可选的比例因子。也适用于多通道图像。

OpenCV documentation

缩放,计算绝对值并将结果转换为8位。

在输入数组的每个元素上,函数convertScaleAbs 按顺序执行三个操作:缩放,绝对 值,转换为无符号的8位类型:

有关Stackoverflow的其他信息:OpenCV: How to use the convertScaleAbs() function

答案 6 :(得分:-2)

使用python openCV从16位转换为8位:

import numpy as np
import cv2
imagePath = "--"

img_8bit = cv2.imread(imagePath).astype(np.uint8)