代码之家  ›  专栏  ›  技术社区  ›  Alexandre Bensi

是否可以转换此代码的一部分以提高速度?

  •  0
  • Alexandre Bensi  · 技术社区  · 3 年前

    我是新来的,

    请,我需要帮助将此代码的一部分转换为任何东西,以提高速度,并且仍然与python程序兼容以使用此函数。有可能用这个来提高速度吗?

    在我的系统中,一个224x224颜色的图像需要360毫秒才能完成。

    #!/usr/bin/env python3
    # coding: utf-8
    # original code https://github.com/verhovsky/squircle/blob/master/squircle.py
    
    import cv2
    import math
    import time
    import numpy
    
    _epsilon = 0.0000000001
    
    def _sgn(x):
        if x == 0.0:
            return 0.0
        if x < 0:
            return -1.0
        return 1.0
    
    def _pixel_coordinates_to_unit(coordinate, max_value):
        return coordinate / max_value * 2 - 1
    
    def _one_coordinates_to_pixels(coordinate, max_value):
        return (coordinate + 1) / 2 * max_value
    
    def _stretch_square_to_disc(x, y):
        if (abs(x) < _epsilon) or (abs(y) < _epsilon):
            return x, y
    
        x2 = x * x
        y2 = y * y
        hypotenuse_squared = x * x + y * y
    
        reciprocal_hypotenuse = 1.0 / math.sqrt(hypotenuse_squared)
    
        multiplier = 1.0
    
        if x2 > y2:
            multiplier = _sgn(x) * x * reciprocal_hypotenuse
        else:
            multiplier = _sgn(y) * y * reciprocal_hypotenuse
    
        return x * multiplier, y * multiplier
    
    def _transform(inp):
        result = numpy.zeros_like(inp)
        for x, row in enumerate(inp):
    
            unit_x = _pixel_coordinates_to_unit(x, len(inp))
    
            for y, _ in enumerate(row):
                unit_y = _pixel_coordinates_to_unit(y, len(row))
    
                try:
                    uv = _stretch_square_to_disc(unit_x, unit_y)
                    if uv is None:
                        continue
                    u, v = uv
    
                    u = _one_coordinates_to_pixels(u, len(inp))
                    v = _one_coordinates_to_pixels(v, len(row))
    
                    result[x][y] = inp[math.floor(u)][math.floor(v)]
                except IndexError:
                    pass
    
        return result
    
    # -- load and test
    
    img = cv2.imread('circle.png')
    
    elapsed = round(time.time() * 1000)
    squareImage = _transform(img[0:224, 0:224])
    print(str(round(time.time() * 1000) - elapsed)+' ms to squareImage')
    
    cv2.imshow('square', squareImage)
    key = cv2.waitKey(0)
    
    cv2.destroyAllWindows()
    

    我本想尝试将这段代码或其中的一部分转换为更快速的代码,可能是CUDA直接在GPU上运行,也可能是NUMBA、Cython、lib等。。。

    1 回复  |  直到 3 年前
        1
  •  2
  •   Andrej Kesely    3 年前

    我用过 numba 装饰功能(删除 _sgn 并使用 np.sign ,删除了 try..except -需要吗?):

    import math
    import time
    
    import cv2
    import numpy
    from numba import njit
    
    _epsilon = 0.0000000001
    
    
    @njit
    def _pixel_coordinates_to_unit(coordinate, max_value):
        return coordinate / max_value * 2 - 1
    
    
    @njit
    def _one_coordinates_to_pixels(coordinate, max_value):
        return (coordinate + 1) / 2 * max_value
    
    
    @njit
    def _stretch_square_to_disc(x, y):
        if (abs(x) < _epsilon) or (abs(y) < _epsilon):
            return x, y
    
        x2 = x * x
        y2 = y * y
        hypotenuse_squared = x * x + y * y
    
        reciprocal_hypotenuse = 1.0 / np.sqrt(hypotenuse_squared)
    
        multiplier = 1.0
    
        if x2 > y2:
            multiplier = np.sign(x) * x * reciprocal_hypotenuse
        else:
            multiplier = np.sign(y) * y * reciprocal_hypotenuse
    
        return x * multiplier, y * multiplier
    
    
    @njit
    def _transform(inp):
        result = numpy.zeros_like(inp)
        for x, row in enumerate(inp):
            unit_x = _pixel_coordinates_to_unit(x, len(inp))
    
            for y, _ in enumerate(row):
                unit_y = _pixel_coordinates_to_unit(y, len(row))
    
                uv = _stretch_square_to_disc(unit_x, unit_y)
                if uv is None:
                    continue
    
                u, v = uv
    
                u = _one_coordinates_to_pixels(u, len(inp))
                v = _one_coordinates_to_pixels(v, len(row))
    
                result[x][y] = inp[math.floor(u)][math.floor(v)]
    
        return result
    
    
    # -- load and test
    
    img = cv2.imread("circle.png")
    
    # warm jit
    # this is needed to let numba do the JIT optimizations
    # if you run the the function "cold", the running time will be larger
    
    # you can use compile-ahead-of-time
    # https://numba.pydata.org/numba-doc/dev/user/pycc.html
    
    squareImage = _transform(img[0:224, 0:224])
    
    elapsed = time.perf_counter_ns()
    squareImage = _transform(img[0:224, 0:224])
    print(str((time.perf_counter_ns() - elapsed) / 1000) + " us to squareImage")
    
    cv2.imwrite("shashed.png", squareImage)
    

    在我的电脑(AMD 5700X)上,它打印:

    528.596 us to squareImage
    
    # without using numba:
    # 47928.774 us to squareImage
    

    使用的图像:

    circle.png

    enter image description here

    结果

    enter image description here


    推荐文章