我需要以1位运行长度编码格式压缩大量(2000)图像(2560x1440x1)。 (格式不是我的选择。)允许的最大运行长度为125次重复。
我使用Python,并尝试使用numpy和本机Python。使用numpy的速度更快。切换到Cython后,速度提高了200%,但是本机代码的速度比numpy快了(2倍)。
使用Cython的速度提高200%似乎太小了。我在做错什么吗?
对原生Python / Cython的主程序调用:
rlestack.append(rleEncode.encodedBitmap_Bytes_nonumpy(imgarr8.flatten(0).tolist()))
或使用Numpy / Cython:
rlestack.append(rleEncode.encodedBitmap_Bytes_withnumpy(imgarr8.flatten(0)))
(imgarr8是numpy.uint8的2560x1440 numpy数组)
Cython RLE编码方法在rleEncode.pyx中:
import numpy
cimport numpy
#!python
@cython.wraparound (False) #turn off negative indexing
@cython.boundscheck(False) # turn off bounds-checking
@cython.nonecheck(False)
def encodedBitmap_Bytes_nonumpy1D(list surfOrFile not None):
""" Converts image data from file on disk to RLE encoded byte string.
Encoding scheme:
Highest bit of each byte is color (black or white)
Lowest 7 bits of each byte is repetition of that color, with max of 125 / 0x7D
"""
#(width, height) = (1440, 2560)
cdef unsigned int nrPixels = 3686400
cdef unsigned int lastPixel = nrPixels - 1
# Count number of pixels with same color up until 0x7D/125 repetitions
rleData = bytearray() # convert bytearray to cdef array has no speed benefit
cdef unsigned char color = 0
cdef unsigned char prevColor = 0
cdef unsigned char black = 0
cdef unsigned char white = 1
cdef unsigned char nocolor=3
cdef unsigned char r
cdef unsigned char nrOfColor = 0
cdef unsigned char encValue = 0
cdef unsigned int pixelNr
cdef unsigned int isLastPixel = False
prevColor = nocolor
for pixelNr in range(nrPixels):
r = surfOrFile[pixelNr]
if (r and 0b10000000): #if (r<128)
color = white
else:
color = black
if prevColor == nocolor: prevColor = color
isLastPixel = (pixelNr == lastPixel)
if color == prevColor and nrOfColor < 0x7D and not isLastPixel:
nrOfColor = nrOfColor + 1
else:
# print (color,nrOfColor,nrOfColor<<1)
encValue = (prevColor << 7) | nrOfColor # push color (B/W) to highest bit and repetitions to lowest 7 bits.
rleData.append(encValue)
prevColor = color
nrOfColor = 1
return bytes(rleData)
#!python
@cython.boundscheck(False) # turn off bounds-checking
@cython.wraparound(False) # turn off bounds-checking
def encodedBitmap_Bytes_withnumpy(numpy.ndarray[numpy.npy_uint8,ndim=1] x):
# Encoding magic
cdef unsigned int n=0
cdef numpy.ndarray[numpy.npy_int64, ndim = 1] starts # npy_int64
cdef numpy.ndarray[numpy.npy_int64, ndim = 1] lengths# npy_int64
cdef numpy.ndarray[numpy.npy_uint8, ndim = 1] values # npy_uint8
where = numpy.flatnonzero
n = len(x)
starts = numpy.r_[0, where(~numpy.isclose(x[1:], x[:-1], equal_nan=True)) + 1]
lengths = numpy.diff(numpy.r_[starts, n])
values = x[starts]
# Reduce repetitions of color to max 0x7D/125 and store in bytearray
rleData = bytearray()
cdef unsigned int nr=0
cdef unsigned int col=0
cdef unsigned char color=0
cdef unsigned char encValue = 0
cdef unsigned int l=len(lengths)
cdef unsigned int i=0
for i in range (0,l):
nr=lengths[i]
col=values[i]
# color = (abs(col)>1) # slow
color = 1 if col else 0 # fast
while nr > 0x7D:
encValue = (color << 7) | 0x7D
rleData.append(encValue)
nr = nr - 0x7D
encValue = (color << 7) | nr
rleData.append(encValue)
# Needed is an byte string, so convert
return bytes(rleData)
我使用以下setup.py用python setup.py build_ext --inplace
编译rleEncode.pyx。
from distutils.core import setup
from Cython.Build import cythonize
import numpy
setup(
ext_modules=cythonize("rleEncode.pyx"),
include_dirs=[numpy.get_include()]
)
(我使用命令sudo apt-get install python3-dev
安装所需的python.h标头。)
答案 0 :(得分:1)
使用您的评论和https://kogs-www.informatik.uni-hamburg.de/~seppke/content/teaching/wise1314/20131128_letsch-gries-boomgarten-cython.pdf,我的搜索速度提高了10倍!
结果代码为:
cimport cython
import numpy as numpy
cimport numpy as numpy
DTYPE = numpy.uint8
ctypedef numpy.uint8_t DTYPE_t
#!python
@cython.wraparound (False) # turn off negative indexing
@cython.boundscheck(False) # turn off bounds-checking
@cython.nonecheck(False)
def encodedBitmap_Bytes_numpy1DBlock(numpy.ndarray[DTYPE_t,ndim=1] surfOrFile):
""" Converts image data from file on disk to RLE encoded byte string.
Encoding scheme:
Highest bit of each byte is color (black or white)
Lowest 7 bits of each byte is repetition of that color, with max of 125 / 0x7D
Credits for speed to:
https://kogs-www.informatik.uni-hamburg.de/~seppke/content/teaching/wise1314/20131128_letsch-gries-boomgarten-cython.pdf
https://stackoverflow.com/questions/53135050/why-is-cython-only-20-faster-on-runlengthencode-b-w-image
"""
# Make room for rleData
cdef numpy.ndarray [DTYPE_t,ndim=1] rleData = numpy.zeros((3686400),dtype=DTYPE)
# Some constants for nr of pixels and last pixelnr
cdef unsigned int nrPixels = 3686400 #(width, height) = (1440, 2560)
cdef unsigned int lastPixel = nrPixels - 1
# Count number of pixels with same color up until 0x7D/125 repetitions
cdef unsigned char color = 0
cdef unsigned char prevColor = 0
cdef unsigned char r
cdef unsigned char nrOfColor = 0
cdef unsigned char encValue = 0
cdef unsigned int pixelNr
cdef unsigned int nrBytes=0
prevColor = surfOrFile[0] >> 7 #prevColor = nocolor
for pixelNr in range(nrPixels):
r = surfOrFile[pixelNr]
color = r >> 7 #if (r<128) color = 1 else: color = 0
if color == prevColor and nrOfColor < 0x7D:# and not isLastPixel:
nrOfColor = nrOfColor + 1
else:
encValue = (prevColor << 7) | nrOfColor # push color (B/W) to highest bit and repetitions to lowest 7 bits.
rleData[nrBytes]=encValue
nrBytes = nrBytes+1
prevColor = color
nrOfColor = 1
# Handle lastpixel, we did nrOfColor++ once too many
nrOfColor=nrOfColor-1
encValue = (prevColor << 7) | nrOfColor # push color (B/W) to highest bit and repetitions to lowest 7 bits.
rleData[nrBytes] = encValue
nrBytes = nrBytes + 1
# Remove excess bytes and return rleData
rleData=rleData[:nrBytes]
return bytes(rleData)