【问题标题】:Most efficient way to read in a JPEG image and determine brightness读取 JPEG 图像并确定亮度的最有效方法
【发布时间】:2017-11-28 19:30:42
【问题描述】:

我正在使用以下方法来获得感知亮度。我很好奇是否有更快的方法来做到这一点?

def brightness( im_file ):
   im = Image.open(im_file)
   stat = ImageStat.Stat(im)
   r,g,b = stat.rms
   return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))

要求

  • 性能是最重要的因素
  • 图像文件小于 30KB
  • 图像的亮度将相似。会有小的差异,但只看一小部分图像就足够了。

【问题讨论】:

  • numpyscipy 可能会更快,因为它可以大大加快计算速度。
  • 你能举一些例子吗?你希望亮度是多少?是全白1 和全黑0 吗?
  • 另外,我认为你可以使用多线程来加速读取图像。
  • @user1767754 可以。
  • @Sraw 图像按顺序实时读取

标签: python image image-processing


【解决方案1】:

PIL 在这方面效率极低。 PIL 是在纯 Python 中实现的,因此它在您的图像中循环in Python。我们来看看ImageStat.Stat.rms属性from the source code的实现:

def _getrms(self):
    "Get RMS for each layer"

    v = []
    for i in self.bands:
        v.append(math.sqrt(self.sum2[i] / self.count[i]))
    return v

使用辅助函数

def _getsum2(self):
    "Get squared sum of all pixels in each layer"

    v = []
    for i in range(0, len(self.h), 256):
        sum2 = 0.0
        for j in range(256):
            sum2 += (j ** 2) * float(self.h[i + j])
        v.append(sum2)
    return v

def _getcount(self):
    "Get total number of pixels in each layer"

    v = []
    for i in range(0, len(self.h), 256):
        v.append(functools.reduce(operator.add, self.h[i:i+256]))
    return v

我很困惑为什么count 有一个函数……但无论如何。无论如何,正如这里所证明的那样,这是非常低效的。以上内容可以直接使用 numpy 函数更有效地编写。


此外,OpenCV 在读取图像方面比 PIL 快得多。从my other answers 之一,我为读取图像的标准库计时,OpenCV 是最快的。


最后,我认为您不应该将 RMS 与欧几里德距离函数相加,原因有两个:

  1. 您使用的权重是用于在灰度图像之间进行转换的类型,该灰度图像是相乘和线性相加的。
  2. 如果图像是浮点数,则将值向下缩放,而如果值为uint8,则将值向上缩放。我不确定你是否真的想要那种规模。我认为线性比例更容易。

我也不确定您在这里使用的权重,蓝色的 0.068 非常小(只有亮度的 6%?)。 YUV standard 使用以下权重:

Y = 0.299*R + 0.587*G + 0.114*B

话虽如此,我的答案并未提供 实际 代码来解决您的问题,因此您应该选择可以解决问题的代码,例如 user1767754 的答案。我只是想指出PIL在这里慢的一些原因。

【讨论】:

  • 很高兴您提到了实现细节以及PIL和CV +1之间的速度关系!
【解决方案2】:

所以这里有一个解决方案,它的速度几乎快了两倍:

计时统计:10000 次运行 求和方法:9.4s ImageStat(您的):24.s

1) 计算所有像素的总和

2) 计算全白图像的总和

3) 现在将计算得到的像素总和映射到0(黑色)和1(白色)之间

import numpy as np
import cv2
from scipy.interpolate import interp1d

#Load Image
img1 = cv2.imread('testing_1.png',3) #READ BGR

width, height, depth = img1.shape
maxValue = width * height * depth * 255
imageValue = np.sum(img1)

#Map Value between 0 and 1
m = interp1d([0,maxValue],[0,1])
print m(imageValue)

Test1:非常暗的图像:值:0.126771993983

Test1:非常亮的图像:值:0.65562396666

【讨论】:

  • 请注意,时间差异既在于使用您使用的库,也在于读取图像; OpenCV 在读取图像时是twice as fast as PIL
  • 我实际上只运行了 PIL.opencv.imgread 的开放式测试,并且 PIL 快了大约 6 倍。要么它知道它是内存中的同一张图像是智能的(我很确定我在阅读后关闭了图像。或者它做了一些神奇的事情。
  • 可能是转换为[0, 1] 中的float numpy 数组减慢了我的测试速度,但是……嗯……这很有趣。也许应该将测试应用于大型数据库。
  • 我已经按照你的帖子进行了测试并在我的机器上运行它,当你进行转换时这可能是完全正确的。我实际上刚刚下载了一个 200 MB 的文件,让我们看看这个测试告诉我们什么。
【解决方案3】:

ImageStat.Stat 只能作用于图像的一个区域:

ImageStat.Stat(image, mask) ⇒ Stat 实例

计算给定图像的统计信息。如果包含口罩,则仅 该掩码覆盖的区域包含在统计信息中。

您说整个图像的亮度将相似,因此您可以为您的用例选择尽可能小的遮罩尺寸。最小可能的掩码大小当然是单个像素。但我猜你的大部分图像可能不是完全灰色的,所以你可以增加蒙版大小,直到找到一个让你满意的值。

您可以使用一些方法来尝试找到最佳蒙版尺寸。有一堆测试图像。计算它们的亮度没有蒙版,以及不同大小的蒙版。然后检查你得到什么不同。尽量减少差异。

还尝试选择不同的区域和形状:中心、左角、正方形、圆形、每隔一个像素。正如 Alexander Reynolds 在 cmets 中指出的那样,具有随机像素的蒙版可能是最佳选择,因为它可以毫无偏差地代表整个图像。

【讨论】:

  • 我认为随机掩码可能最适合最小化检查像素的数量,同时尽可能减少测试的偏差。
  • 对,当然。随机应该比中心好。谢谢!
猜你喜欢
  • 2011-06-20
  • 2012-02-14
  • 2011-08-22
  • 1970-01-01
  • 2010-11-29
  • 1970-01-01
  • 1970-01-01
  • 2015-03-22
  • 2017-03-04
相关资源
最近更新 更多