颜色还原 - 仅生成红色

Color Reduction - Only Reds are generated

我正在尝试将 ppm 图像文件的颜色从 255 种颜色减少到 5 种颜色(红色、蓝色、绿色、黑色和白色)。当我启动我的测试文件时,生成的 pixel_list 只有红色,这是不正确的。我正在使用欧氏距离公式来查找像素最接近的 5 种颜色中的哪一种,并将值更改为该颜色。 (颜色还原)

这是我的数据示例:(前三行是文件类型、尺寸和总颜色。后面几行是按 r、g、b 排序的像素数据。)

P3
200 200
255
0
48
255
216
52
180
252
255
176
212
96
4
0
108
20
40
64
80
140
0
80

我的代码:

import math

with open('test_pattern.ppm','r') as f:
    output = f.read().split("\n")

i = 0
r_point = 3 + i
g_point = 4 + i
b_point = 5 + i
pixel_list = []

resolution = []
resolution.append(output[1].split(" "))
file_size = resolution[0]
file_size = int(file_size[0]) * int(file_size[1])
file_size = int(file_size*3)
print(file_size)

while file_size >= i:
    r = int(output[r_point])
    g = int(output[g_point])
    b = int(output[b_point])
    if all(math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) < x for x in [math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)]):
        r = 255
        g = 0
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    elif all(math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2) < x for x in [math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2) , math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)]):
        r = 0
        g = 255
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i +=3


    elif all(math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2) < x for x in [math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)]):
        r = 0
        g = 0
        b = 255
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    elif all(math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2) < x for x in [math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2) , math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)]):
        r = 0
        g = 0
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    elif all(math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2) < x for x in [math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2) , math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2) , math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2)]):
        r = 255
        g = 255
        b = 255
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


print(pixel_list)

第二次尝试:

import math

with open('test_pattern.ppm','r') as f:
    output = f.read().split("\n")
i = 0
r_point = 3 + i
g_point = 4 + i
b_point = 5 + i
pixel_list = []
resolution = []
resolution.append(output[1].split(" "))
file_size = resolution[0]
file_size = int(file_size[0]) * int(file_size[1])
file_size = int(file_size*3)
print(file_size)


while file_size >= i:
    r = int(output[r_point])
    g = int(output[g_point])
    b = int(output[b_point])
    a = math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2)
    b = math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2)
    c = math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2)
    d = math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2)
    e = math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)

    L = [a, b, c, d, e]
    idx = min(range(len(L)), key=L.__getitem__)

    if idx == 0:
        # red
        r = 255
        g = 0
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3

    if idx == 1:
        # green
        r = 0
        g = 255
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    if idx == 2:
        # blue
        r = 0
        g = 0
        b = 255
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    if idx == 3:
        # white
        r = 0
        g = 0
        b = 0
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


    if idx == 4:
        # black
        r = 255
        g = 255
        b = 255
        pixel_list.append(r)
        pixel_list.append(g)
        pixel_list.append(b)
        i += 3


print(pixel_list)

你的第一个 if 语句等同于:

math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2) < math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2): #your last boolean in your brackets

这是因为 python 计算布尔值的方式:

>>> 4 and 5
5

我假设您想确保第一个语句小于 < 运算符右侧的所有表达式。你应该这样做:

if all(firstexpression < x for x in [all your expressions in here]):

这利用了 pythons all 函数,它接受一个列表和 returns 是否每个元素都断言为真。

您使用的 and 有误。它将浮点数与 and 组合在一起 0.0 或最后一个评估值。你也在计算和 比你需要的更多地重新计算相同的值。你也在计算 平方根,这是昂贵的,当你不需要的时候。

计算五个距离的平方,一次,然后看哪个是 最少。

你似乎在这里说什么

a < b and c and d and e

a < b and a < c and a < d and a < e

但是无论如何,当您真正感兴趣的是 最小值.

时,一遍又一遍地计算和比较这些值是非常低效的

所以考虑:

a = math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2)
b = math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2)
c = math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2)
d = math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2)
e = math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)

L = [a, b, c, d, e]
idx = min(range(len(L)), key=L.__getitem__)

现在 idxL 中最小项的索引,因此您可以使用该索引来 select 颜色

if idx == 0:
    # red
if idx == 1:
    # green
if idx == 2:
    # blue
if idx == 3:
    # white
if idx == 4:
    # black

旁白:这是一个很好的优化不要打扰 sqrt,因为它不影响排序。

这是一个示例运行,使用您问题的第一个像素 (r=0, g=48, b=255)

>>> import math
>>> r = 0
>>> g = 48
>>> b = 255
>>> a = math.sqrt((r-255)**2 + (g - 0)**2 + (b-0)**2)
>>> b = math.sqrt((r-0)**2 + (g - 255)**2 + (b-0)**2)
>>> c = math.sqrt((r-0)**2 + (g - 0)**2 + (b-255)**2)
>>> d = math.sqrt((r-0)**2 + (g - 0)**2 + (b-0)**2)
>>> e = math.sqrt((r-255)**2 + (g - 255)**2 + (b-255)**2)
>>> L = [a, b, c, d, e]
>>> idx = min(range(len(L)), key=L.__getitem__)
>>> idx
2

我只是把它放在那里作为补充信息,因为它使用了 NumPy:

import numpy as np
import matplotlib.pyplot as plt

a = np.random.random((50, 50, 3))
do = np.ones_like(a, dtype=bool)
do[0], do[-1], do[:,0], do[:,-1] = False, False, False, False

a2 = 0.25*(np.roll(a, 1, axis=0) + np.roll(a, -1, axis=0) +
           np.roll(a, 1, axis=1) + np.roll(a, -1, axis=1)) #smooth a little
b = a.copy()
b[do] = a2[do]

a, b = a[1:-1,1:-1], b[1:-1,1:-1] # trim the edge

c = (b - b.min()) / (b.max() - b.min())  # rescale a bit

colors = np.array([[1,0,0], [0,1,0], [0,0,1], [1,1,1], [0,0,0]], dtype=float)

dsqa = ((a[...,None,:]-colors[None,None,...])**2).sum(axis=-1)
dsqb = ((b[...,None,:]-colors[None,None,...])**2).sum(axis=-1)
dsqc = ((c[...,None,:]-colors[None,None,...])**2).sum(axis=-1)

i_closesta = np.argmin(dsqa, axis=-1)
i_closestb = np.argmin(dsqb, axis=-1)
i_closestc = np.argmin(dsqc, axis=-1)

a_reduced = colors[i_closesta]
b_reduced = colors[i_closestb]
c_reduced = colors[i_closestc]

fs = 18
plt.figure()
plt.subplot(2,3,1)
plt.imshow(a, interpolation="nearest")
plt.title("raw noise", fontsize=fs)
plt.subplot(2,3,4)
plt.imshow(a_reduced, interpolation="nearest")
plt.title("raw noise reduced", fontsize=fs)
plt.subplot(2,3,2)
plt.imshow(b, interpolation="nearest")
plt.title("smoothed noise", fontsize=fs)
plt.subplot(2,3,5)
plt.imshow(b_reduced, interpolation="nearest")
plt.title("smooth noise reduced", fontsize=fs)
plt.subplot(2,3,3)
plt.imshow(c, interpolation="nearest")
plt.title("smoothed, rescaled noise", fontsize=fs)
plt.subplot(2,3,6)
plt.imshow(c_reduced, interpolation="nearest")
plt.title("smoothed, rescaled noise reduced", fontsize=fs)
plt.show()

你似乎对事情有一些误解。一个是 if expression0 < (expression1, expression2, etc) 会将第一个值与其他每个值进行比较。有必要明确地将第一个与每个进行比较: if expression0 < expression1 and expression0 < expression2 and ....

另一个是如果你有:

i = 0
r_point = 3 + i
g_point = 4 + i
b_point = 5 + i

并更改集 i += 3 使 r_point 和其他值将自动更改。同样,您必须明确地执行此操作。

这是您的代码的修改版本,可以满足您的需求。它还使用了几个函数来简化代码。

from math import fsum, sqrt

def dist(c1, c2):
    " Euclidean distance between two colors. "
    diffs = (b-a for a, b in zip(c1, c2))
#    return sqrt(fsum(diff*diff for diff in diffs))
    return fsum(diff*diff for diff in diffs)  # distance squared will work

def clostest_color(color, base_colors):
    " Return the color in 'base_colors' that is closest to 'color'. "
    dists = [dist(color, base_color) for base_color in base_colors]
    min_dist = min(dists)
    return base_colors[dists.index(min_dist)]

BASE_COLORS = (255,0,0), (0,255,0), (0,0,255), (0,0,0), (255,255,255)

with open('test_pattern.ppm', 'r') as f:
    output = f.read().splitlines()

width, height = (int(v) for v in output[1].split(' '))
num_pixels = width * height
print('num_pixels: {}'.format(num_pixels))

pixel_list = []
pixel_count = 0
OFFSET = 3
r_point = pixel_count*3 + OFFSET
g_point = r_point + 1
b_point = r_point + 2
while pixel_count < num_pixels:
    r, g, b = int(output[r_point]), int(output[g_point]), int(output[b_point])
    r, g, b = clostest_color((r, g, b), BASE_COLORS)
    pixel_list.extend((r, g, b))
    pixel_count += 1
    r_point, g_point, b_point = r_point+3, g_point+3, b_point+3