首页 > 解决方案 > RGB 图像的像素强度以及如何将其与整数相乘以查看灰度阴影

问题描述

我有一个 RGB 图像,它有 4 种不同的颜色黑色(0,0,0)作为背景,绿色(106,136,93)蓝色(64,224,208)和棕色(168,124,85)。当我将图像读取为灰度并使用 np.unique() 它返回一个巨大的像素强度列表。但实际上,只有 4 种强度,即 [0,1,2,3] 黑色、绿色、蓝色和棕色。

import cv2
import numpy as np

test = cv2.imread("test-BlackBG.png",0) #test image 

results = np.unique(test)     #returns [0,1,2,3,4,5,6,7,8...........132]
print(test.shape)             #returns (480, 640)
print(results)
cv2.imshow("image",test)
cv2.waitKey()
cv2.destroyAllWindows()

预期结果:当我将图像乘以 85 时,它应该以不同的灰度显示所有 3 种强度。

这是输入图像-

标签: pythonnumpyopencvimage-processing

解决方案


您的问题和假设存在许多问题。


你不能数颜色np.unique(image)

您无法用 计算图像中的颜色np.unique(im)。让我们看看为什么制作一个只有 4 个强度的随机图像:0、1、2 和 3。

import numpy as np
import cv2

# Ensure repeatable, deterministic randomness!
np.random.seed(42)

# Make a random image
im = np.random.randint(0,4,(480,640,3), dtype=np.uint8)

看起来像这样,其中每一行是一个像素的 RGB 三元组:

array([[[2, 2, 3],
    [3, 2, 1],
    [2, 2, 0],
    ...,
    [3, 3, 2],
    [0, 0, 1],
    [1, 1, 1]],
    ...,
    [3, 3, 1],
    [2, 3, 0],
    [0, 1, 3]]], dtype=uint8)

现在,如果您尝试获得这样的独特颜色,它将无法正常工作,因为每种颜色都是3 种强度的组合:

np.unique(im)    # prints: array([0, 1, 2, 3], dtype=uint8)

然而,如果您想要唯一颜色的数量,则需要查找三个 RGB/BGR 值的唯一组合的数量:

np.unique(im.reshape(-1, im.shape[2]), axis=0)

它给出了图像中唯一 RGB/BGR 三元组的向量 - 每行都是唯一的颜色组合:

array([[0, 0, 0],
       [0, 0, 1],
       [0, 0, 2],
       [0, 0, 3],
       [0, 1, 0],
       [0, 1, 1],
       [0, 1, 2],
       [0, 1, 3],
       [0, 2, 0],
       [0, 2, 1],
       [0, 2, 2],
       [0, 2, 3],
       [0, 3, 0],
       [0, 3, 1],
       [0, 3, 2],
       [0, 3, 3],
       [1, 0, 0],
       [1, 0, 1],
       [1, 0, 2],
       [1, 0, 3],
       [1, 1, 0],
       [1, 1, 1],
       [1, 1, 2],
       [1, 1, 3],
       [1, 2, 0],
       [1, 2, 1],
       [1, 2, 2],
       [1, 2, 3],
       [1, 3, 0],
       [1, 3, 1],
       [1, 3, 2],
       [1, 3, 3],
       [2, 0, 0],
       [2, 0, 1],
       [2, 0, 2],
       [2, 0, 3],
       [2, 1, 0],
       [2, 1, 1],
       [2, 1, 2],
       [2, 1, 3],
       [2, 2, 0],
       [2, 2, 1],
       [2, 2, 2],
       [2, 2, 3],
       [2, 3, 0],
       [2, 3, 1],
       [2, 3, 2],
       [2, 3, 3],
       [3, 0, 0],
       [3, 0, 1],
       [3, 0, 2],
       [3, 0, 3],
       [3, 1, 0],
       [3, 1, 1],
       [3, 1, 2],
       [3, 1, 3],
       [3, 2, 0],
       [3, 2, 1],
       [3, 2, 2],
       [3, 2, 3],
       [3, 3, 0],
       [3, 3, 1],
       [3, 3, 2],
       [3, 3, 3]], dtype=uint8)

或者,作为一组简单的独特颜色:

len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 64

因此,对于您的图像:

# Open image
im = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)

# Count unique colours
len(np.unique(im.reshape(-1, im.shape[2]), axis=0)    # prints 790

颜色比你想象的要多

为什么我的颜色比我预期的要多?最常见的两个原因是:

  • 图像被保存为 JPEG
  • 有抗锯齿的文本或绘制的形状

让我们看看另存为 JPEG 是如何搞砸你的!

# Load image and count colours
im = cv2.imread('image.png',cv2.IMREAD_UNCHANGED)
len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 790

# Save as JPEG
cv2.imwrite('temp.jpg',im)

# Reload and recount just the same
im = cv2.imread('temp.jpg',cv2.IMREAD_UNCHANGED)
len(np.unique(im.reshape(-1, im.shape[2]), axis=0))    # prints 4666 !!!

如何对图像进行调色 - (将颜色减少到固定调色板)?

如果您想将图像调色到您自己的特定调色板,首先您需要以 BGR 顺序 ( !) 指定您的调色板以匹配 OpenCV 的顺序:

palette = np.array([
   [0,0,0],                # Black
   [93,136,106],           # Green
   [208,224,64],           # Blue
   [85,124,168]],          # Brown
   dtype=np.uint8)

然后阅读您的图像,丢弃完全无意义的 alpha 通道:

test = cv2.imread("image.png",cv2.IMREAD_COLOR)

然后计算从每个像素到每个调色板条目的距离:

distance = np.linalg.norm(test[:,:,None] - palette[None,None,:], axis=3)

然后为每个像素选择最接近的调色板颜色:

palettised = np.argmin(distance, axis=2).astype(np.uint8)

您的图像现在位于数组中palettised,并且存储在每个像素位置是调色板中最近颜色的索引 - 因此,由于您的调色板有 4 个条目 (0..3),因此图像的所有元素都是 0、1、2或 3。

所以,现在你可以乘以 85:

result = palettised * 85

在此处输入图像描述


推荐阅读