用于循环计算的python性能提示

时间:2015-07-29 18:52:28

标签: python python-2.7

Python 2.7,Windows 7。

我正在寻找有关如何使计算繁重的脚本运行得更快的提示。首先想一想我在做什么:

从给定的颜色开始,我想生成一个包含30多种颜色(rgb值)的列表,这些颜色与人眼最大不同,并且列表的前面比末端更有特色。

目前,我估计脚本需要约48小时才能完成。我可以让它在周末运行,但我想我会借此机会学习一些有关python性能的知识。

概述代码的作用:

gen_colours()包含一个运行30次的循环。每次4个进程运行multi(n, l, g),其中包含遍历0到255之间的每个r,g和b值的大循环(r值在进程之间分配,因此它循环64次)。最内层循环包含另一个循环,它通过调用compute_dist([r, g, b], c)来检查已经找到的rgb值的rgb值。

无论如何,如果没有完全重构我的代码,那么有助于加速它的事情会很酷。另外,最多运行所有四个cpu 48小时......那里有问题吗?

代码:

from math import sqrt, pow, atan2, atan, sin, cos, exp, radians, degrees
from fractions import Fraction
import time
import multiprocessing


def to_xyz(rgb):
    r = rgb[0] / 255.0
    g = rgb[1] / 255.0
    b = rgb[2] / 255.0

    f = Fraction(12, 5)

    if r > 0.04045:
        r = ((r + 0.055) / 1.055) ** f
    else:
        r /= 12.92
    if g > 0.04045:
        g = ((g + 0.055) / 1.055) ** f
    else:
        g /= 12.92
    if b > 0.04045:
        b = ((b + 0.055) / 1.055) ** f
    else:
        b /= 12.92

    r *= 100
    g *= 100
    b *= 100

    # Observer = 2 degrees, Illuminant = D65
    x = r * 0.4124 + g * 0.3576 + b * 0.1805
    y = r * 0.2126 + g * 0.7152 + b * 0.0722
    z = r * 0.0193 + g * 0.1192 + b * 0.9505

    return [x, y, z]


def to_lab(xyz):

    x = xyz[0]
    y = xyz[1]
    z = xyz[2]

    # Observer= 2deg, Illuminant= D65
    x /= 95.047
    y /= 100.0
    z /= 108.883

    f = Fraction(1, 3)

    if x > 0.008856:
        x **= f
    else:
        x = 7.787 * x + 0.13793103448
    if y > 0.008856:
        y **= f
    else:
        y = 7.787 * y + 0.13793103448
    if z > 0.008856:
        z **= f
    else:
        z = 7.787 * z + 0.13793103448

    L = 116 * y - 16
    a = 500 * (x - y)
    b = 200 * (y - z)

    return [L, a, b]


def compute_dist(rgb1, rgb2):
    """ Compute the apparent difference in colours using CIEDE2000 standards """

    xyz1 = to_xyz(rgb1)
    xyz2 = to_xyz(rgb2)

    lab1 = to_lab(xyz1)
    lab2 = to_lab(xyz2)

    a1 = lab1[1]
    a2 = lab2[1]
    b1 = lab1[2]
    b2 = lab2[2]
    L1 = lab1[0]
    L2 = lab2[0]

    c1 = sqrt(a1 * a1 + b1 * b1)
    c2 = sqrt(a2 * a2 + b2 * b2)
    c = (c1 + c2) / 2
    crs = c ** 7
    x = 0.5 - 0.5 * sqrt(crs / (crs + 6103515625))
    temp = (1 + x) * a1
    c1 = sqrt(temp * temp + b1 * b1)
    h1 = hue(temp, b1)
    temp = (1 + x) * a2
    c2 = sqrt(temp * temp + b2 * b2)
    h2 = hue(temp, b2)
    dL = L2 - L1
    dc = c2 - c1
    if c1 * c2 == 0:
        dh = 0
    else:
        temp = round(h2 - h1, 12)
        if abs(temp) <= 180:
            dh = h2 - h1
        else:
            if temp > 180:
                dh = h2 - h1 - 360
            else:
                dh = h2 - h1 + 360
    dh = sqrt(c1 * c2) * sin(radians(dh / 2))
    dh += dh
    lav = (L1 + L2) / 2
    cav = (c1 + c2) / 2
    if c1 * c2 == 0:
       htot = h1 + h2
    else:
        temp = abs(round(h1 - h2, 12))
        if temp > 180:
            if h2 + h1 < 360:
                htot = h1 + h2 + 360
            else:
                htot = h1 + h2 - 360
        else:
            htot = h1 + h2
        htot /= 2

    T = 1 - 0.17 * cos(radians(htot - 30)) + 0.24 * cos(radians(2 * htot)) + 0.32 * cos(radians(3 * htot + 6)) - 0.20 * cos(radians(4 * htot - 63))
    htotdtme = (htot / 25) - 11
    xPH = 30 * exp(-htotdtme * htotdtme)
    cavrs = cav ** 7
    scocp = sqrt(cavrs / (cavrs + 6103515625))
    xRC = scocp + scocp
    lavmf = lav - 50
    lavmfs = lavmf * lavmf
    SL = 1 + 0.015 * lavmfs / sqrt(20 + lavmfs)
    SC = 1 + 0.045 * cav
    SH = 1 + 0.015 * cav * T
    RT = -sin(radians(xPH + xPH)) * xRC
    dL /= SL
    dc /= SC
    dh /= SH
    dE = sqrt(dL * dL + dc * dc + dh * dh + RT * dc * dh)

    return dE


def hue(a, b):          # Function returns CIELAB-Hue value

    c = 0
    if a >= 0 and b == 0:
        return 0
    if a < 0 and b == 0:
        return 180
    if a == 0 and b > 0:
        return 90
    if a == 0 and b < 0:
        return 270
    if a > 0 and b > 0:
        c = 0
    elif a < 0:
        c = 180
    elif b < 0:
        c = 360
    return degrees(atan(b / a)) + c


def multi(p, l, q):
    f = 0
    n = []

    s = p * 64
    e = (p + 1) * 64
    for r in xrange(s, e):
        for g in xrange(256):
            for b in xrange(256):
                s = 1000  # smallest dist

                for c in l:  # compare to existing colours

                    d = compute_dist([r, g, b], c)
                    if d < s:
                        s = d

                if s > f:
                    n = [r, g, b]
                    f = s

    q.put(f)
    q.put(n)


def gen_colours(start_col=[68, 68, 68]):

    out = open('colour_output.txt', 'w')
    l = [start_col]

    if __name__ == '__main__':

        q0 = multiprocessing.Queue()
        q1 = multiprocessing.Queue()
        q2 = multiprocessing.Queue()
        q3 = multiprocessing.Queue()

        for h in xrange(30):  # create 30 more colours

            p0 = multiprocessing.Process(target=multi, args=[0, l, q0])
            p1 = multiprocessing.Process(target=multi, args=[1, l, q1])
            p2 = multiprocessing.Process(target=multi, args=[2, l, q2])
            p3 = multiprocessing.Process(target=multi, args=[3, l, q3])

            p0.start()
            p1.start()
            p2.start()
            p3.start()

            p0.join()
            p1.join()
            p2.join()
            p3.join()

            d0 = q0.get()
            d1 = q1.get()
            d2 = q2.get()
            d3 = q3.get()
            c0 = q0.get()
            c1 = q1.get()
            c2 = q2.get()
            c3 = q3.get()

            d = [d0, d1, d2, d3]
            c = [c0, c1, c2, c3]

            m = max(d)
            i = d.index(m)
            n = c[i]

            l.append(n)
            out.write("[" + str(n[0]) + ", " + str(n[1]) + ", " + str(n[2]) + "]\n")
            print "\nnew colour added: " + str(l)

        out.close()
        print "Done"


gen_colours()

任何提示?

编辑:

一个明显的改进是我每次都在找到的rgb颜色上计算Lab值。我添加了一个列表来存储这些的Lab值,这样就不需要为每个循环执行此操作。这减少了大约1/4的时间。但不是我正在寻找的Python性能改进。

2 个答案:

答案 0 :(得分:1)

如果已经选择了颜色R:100 G:100 B:100,我确定R:100 G:100 B:101的颜色不是“最大特色”解决方案。

您可以做的一个快速改进是省略检查相似的颜色(即R和G值在给定范围内具有B值的相同)。

答案 1 :(得分:0)

你做得太多了。

当大多数监视器/色域/环境/眼睛组合比CIE计算产生的可辨性差得多时,您似乎正在使用24位RGB色彩空间。

假设您这样做是为了为真实的眼睛提供真实世界的颜色,您还必须考虑到无数形式的色盲,这会将您减少到小于12位的有用色彩空间。即使我们只是谈论亮度,当亮度接近设备色域的三分之一时,显着的差异也会变得更加稀疏。

你遇到的问题是算法;也就是说,当添加的细节无关紧要时,你正在努力工作以获得详细的结果。在#000和#fff之间,只有4096种可能的颜色,红绿轴可以被拒绝。