Leptonica - 应用 otsu 阈值后无法写入图像

Posted

技术标签:

【中文标题】Leptonica - 应用 otsu 阈值后无法写入图像【英文标题】:Leptonica - unable to write image after applying otsu threshold 【发布时间】:2014-09-30 16:09:03 【问题描述】:

我正在尝试使用 leptonica 处理后将图像保存为 jpeg。我正在使用带有 ctypes 的 python,我的代码是:

import ctypes

leptlib = "liblept.so"
leptonica = ctypes.cdll.LoadLibrary(leptlib)

filename = "IMAG0724.jpg"
img = leptonica.pixRead(filename)

leptonica.pixConvertTo8.argtypes = [ctypes.c_void_p, 
                                    ctypes.c_int32]

pix_image = leptonica.pixConvertTo8(img, False)

leptonica.pixOtsuAdaptiveThreshold.argtypes = [ctypes.c_void_p, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_float]

otsu = leptonica.pixOtsuAdaptiveThreshold(pix_image,20,20,0,0,0.1)

leptonica.pixWriteJpeg.argtypes = [ctypes.c_void_p, 
                                   ctypes.c_void_p, 
                                   ctypes.c_int32, 
                                   ctypes.c_int32]

leptonica.pixWriteJpeg("otsu-lept", otsu, 75, 0)

此代码产生错误:

pixWriteJpeg 中的错误:pix 未定义

我相信这是因为我需要在应用 otsu 之后但在编写新图像之前做一些事情。我错过了什么?

编辑-

我现在根据 leptonica 文档 http://tpgit.github.io/Leptonica/binarize_8c.html 修改了以下内容:

leptonica.pixOtsuAdaptiveThreshold.argtypes = [ctypes.c_void_p, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_float,
                                               ctypes.c_void_p]

leptonica.pixOtsuAdaptiveThreshold(pix_image,20,20,0,0,0.1, img)

leptonica.pixWriteJpeg("otsu-lept", img, 75, 0)

现在出现一个新错误:

支持的最大图像尺寸为 65500 像素 pixWriteStreamJpeg 中的错误:内部 jpeg 错误 pixWriteJpeg 中的错误:pix 未写入流

我的图像分辨率为 1552 x 2592,并且 leptonica.pixWriteJpeg 在 otsu 函数行被注释掉时可以工作,所以看起来问题仍然在于 otsu 函数返回的图像。

**** 编辑 2 ****

当我使用 leptonica 检查输出 img 时,它告诉我宽度是一个很大的数字,每次运行该函数时似乎都会发生变化(例如 149996048),并且高度在与输入图像相同的值下保持正确。看起来 otsu 函数出于某种原因将图像宽度更改为这么大的值。

编辑 3

下面的jsbueno为我提供了这个问题的解决方案,我将在这里分享。问题是因为我将图像直接传递给函数,而实际上需要将指针的指针传递给该函数,然后该函数才起作用。最终工作代码如下:

import ctypes

leptlib = "liblept.so"
leptonica = ctypes.cdll.LoadLibrary(leptlib)

filename = "IMAG0724.jpg"
img = leptonica.pixRead(filename)

leptonica.pixConvertTo8.argtypes = [ctypes.c_void_p, 
                                    ctypes.c_int32
                                    ]

pix_image = leptonica.pixConvertTo8(img, False)
w = leptonica.pixGetWidth(img)
h = leptonica.pixGetHeight(img)
pixa_out = leptonica.pixCreate(w,h,8)
pixa = ctypes.c_void_p(pixa_out)
leptonica.pixOtsuAdaptiveThreshold.argtypes = [ctypes.c_void_p, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_int32, 
                                               ctypes.c_float,
                                               ctypes.c_void_p,
                                               ctypes.c_void_p
                                               ]

otsu = leptonica.pixOtsuAdaptiveThreshold(pix_image,
                                          20,
                                          20,
                                          0,
                                          0,
                                          0.1,
                                          None,
                                          ctypes.addressof(pixa)
                                          )


leptonica.pixWritePng("otsu-lept", pixa, 8)

【问题讨论】:

你能检查otsu的类型吗?看起来它是带有错误代码的 int,而不是 pix 指针。 是的,类型是 int。感谢这一点,我刚刚意识到为什么会这样,并通过将 ctypes.c_void_p 添加到 otsu 的 argtypes 列表的末尾并提供输出图像来反映这一点,并修改了函数以反映这一点。它没有返回不同的错误。请参阅上述问题的修正。 pixOtsuAdaptiveThreshold() 是否返回通过或错误?看起来你的参数比需要的少。 函数返回一个零,根据文档是成功的。根据文档,最后两个参数是可选的返回。我已经包含一个返回“基于阈值数组的阈值输入像素”,因为我希望输出是应用阈值的原始图像。 【参考方案1】:

我发现你做错了什么 - 如果你检查函数签名,它在末尾可选 2 个互斥参数 - 其中一个是返回系数的数组,而不是图像 - 非常最后一个参数是一个空的图像,用于绘制算法的应用程序。

此外,最后两个参数是 leptonica PIX 对象的“指向指针的指针” - 您可以创建一个包含 ctypes.c_void_p 对象的 Python 变量,并通过 ctypes.addressof 到 leptonica 函数。

outsu函数的文档如下:

/*------------------------------------------------------------------*
 *                 Adaptive Otsu-based thresholding                 *
 *------------------------------------------------------------------*/
/*!
 *  pixOtsuAdaptiveThreshold()
 *
 *      Input:  pixs (8 bpp)
 *              sx, sy (desired tile dimensions; actual size may vary)
 *              smoothx, smoothy (half-width of convolution kernel applied to
 *                                threshold array: use 0 for no smoothing)
 *              scorefract (fraction of the max Otsu score; typ. 0.1;
 *                          use 0.0 for standard Otsu)
 *              &pixth (<optional return> array of threshold values
 *                      found for each tile)
 *              &pixd (<optional return> thresholded input pixs, based on
 *                     the threshold array)
 *      Return: 0 if OK, 1 on error
 *

PS。在研究这一点时,我设法为 leptonica 1.7.1 构建了 python-leptonica 绑定 - 一旦我清理了我为到达那里所做的烂摊子,我应该再发布一个版本。

对于任何能够运行 python-leptonica 的人,就像现在一样(仅限于 leptonica 1.6.0 没有黑客攻击)- 使用此功能的代码将是这样的:

import leptonica
import ctypes
img = leptonica.functions.pixRead("t1.png")
imggray = leptonica.functions.pixConvertRGBToGrayMinMax(img, 1)
img = leptonica.functions.pixRead("t1.png")
output = leptonica.functions.pixCreate(imggray.w, imggray.h, 1)
a = ctypes.c_voidp()
leptonica.functions.pixOtsuAdaptiveThreshold(imggray, 20, 20, 0, 0, .1, None, ctypes.addressof(a))
output  = leptonica.PIX(from_address=a)
leptonica.functions.pixWritePng("t3.png", c, 1)

【讨论】:

非常感谢!这很棒,但我还不能让它工作。我在 argtpyes 中添加了额外的 c_void_p,并且我还在函数中添加了 ctypes.cvoid_p(0)。新的错误消息说“模块对象没有属性 cvoid_p”。我猜这是上面的输入错误,所以我将其更改为 ctypes.c_void_p(0),现在错误显示为 'ctypes.ArgumentError: argument 6: : wrong type'。知道现在出了什么问题吗?再次感谢。 好的,我刚刚修复了错误,我错误地删除了 c_float。现在再次出现原始错误 - “最大支持的图像尺寸为 65500 像素 pixWriteStreamJpeg 中的错误:内部 jpeg 错误 pixWriteJpeg 中的错误:pix 未写入流”。已尝试切换最后两个变量的位置以防万一但出现相同的结果。

以上是关于Leptonica - 应用 otsu 阈值后无法写入图像的主要内容,如果未能解决你的问题,请参考以下文章

图像分割基于灰狼算法优化Otsu图像实现多阈值分割matlab源码

大津法---OTSU算法

OpenCV计算机视觉 —— 图像的阈值处理与自适应阈值Otsu

OTSU 获取最佳阈值,及opencv二值化

灰度图像的自动阈值分割(Otsu 法)(转载)

OpenCV OTSU 阈值删除文本