我正在尝试在 python 中进行一些图像操作,但我在堆栈溢出方面遇到了一些麻烦。阅读了一些内容后,我编辑了 np.array 以获取额外的参数 dtype='int64'。 (之前工作得很好,前提是我没有调用阈值方法)
这解决了异常错误,但是当我尝试绘制图像来绘制它时,它不起作用。也不异常(exception),它什么也没绘制。该错误不在阈值方法中,因为即使我将其注释掉并运行它,它仍然不会绘制。你知道我做错了什么吗?
完整代码如下:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import time
import math
def threshold(imageArray):
balanceAr = []
newArray = imageArray
for eachRow in imageArray:
for eachPix in eachRow:
avgNum = reduce(lambda x, y: x + y, eachPix[:3])/float(len(eachPix[:3]))
balanceAr.append(avgNum)
balance = reduce(lambda x , y: x + y , eachPix[:3]/float(len(balanceAr)))
for eachRow in newArray:
for eachPix in eachRow:
if reduce(lambda x , y: x + y, eachPix[:3])/float(len(eachPix[:3])) > balance:
eachPix[0] = 255
eachPix[1] = 255
eachPix[2] = 255
eachPix[3] = 255
else:
eachPix[0] = 0
eachPix[1] = 0
eachPix[2] = 0
eachPix[3] = 255
y = Image.open('images/numbers/y0.5.png')
yar = np.asarray(y, dtype='int64')
threshold(yar)
fig = plt.figure()
ax3 = plt.subplot2grid((8,6), (0,3), rowspan=4, colspan=3)
ax3.imshow(yar)
plt.show()
最佳答案
我不知道为什么 MatPlotLib 不会绘制您的图像,但我可以告诉您threshold
函数的一些问题。
您编写了
newArray = imageArray
但这仅意味着newArray
是同一数组的另一个名称。这意味着您的threshold
函数会覆盖原始图像,这可能非常不方便(尤其是在测试时)。您可能想要复制该图像:newArray = imageArray.copy()
在这组行中:
balanceAr = [] for eachRow in imageArray: for eachPix in eachRow: avgNum = reduce(lambda x, y: x + y, eachPix[:3])/float(len(eachPix[:3])) balanceAr.append(avgNum)
您正在计算一个(展平的)数组
balanceAr
,其条目是每个像素的前三个 channel 的平均值。您可以通过循环图像中的每个像素来完成此操作。但是,当您可以对代码进行向量化并在一次操作中计算所有像素的结果时,NumPy 的效率最高。在这种情况下,您可以使用 NumPy's fancy indexing获取图像的前三个 channel :colour_channels = imageArray[...,:3]
然后调用
numpy.mean
获取每个像素的平均值:balanceAr = colour_channels.mean(axis=-1)
(这会构造一个二维数组:如果您确实想要一个扁平版本,您可以调用
flatten
方法,但这不是必需的,我将在下面解释。)这一行:
balance = reduce(lambda x , y: x + y , eachPix[:3]/float(len(balanceAr)))
看来您的意图是计算
balanceAr
的平均值,但您搞砸了,只替换了eachPix[ 的一个出现: 3]
由balanceAr
提供。显然这会计算出错误的结果。当然,您需要的是:
balance = balanceAr.mean()
在下一组行中,将图像中平均颜色 channel 高于
平衡
的像素替换为白色,将低于平均颜色 channel 的像素替换为黑色。同样,您应该向量化此操作。您可以计算一个掩码数组,这是一个对于高于平均值的像素为True
的 bool 数组:mask = balanceAr > balance
构造一个合适大小的空图像:
result = np.empty(imageArray.shape)
将蒙版中的像素设置为白色,将其他像素设置为黑色:
result[mask] = (255, 255, 255, 255) result[~mask] = (0, 0, 0, 255)
更仔细地考虑这个算法,很明显您实际上不需要取颜色 channel 的平均值。除以 3 总是相同的,因此可以简单地省略它,并且我们可以使用颜色 channel 的总和来代替。 (调用
numpy.sum
而不是numpy.mean
。)将所有这些放在一起,我将如何对其进行编程:
import numpy as np WHITE = np.array((255, 255, 255, 255), dtype=np.uint8) BLACK = np.array(( 0, 0, 0, 255), dtype=np.uint8) def threshold2(img, high=WHITE, low=BLACK): """Return a new image whose pixels are `high` where pixels in `img` have a higher sum of colour channels than the average for the image, and `low` elsewhere. """ colsum = img[...,:3].sum(axis=-1) mask = colsum > colsum.mean() result = np.empty(img.shape, dtype=np.uint8) result[mask] = high result[~mask] = low return result
这比您的代码快大约 200 倍:
>>> from timeit import timeit >>> img = np.random.randint(0, 256, (400, 400, 4)) >>> timeit(lambda:threshold2(img), number=1) # mine 0.05198820028454065 >>> timeit(lambda:threshold(img), number=1) # yours 10.539333346299827
图像颜色 channel 的总和有点像 luminance图像的不同之处在于它没有考虑对 channel 的不同生理 react (绿色被认为比红色更亮,而红色被认为比蓝色更亮)。也许您应该使用 0.2126 R + 0.7152 G + 0.0722 B 而不是 R + G + B?
如果这是正确的,你需要这样的东西:
# sRGB luminosity coefficients, plus 0 for the alpha channel LUMINOSITY = np.array((0.2126, 0.7152, 0.0722, 0)) def threshold3(img, high=WHITE, low=BLACK, luminosity=LUMINOSITY): """Return a new image whose pixels are `high` where pixels in `img` have a higher luminance than the average for the image, and `low` elsewhere. The optional `luminosity` argument provides the multipliers for the red, green and blue channels. """ luminance = (img * luminosity).sum(axis=-1) mask = luminance > luminance.mean() result = np.empty(img.shape, dtype=np.uint8) result[mask] = high result[~mask] = low return result
关于Python、Numpy 堆栈溢出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22082602/