【问题标题】:Slow numpy array handling python缓慢的numpy数组处理python
【发布时间】:2020-08-23 05:53:33
【问题描述】:

我已经开始使用图像,目前我正在尝试重新缩放和灰度图像(尺寸 6000x4000 -> 600x400)以更好地使用它。为此,我使用 Numpy 和 PIL.Images。

import PIL.Image as Img
import numpy as np

img = Img.open('rendering/testpic.jpg', 'r')

r, g, b = img.split()
channels = np.array([np.array(r), np.array(g), np.array(b)])

small_channels = []
for channel in channels:
    x_len = len(channel)//10
    y_len = len(channel[0])//10
    for chunk_x in range(x_len):
        for chunk_y in range(y_len):
            pix_sum = 0
            for x_in_chunk in range(10):
                for y_in_chunk in range(10):
                    pix_sum += channel[chunk_x*10+x_in_chunk,chunk_y*10+y_in_chunk]
            channel[chunk_x,chunk_y] = pix_sum // 100
    small_channels.append(channel[:x_len,:y_len])

channels = np.array(small_channels)

grayscale = np.round((channels[0]*0.3+ channels[1]*0.6+ channels[2]*0.1)).astype('uint8')
pixels = np.stack([grayscale, grayscale, grayscale], axis = 2)
new_img = Img.fromarray(pixels)
new_img.show()

所以我正在做的是将通道分成大小为 10 的块,然后将块的平均值映射到左上角。最后我把图片的其余部分剪掉了。

对我来说,这总共需要大约 100 到 130 秒。有没有更快的方法来做到这一点?我哪里效率低了?我是新手,所以我可能做错了很多事情。例如,Photoshop 是如何快速放大和缩小图片的?

【问题讨论】:

  • 预期结果是什么?
  • 你应该考虑OpenCV,据报道它比PIL快得多。
  • @sk500 正如我所说,它应该将图像重新缩放到其原始大小的十分之一。
  • 对于使用 numpy 进行灰度化,您可能想在此处查看我的答案:stackoverflow.com/a/65919555/6342392,这种方法需要 0.026 秒才能运行

标签: python arrays image performance numpy


【解决方案1】:

我们可以使用numpy 数组切片和一些方法来加快处理速度,而不是循环遍历图像中的每个像素。我已经移除了内部循环并使用了切片和numpy 数组的.sum() 方法:

import PIL.Image as Img
import numpy as np

img = Img.open('rendering/testpic.jpg', 'r')

r, g, b = img.split()
channels = np.array([np.array(r), np.array(g), np.array(b)])

small_channels = []
for channel in channels:
    x_len = len(channel)//10
    y_len = len(channel[0])//10
    for chunk_x in range(x_len):
        for chunk_y in range(y_len):
            # slice all pixels within 10*10 box and sum them
            pix_sum = channel[chunk_x*10:10*(chunk_x+1),chunk_y*10:10*(chunk_y+1)].sum()
            channel[chunk_x, chunk_y] = pix_sum // 100
    small_channels.append(channel[:x_len,:y_len])

channels = np.array(small_channels)

grayscale = np.round((channels[0]*0.3+ channels[1]*0.6+ channels[2]*0.1)).astype('uint8')
pixels = np.stack([grayscale, grayscale, grayscale], axis = 2)
new_img = Img.fromarray(pixels)
new_img.show()

根据我的测试,这个算法快了 3-4 倍。我希望这有帮助。一定要看看numpy 数组——它们非常有用,尤其是对于图像,并且在很多情况下计算速度更快。

【讨论】:

  • 你有什么建议可以改进我的代码(比如从头开始,从概念上讲)以重新缩放图像?
  • 有一些来自知名包的选项,例如它看起来像 skimage.transform.downscale_local_mean 看起来正是你想要的:scikit-image.org/docs/dev/api/…
  • 正如@sK500 所建议的,scipy.ndimage 和 openCV 还有其他选项。我会让他们看看他们的结果是否是你所追求的。否则我会尽可能多地使用 numpy 切片来加速你的代码。我希望这能给你一些想法!
【解决方案2】:

在这种情况下我不会使用循环,cv2.resize() 会完成这项工作。

这是三种方法的时间比较:

import PIL.Image as Img
import numpy as np
from time import perf_counter
import cv2


def timer(method):
    def timed(*args, **kwargs):
        t1 = perf_counter()
        result = method(*args, **kwargs)
        t2 = perf_counter() 
        print(f'{method.__name__} time: {t2 - t1} seconds')
        return result
    return timed


@timer
def resize_1(image_path, shrink):
    img = Img.open(image_path, 'r')
    r, g, b = img.split()
    channels = np.array([np.array(r), np.array(g), np.array(b)])
    small_channels = []
    for channel in channels:
        x_len = len(channel)//shrink
        y_len = len(channel[0])//shrink
        for chunk_x in range(x_len):
            for chunk_y in range(y_len):
                pix_sum = 0
                for x_in_chunk in range(shrink):
                    for y_in_chunk in range(shrink):
                        pix_sum += channel[chunk_x*shrink+x_in_chunk,chunk_y*shrink+y_in_chunk]
                channel[chunk_x,chunk_y] = pix_sum // 100
        small_channels.append(channel[:x_len,:y_len])
    channels = np.array(small_channels)
    grayscale = np.round((channels[0]*0.3+ channels[1]*0.6+ channels[2]*0.1)).astype('uint8')
    pixels = np.stack([grayscale, grayscale, grayscale], axis = 2)
    return Img.fromarray(pixels)


@timer
def resize_2(image_path, shrink):
    img = Img.open(image_path, 'r')
    r, g, b = img.split()
    channels = np.array([np.array(r), np.array(g), np.array(b)])
    small_channels = []
    for channel in channels:
        x_len = len(channel)//shrink
        y_len = len(channel[0])//shrink
        for chunk_x in range(x_len):
            for chunk_y in range(y_len):
                # slice all pixels within 10*10 box and sum them
                pix_sum = channel[chunk_x*shrink:shrink*(chunk_x+1),
                          chunk_y*shrink:shrink*(chunk_y+1)].sum()
                channel[chunk_x, chunk_y] = pix_sum // 100
        small_channels.append(channel[:x_len,:y_len])
    channels = np.array(small_channels)
    grayscale = np.round((channels[0]*0.3+ channels[1]*0.6+ channels[2]*0.1)).astype('uint8')
    pixels = np.stack([grayscale, grayscale, grayscale], axis = 2)
    return Img.fromarray(pixels)


@timer
def resize_3(image_path, shrink):
    image = cv2.imread(image_path)
    size = image.shape[:-1]
    new_size = tuple(int(item / shrink) for item in size)[::-1]
    resized = cv2.resize(image, tuple(new_size))
    gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
    return gray


if __name__ == '__main__':
    img = 'sample_image.png'
    shrink_by = 10
    image1, image2, image3 = [item(img, shrink_by) for item in [resize_1, resize_2, resize_3]]
    image1.show()
    image2.show()
    cv2.imshow('resize_3', image3)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

输出:

resize_1 time: 1.980221013 seconds
resize_2 time: 0.3170622839999999 seconds
resize_3 time: 0.01659756599999973 seconds

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-03
    • 2019-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多