【问题标题】:How to reduce the execution time of this Python script for histogram matching?如何减少直方图匹配这个 Python 脚本的执行时间?
【发布时间】:2015-03-17 14:34:07
【问题描述】:

我在 Python 中实现了一种算法,用于减少图像中的强度变化(闪烁)。该算法首先计算图像每一行的累积直方图。然后,它使用高斯核对垂直方向(跨列)的累积行累积直方图进行过滤,从而减少行累积直方图之间的差异。在最后一部分,原始图像的每一行累积直方图应该与对应得到的经过高斯滤波的行累积直方图相匹配。因此,执行直方图匹配操作(每行),并重建所需的行。自然地,最终通过简单地将所有行垂直堆叠在一起来重建图像。

在我的代码中,最后一部分相互之间有两个 for 循环(针对每个强度级别 [0,255] 对每一行和每一行内部进行迭代)需要大量时间,因此使用该算法不再可行。对于单个 4592x3448 (16MP) 图像,在我的机器上执行时间超过 10 分钟。当然,为 3448 行中的每一行迭代超过 256 个强度值应该会大大减慢算法的速度,但由于算法的性质,我看不出有任何其他方法可以避免这些 for 循环。

我对 Python 很陌生,所以我在这里的编程方面可能犯了严重的罪行。我将不胜感激任何提示和代码审查。您可以在此链接下找到示例图像: http://s3.postimg.org/a17b3otpf/00000_cam0.jpg

import time
import cv2
import numpy as np
import scipy.ndimage as ndi
from matplotlib import pyplot as plt

start_time = time.time()
### Algorithm: Filtering row cumulative histograms with different Gaussian variances
T = 200     # threshold
img = cv2.imread('flicker.jpg',0)
rows,cols = img.shape
cdf_hist = np.zeros((rows,256))
for i in range(0,rows):    
    # Read one row
    img_row = img[i,]
    # Calculate the row histogram
    hist_row = cv2.calcHist([img_row],[0],None,[256],[0,256])
    # Calculate the cumulative row histogram
    cdf_hist_row = hist_row.cumsum()
    # Accumulate the cumulative histogram of each row
    cdf_hist[i,:] = cdf_hist_row

# Apply Gaussian filtering on the row cumulative histograms along the columns (vertically)   
# For dark pixels, use higher sigma
Gauss1_cdf = ndi.gaussian_filter1d(cdf_hist, sigma=6, axis=0, output=np.float64, mode='nearest')
# For bright pixel, use lower sigma
Gauss2_cdf = ndi.gaussian_filter1d(cdf_hist, sigma=3, axis=0, output=np.float64, mode='nearest')

##
print("--- %s seconds ---" % (time.time() - start_time))
### UNTIL HERE: it takes in my computer approx. 0.25 sec with a 16MP image

### This part takes too much time ###### START ######################
# Perform histogram matching (for each row) to either 'Hz1' or 'Hz2'
img_match = np.copy(img)
for r in range(0,rows):
    row = img[r,:]
    Hy = cdf_hist[r,:]       # Original row histogram
    Hz1 = Gauss1_cdf[r,:]    # Histogram 1 to be matched
    Hz2 = Gauss2_cdf[r,:]    # Histogram 2 to be matched
    row_match = img_match[r,:]
    for i in range(0,255):      # for each intensity value
        # Find the indices of the pixels in the row whose intensity = i
        ind = [m for (m,val) in enumerate(row) if val==i]
        j = Hy[i]
        while True:
            # use the appropriate CDF (Hz1 or Hz2) according to the bin number
            if i<T:
                k = [m for (m,val) in enumerate(Hz1) if val>j-1 and val<j+1]
            else:
                k = [m for (m,val) in enumerate(Hz2) if val>j-1 and val<j+1]
            if len(k)>0:
                break
            else:
                j = j+1
        row_match[ind] = k[0]        
###################### END ####################################    

# Set upper bound to the change of intensity values to avoid brightness overflows etc.
alfa = 5
diff_img = cv2.absdiff(img,img_match)
img_match2 = np.copy(img_match)
img_match2[diff_img>alfa] = img[diff_img>alfa] + alfa

## Plots
plt.subplot(121), plt.imshow(img,'gray')
plt.subplot(122), plt.imshow(img_match2, 'gray')
plt.show()

print("--- %s seconds ---" % (time.time() - start_time))

【问题讨论】:

  • 这将是一个更好的地方来提出您的问题:codereview.stackexchange.com 在这里提出问题的一个问题是,人们普遍希望这个问题有一个单一的答案。你的问题不够清楚,只有一个答案。我可能会看到一个错误。其他人可能会看到另一个...
  • 我假设 time.time() 被用来衡量计算需要多长时间?更好的方法是使用 timeit。
  • 而不是if len(k)&gt;0:,您可以使用if k:,但无论如何您都不应该定义k。请参阅下一条评论。
  • 它看起来像这里:k = [m for (m,val) in enumerate(Hz2) if val&gt;j-1 and val&lt;j+1]你只想找到j是否出现在Hz2中,稍后你想知道它出现的第一个位置(我假设它必须是一个int )。所以首先,只需用if j in Hz2: 替换k=..., if len(k)&gt;0:。然后做row_match[ind] = Hz2.index(j)
  • 我会给出一个解决方案——但如果我能理解ind 是什么就更好了。它看起来像是一个列表,但它也被用作索引?

标签: python algorithm image-processing histogram


【解决方案1】:

我不打算使用 timeit,但值得一试。我将不理会第一部分(最后一部分)。

### This part takes too much time ###### START ######################
# Perform histogram matching (for each row) to either 'Hz1' or 'Hz2'
img_match = np.copy(img)
for r in xrange(rows):  #no need to tell range to start from 0. and you should use xrange
    row = img[r,:]
    rowlength = len(row) #so you don't have to keep recalculating later.
    Hy = cdf_hist[r,:]       # Original row histogram
    Hz1 = Gauss1_cdf[r,:]    # Histogram 1 to be matched
    Hz2 = Gauss2_cdf[r,:]    # Histogram 2 to be matched
    Hz1Dict = {}
    Hz2dict = {}
    for index, item in enumerate(reversed(Hz1)):
        Hz1Dict[math.ceil(item)] = index
        Hz1Dict[math.floor(item)] = index
    for index, item in enumerate(reversed(Hz2)):
        Hz2Dict[math.ceil(item)] = index
        Hz2Dict[math.floor(item)] = index
    row_match = img_match[r,:]
    for i in xrange(255):      # for each intensity value
        # Find the indices of the pixels in the row whose intensity = i
        ind = [m for m in xrange(rowlength) if row[m]==i]
        j = Hy[i]
        while True:
            # use the appropriate CDF (Hz1 or Hz2) according to the bin number
            if i<T:
                if j in Hz1dict:
                    row_match[ind] = Hz1dict[j]
                    break
            else:
                if j in Hz2dict:
                    row_match[ind] = Hz2dict[j]
                    break
            else:
                j = j+1
###################### END ####################################    

显然你应该仔细检查我根本没有改变逻辑。

edit 我现在所做的是为每个赫兹记录存储一个字典,为每个 j 记录 j-1 和 j+1 之间的第一个数字的索引(注意枚举中的相反) .所以现在我只检查 dict 是否有对应的 j 作为键。如果是这样,我将 k row_match[ind] 设置为该值。

【讨论】:

  • 我为定义 ind 所做的可能不是最佳的。请参阅stackoverflow.com/questions/6294179/…,那里的主流观点似乎是您尝试过的。
  • 感谢您的快速回答。我想你忘了删除最后一行,不是吗? row_match[ind] = k[0]。还有一个问题:index() 是否适用于 np 数组?
  • 这段代码的问题是我实际上是在寻找j 可以在其中的范围,因为直方图Hy 和Hz1(或Hz2)并不完全包含匹配值,因此使用您的代码j 会不断增加。更准确地说Hy 例如包含类似 [2.0, 6.0, 8.0, 10.0 ...] 的值,而 Hz1(或 Hz2)包含类似 [7.1, 9.4, 11.7, 13.8 ...] 的值。所以,if j in Hz1 或 elif j in Hz2 永远不会成为真的。
  • 所以我正在研究如何修改这部分。您是否希望代码中的循环通常在第一个 j 之后退出,或者您是否希望它循环多次?我认为是后者,在这种情况下,我想我知道该怎么做,但直到以后才有机会进行修改。
  • 我认为修改后的代码会起作用。这仍然会循环遍历行和i,但内部循环现在应该是 O(1)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 1970-01-01
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多