如何高效地对图像中每个像素的每个通道应用函数(用于颜色转换)?

4
我正在尝试实现Reinhard的方法,使用目标图像的颜色分布来归一化传入的图像以进行研究项目。我已经让代码能够正常输出,但速度很慢。遍历300张图片需要大约20分钟。我相信瓶颈在于如何处理将该函数应用于每个图像。我目前正在遍历图像的每个像素,并对每个通道应用以下函数。
def reinhard(target, img):

    #converts image and target from BGR colorspace to l alpha beta
    lAB_img = cv2.cvtColor(img, cv2.COLOR_BGR2Lab)
    lAB_tar = cv2.cvtColor(target, cv2.COLOR_BGR2Lab)

    #finds mean and standard deviation for each color channel across the entire image
    (mean, std) = cv2.meanStdDev(lAB_img)
    (mean_tar, std_tar) = cv2.meanStdDev(lAB_tar)

    #iterates over image implementing formula to map color normalized pixels to target image
    for y in range(512):
        for x in range(512):
            lAB_tar[x, y, 0] = (lAB_img[x, y, 0] - mean[0]) / std[0] * std_tar[0] + mean_tar[0]
            lAB_tar[x, y, 1] = (lAB_img[x, y, 1] - mean[1]) / std[1] * std_tar[1] + mean_tar[1]
            lAB_tar[x, y, 2] = (lAB_img[x, y, 2] - mean[2]) / std[2] * std_tar[2] + mean_tar[2]
    mapped = cv2.cvtColor(lAB_tar, cv2.COLOR_Lab2BGR)
    return mapped

我的主管告诉我可以尝试使用矩阵一次性应用函数以提高运行时间,但我不太确定如何执行。


快速查看一下numpy文档不会有坏处。使用它提供的向量化操作。 - Dan Mašek
如果有帮助的话,看起来已经有人做过类似的事情,你可以尝试适应一下(虽然它看起来非常定制化,是为他自己的实验室染色包设计的),源代码在这里:https://github.com/Peter554/StainTools/blob/master/staintools/reinhard_color_normalizer.py - G. Anderson
2个回答

3

原图和目标图像:

enter image description here enter image description here

使用Reinhard方法进行颜色转移的结果,仅需5毫秒

enter image description here enter image description here


我更喜欢在numpy向量化操作中实现该公式,而不是python循环

# implementing the formula
#(Io - mo)/so*st + mt  = Io * (st/so) + mt - mo*(st/so)
ratio = (std_tar/std_ori).reshape(-1)
offset = (mean_tar - mean_ori*std_tar/std_ori).reshape(-1)
lab_tar = cv2.convertScaleAbs(lab_ori*ratio + offset)

这里是代码:
# 2019/02/19 by knight-金
# https://dev59.com/1rHma4cB1Zd3GeqPTPBS#54757659

import numpy as np
import cv2

def reinhard(target, original):
    # cvtColor: COLOR_BGR2Lab
    lab_tar = cv2.cvtColor(target, cv2.COLOR_BGR2Lab)
    lab_ori = cv2.cvtColor(original, cv2.COLOR_BGR2Lab)

    # meanStdDev: calculate mean and stadard deviation
    mean_tar, std_tar = cv2.meanStdDev(lab_tar)
    mean_ori, std_ori = cv2.meanStdDev(lab_ori)

    # implementing the formula
    #(Io - mo)/so*st + mt  = Io * (st/so) + mt - mo*(st/so)
    ratio = (std_tar/std_ori).reshape(-1)
    offset = (mean_tar - mean_ori*std_tar/std_ori).reshape(-1)
    lab_tar = cv2.convertScaleAbs(lab_ori*ratio + offset)

    # convert back
    mapped = cv2.cvtColor(lab_tar, cv2.COLOR_Lab2BGR)
    return mapped

if __name__ == "__main__":
    ori = cv2.imread("ori.png")
    tar = cv2.imread("tar.png")

    mapped = reinhard(tar, ori)
    cv2.imwrite("mapped.png", mapped)

    mapped_inv = reinhard(ori, tar)
    cv2.imwrite("mapped_inv.png", mapped)

1

在查看numpy文档后,我成功解决了问题。我只需要使用正确的数组访问方式替换嵌套的for循环即可。这样迭代所有300张图像不到一分钟。

lAB_tar[:,:,0] = (lAB_img[:,:,0] - mean[0])/std[0] * std_tar[0] + mean_tar[0]
lAB_tar[:,:,1] = (lAB_img[:,:,1] - mean[1])/std[1] * std_tar[1] + mean_tar[1]
lAB_tar[:,:,2] = (lAB_img[:,:,2] - mean[2])/std[2] * std_tar[2] + mean_tar[2]

1
同时进行它们,你可以在20秒内完成所有任务。恭喜你解决了问题并分享出来。 - Mark Setchell
干得好 :) 现在我有一种感觉,你可以将其简化为一行,试试看... - Dan Mašek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接