【问题标题】:How to convert a 16-bit to an 8-bit image in OpenCV?如何在 OpenCV 中将 16 位图像转换为 8 位图像?
【发布时间】:2014-10-18 14:27:06
【问题描述】:

我有一个 16 位灰度图像,我想在 OpenCV for Python 中将其转换为 8 位灰度图像,以便将其与各种功能(如 findContours 等)一起使用。如何在 Python 中做到这一点?

【问题讨论】:

  • 在 c++ 中使用 .convertTo 完成。也许您可以通过这些信息找到它。此外,如果您想转换为二进制(例如 findContours),一个简单的 > 0 操作可能会解决问题。
  • @Micka 即使我用你提到的方法改变图像深度,OpenCV 仍然会将图像视为 16 位图像。
  • 抱歉,python openCv 帮不上忙。只使用过 c++ openCV。

标签: python numpy opencv image-processing


【解决方案1】:

您可以使用 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)

【讨论】:

  • +1 的答案,虽然另一个更快:) 我想知道是否可以以某种方式使用.clip(lb, ub),因为它可以很好地处理None
  • @uhoh clip() 不会处理lower_boundupper_bound 都是None 的情况。 img.clip(None, None) 提高 ValueError: array_clip: must set either max or min
  • 老鼠!那是个很好的观点。我对此感到惊讶;我想知道为什么决定不处理那个案子。这两个值可能来自不同的地方,并且提前彼此不知道。例如,在 matplotlib plt.xlim(None, None) 中可以正常工作。好的,我会记住这一点。谢谢!
  • 这里必须修改: if lower_bound is not None: if not(0 16): raise ValueError( '"lower_bound" 必须在 [0, 65535] 范围内]') if upper_bound is not None: if not(0 16): raise ValueError( '"upper_bound" must be in the range [0, 65535]')
【解决方案2】:

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

这行得通:

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

【讨论】:

  • 出于处理目的,您可以添加另一个缺口:ratio = np.amax(im16) / 256 ; img8 = (im16 / ratio).astype('uint8')
【解决方案3】:

使用 scipy.misc.bytescale 转换为 8 位真的很容易。 OpenCV 矩阵是一个 numpy 数组,因此 bytescale 将完全满足您的需求。

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

【讨论】:

  • bytescale 在 SciPy 1.0.0 中已弃用,并将在 1.2.0 中删除。
  • 这已被 skimage.util.img_as_ubyte 取代
【解决方案4】:

来自 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)

【讨论】:

    【解决方案5】:

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

    例如,

    如果你这样做 img8 = (img16/256).astype('uint8') 低于 256 的值将映射到 0

    如果你这样做img8 = img16.astype('uint8') 255 以上的值将映射到 0

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

    【讨论】:

      【解决方案6】:

      Opencv提供函数cv2.convertScaleAbs()

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

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

      OpenCVdocumentation:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-08-18
        • 2018-02-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多