【问题标题】:How to perform logical operation and logical indexing using VIPS in Python?如何在 Python 中使用 VIPS 执行逻辑操作和逻辑索引?
【发布时间】:2015-10-18 06:36:03
【问题描述】:

我有以下使用 Python 和 OpenCV 的代码。简而言之,我有一堆以不同焦深拍摄的图像。这些代码在所有焦点深度 (z) 中挑选出每个 (x,y) 位置具有最大高斯响应的拉普拉斯算子的像素,从而创建焦点堆叠图像。函数get_fmap 创建一个二维数组,其中每个像素将包含具有最大对数响应的焦平面的编号。在以下代码中,被注释掉的行是我当前的 VIPS 实现。它们在函数定义中看起来不兼容,因为它只是部分解决方案。

# from gi.repository import Vips

def get_log_kernel(siz, std):
    x = y = np.linspace(-siz, siz, 2*siz+1)
    x, y = np.meshgrid(x, y)
    arg = -(x**2 + y**2) / (2*std**2)
    h = np.exp(arg)
    h[h < sys.float_info.epsilon * h.max()] = 0
    h = h/h.sum() if h.sum() != 0 else h
    h1 = h*(x**2 + y**2 - 2*std**2) / (std**4)
    return h1 - h1.mean()

def get_fmap(img):    # img is a 3-d numpy array.
    log_response = np.zeros_like(img[:, :, 0], dtype='single')
    fmap = np.zeros_like(img[:, :, 0], dtype='uint8')
    log_kernel = get_log_kernel(11, 2)
    # kernel = get_log_kernel(11, 2)
    # kernel = [list(row) for row in kernel]
    # kernel = Vips.Image.new_from_array(kernel)
    # img = Vips.new_from_file("testimg.tif")
    for ii in range(img.shape[2]):           
        # img_filtered = img.conv(kernel)
        img_filtered = cv2.filter2D(img[:, :, ii].astype('single'), -1, log_kernel)
        index = img_filtered > log_response
        log_response[index] = img_filtered[index]
        fmap[index] = ii
    return fmap

然后fmap 将用于从不同焦平面中挑选像素以创建焦点堆叠图像

这是在一个非常大的图像上完成的,我觉得 VIPS 在这方面可能比 OpenCV 做得更好。然而,官方文档提供的关于其 Python 绑定的信息相当少。根据我在互联网上可以找到的信息,我只能使图像卷积工作(在我的情况下,它比 OpenCV 快一个数量级。)。我想知道如何在 VIPS 中实现这一点,尤其是这些行?

log_response = np.zeros_like(img[:, :, 0], dtype = 'single')

index = img_filtered > log_response

log_response[index] = im_filtered[index]

fmap[index] = ii

【问题讨论】:

    标签: python opencv numpy image-processing vips


    【解决方案1】:

    log_responsefmap 在问题代码中被初始化为 3D 数组,而问题文本指出输出 fmap 是 2D 数组。所以,我假设log_responsefmap 将被初始化为形状与每个图像相同的二维数组。因此,编辑将是 -

    log_response = np.zeros_like(img[:,:,0], dtype='single')
    fmap = np.zeros_like(img[:,:,0], dtype='uint8')
    

    现在,回到问题的主题,您正在对每个图像一个接一个地执行 2D 过滤,并获得所有堆叠图像中过滤输出的最大索引。如果您不知道cv2.filter2D 的文档,它也可以用于多维数组,为我们提供一个多维数组作为输出。然后,获取所有图像的最大索引就像.argmax(2) 一样简单。因此,实现必须非常高效,而且很简单 -

    fmap = cv2.filter2D(img,-1,log_kernel).argmax(2)
    

    【讨论】:

    • 谢谢。在提供这个最小的例子时我犯了一个错误。我已经编辑了我的帖子。很高兴知道cv2.filter2d 有这样的用法。我一定会试一试的。但是,VIPS 默认使用多个内核,而对于 OpenCV,我必须自己实现它。并且向 OpenCV 添加多处理也会增加一些开销。我认为multiprocessing 模块加上 OpenCV 的性能不会接近 VIPS。我用 11x11 内核在 3000x4000 图像上测试了高斯滤波。 VIPS在0.0025秒内完成了任务。 OpenCV 耗时 0.1334 秒。
    • @user3667217 那么,您已经在 VIPS 中实现了该解决方案吗?如此,在这里分享作为答案?好吧,直到今天我才从你那里听说过,所以谢谢!让我知道对此的任何其他想法!那个 VIPS,我肯定看起来很有趣。
    • 我在VIPS中还没有一个完整的解决方案,所以我才询问如何在VIPS中进行逻辑运算和索引。显然 VIPS 正在积极开发(目前已经是第 8 版),但鲜为人知。我设法做的与现有的 python 手册相差无几。我只得到它来执行高斯滤波并使用我自己的内核进行滤波。我还在探索它。我将在我的 OP 中输入代码。 VIPS 真的很棒。例如,它读取图像花费了 0.0011 秒,而 openCV 花费了 0.043 秒。它的速度快得令人难以置信。绝对值得一试!
    • @user3667217 你正在与我分享疯狂的东西!我的意思是我已经使用 OpenCV 很长时间了。所以,从你所说的来看,这个 VIPS 似乎是一个新事物,绝对令人兴奋!我一定会在某个时候试一试。但在这个问题上,我认为我无法帮助 VIPS。值得等待看到有人对此有更多见解。
    • 你试过带 TBB 的 OpenCV 吗?我上面的结果来自单线程 OpenCV。 OpenCV + TBB 是一种选择,但我找不到很多例子。我仍然不认为它们加起来会胜过 VIPS,尽管如此。但当然,VIPS 在其执行的功能数量方面受到更多限制。
    【解决方案2】:

    在咨询了Python VIPS manual 和一些反复试验后,我想出了自己的答案。我的 numpy 和 OpenCV 实现可以像这样翻译成 VIPS:

    import pyvips
    
    img = []
    for ii in range(num_z_levels):
        img.append(pyvips.Image.new_from_file("testimg_z" + str(ii) + ".tif")
    
    def get_fmap(img)
        log_kernel = get_log_kernel(11,2)  # get_log_kernel is my own function, which generates a 2-d numpy array.
        log_kernel = [list(row) for row in log_kernel]  # pyvips.Image.new_from_array takes 1-d list array.
        log_kernel = pyvips.Image.new_from_array(log_kernel)  # Turn the kernel into Vips array so it can be used by Vips.
        log_response = img[0].conv(log_kernel)
    
        for ii in range(len(img)):
            img_filtered = img[ii+1].conv(log_kernel)
            log_response = (img_filtered > log_response).ifthenelse(img_filtered, log_response)
            fmap = (img_filtered > log_response).ifthenelse(ii+1, 0)
    

    通过ifthenelse方法实现逻辑索引:

    result_img = (test_condition).ifthenelse(value_if_true, value_if_false)
    

    语法相当灵活。测试条件可以是两个相同大小的图像之间的比较,也可以是一个图像和一个值之间的比较,例如img1 &gt; img2img &gt; 5。同样,value_if_true 可以是单个值或 Vips 图像。

    【讨论】:

    • 我是 vips 维护者。你的代码看起来不错。看到vipslogmat功能了吗?它可能对你有用。它创建一个日志掩码vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/…,从 Python 中用作(例如)Vips.Image.logmat(2, 0.1)。它还将生成近似整数掩码和可分离掩码,这可以提供有用的加速。如果您愿意,可以使用字符串 "float" 代替 Vips.Precision.FLOAT
    • (第 2 部分)您需要 index 图像吗?我认为您可以将其重写为fmap = (img_filtered &gt; log_response).ifthenelse(ii + 1, 0),我可能遗漏了一些东西。如果您有任何可以分享的基准,我会很好奇速度和内存使用与 opencv 相比如何。
    • 尝试:x = Vips.Image.logmat(2, 0.1); x.matrixprint() 查看 vips 制作的日志垫。添加precision = "float" 以获得浮动版本。
    • 哦,你是维护者!谢谢你的评论。我上周才开始使用 VIPS,但仍然无法找到所有东西的位置。我不太明白关于日志内核的等式。和我的一样吗? (我刚刚在我的问题中发布了它)。我不需要index 图片,所以你的建议应该更好。
    • 图像轴被限制为 2^31,像素到它的平方,所以 2^62。我经常在笔记本电脑上处理 200,000 x 200,000 像素的幻灯片图像。它也可以在 32 位机器上处理这些巨大的图像,尽管这些天已经不太重要了,谢天谢地。
    猜你喜欢
    • 2017-05-29
    • 1970-01-01
    • 2021-03-08
    • 2013-11-04
    • 1970-01-01
    • 1970-01-01
    • 2016-08-05
    • 1970-01-01
    • 2021-12-06
    相关资源
    最近更新 更多