Ben*_*ber 2 python opencv image-processing
我想删除蒙版周围的所有空白,使图像的边界与圆圈对齐,如下所示:
我编写了一个脚本来执行此操作,方法是搜索每一列和每一行,直到出现值大于 0 的像素。从左到右、从右到左、从上到下、从下到上搜索可以得到蒙版边界,从而可以裁剪原始图像。这是代码:
ROWS, COLS, _ = img.shape
BORDER_RIGHT = (0,0)
BORDER_LEFT = (0,0)
right_found = False
left_found = False
# find borders of blank space for removal.
# left and right border
print('Searching for Right and Left corners')
for col in tqdm(range(COLS), position=0, leave=True):
for row in range(ROWS):
if left_found and right_found:
break
# searching from left to right
if not left_found and N.sum(img[row][col]) > 0:
BORDER_LEFT = (row, col)
left_found = True
# searching from right to left
if not right_found and N.sum(img[row][-col]) > 0:
BORDER_RIGHT = (row, img.shape[1] + (-col))
right_found = True
BORDER_TOP = (0,0)
BORDER_BOTTOM = (0,0)
top_found = False
bottom_found = False
# top and bottom borders
print('Searching for Top and Bottom corners')
for row in tqdm(range(ROWS), position=0, leave=True):
for col in range(COLS):
if top_found and bottom_found:
break
# searching top to bottom
if not top_found and N.sum(img[row][col]) > 0:
BORDER_TOP = (row, col)
top_found = True
# searching bottom to top
if not bottom_found and N.sum(img[-row][col]) > 0:
BORDER_BOTTOM = (img.shape[0] + (-row), col)
bottom_found = True
# crop left and right borders
new_img = img[:,BORDER_LEFT[1]: BORDER_RIGHT[1] ,:]
# crop top and bottom borders
new_img = new_img[BORDER_TOP[0] : BORDER_BOTTOM[0],:,:]
Run Code Online (Sandbox Code Playgroud)
我想知道是否有更有效的方法来做到这一点。对于较大的图像,这可能非常耗时,尤其是当掩模相对于原始图像形状相对较小时。谢谢!
假设图像中只有这个对象,有两种方法可以做到这一点:
numpy.where
来查找所有非零位置,然后在相应的行和列位置上使用numpy.min
和来给出边界矩形。numpy.max
numpy.where
cv2.findContours
。这应该会产生单个轮廓,因此一旦获得这些点,您就可以将其cv2.boundingRect
返回矩形的左上角,然后返回其范围的宽度和高度。如果只有一个对象并且效率很高,则第一种方法将起作用。如果有多个对象,则第二个方法将起作用,但您必须知道感兴趣的对象位于哪个轮廓,然后您只需索引到 的输出cv2.findContours
并将其通过管道cv2.boundingRect
即可获得感兴趣的对象的矩形尺寸。
然而,要点是,这些方法中的任何一种都比您提出的手动循环每行和列并计算总和的方法更有效。
这组步骤对于两种方法来说都是通用的。总之,我们读入图像,然后将其转换为灰度,然后转换为阈值。我无法访问您的原始图像,因此我从 Stack Overflow 读取它并对其进行裁剪,以便轴不显示。这也适用于第二种方法。
这是我拍摄的图像的重建图像。
首先,我将直接从互联网读取图像,并导入完成工作所需的相关包:
import skimage.io as io
import numpy as np
import cv2
img = io.imread('https://i.stack.imgur.com/dj1a8.png')
Run Code Online (Sandbox Code Playgroud)
值得庆幸的是,Scikit image 有一个直接从互联网读取图像的方法:skimage.io.imread
。
之后,我将把图像转换为灰度,然后对其进行阈值处理:
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
im = img_gray > 40
Run Code Online (Sandbox Code Playgroud)
我使用 OpenCVcv2.cvtColor
将图像从彩色转换为灰度。之后,我对图像进行阈值设置,将任何高于 40 的强度设置为True
,将其他所有设置设置为False
。我通过反复试验选择了 40 的阈值,直到得到一个看起来是圆形的掩模。看看这张图片我们得到:
如上所示,numpy.where
在阈值图像上使用,然后使用numpy.min
并numpy.max
找到适当的左上角和右下角并裁剪图像:
(r, c) = np.where(im == 1)
min_row, min_col = np.min(r), np.min(c)
max_row, max_col = np.max(r), np.max(c)
im_crop = img[min_row:max_row+1, min_col:max_col+1]
Run Code Online (Sandbox Code Playgroud)
numpy.where
对于二维数组将返回非零的行和列位置的元组。如果我们找到最小行和列位置,它对应于边界矩形的左上角。同样,最大行和列位置对应于边界矩形的右下角。好处是它以矢量化方式工作,这意味着它可以在一次扫描中对整个 NumPy 数组进行操作numpy.min
。numpy.max
上面使用了这个逻辑,然后我们索引到原始彩色图像以裁剪出包含感兴趣对象的行和列的范围。 im_crop
包含该结果。请注意,当我们索引时,最大行和列需要添加 1,因为末端索引的切片是互斥的,因此添加 1 可确保我们包含矩形右下角的像素位置。
因此我们得到:
我们将用来cv2.findContours
查找图像中所有对象的所有轮廓点。由于只有一个对象,因此只能生成一个轮廓,因此我们使用该轮廓进行管道传输以cv2.boundingRect
查找对象边界矩形的左上角,并结合其宽度和高度来裁剪图像。
cnt, _ = cv2.findContours(im.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
x, y, w, h = cv2.boundingRect(cnt[0])
im_crop = img[y:y+h, x:x+w]
Run Code Online (Sandbox Code Playgroud)
请注意,我们必须将阈值图像转换为无符号 8 位整数,因为这是函数所期望的类型。此外,我们使用cv2.RETR_EXTERNAL
因为我们只想检索我们在图像中看到的任何对象的外周坐标。我们还用于cv2.CHAIN_APPROX_NONE
返回对象上每个可能的轮廓点。这cnt
是在图像中找到的轮廓列表。该列表的大小应该仅为 1,因此我们直接对其进行索引并将其通过管道传输到cv2.boundingRect
. 然后,我们使用矩形的左上角,结合其宽度和高度来裁剪对象。
因此我们得到:
这是从开始到结束的完整代码清单。我在下面留下了评论来描述方法 #1 和 #2 是什么。目前,方法 #2 已被注释掉,但您可以通过简单地注释和取消注释相关代码来决定要使用哪一种。
import skimage.io as io
import cv2
import numpy as np
img = io.imread('https://i.stack.imgur.com/dj1a8.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
im = img_gray > 40
# Method #1
(r, c) = np.where(im == 1)
min_row, min_col = np.min(r), np.min(c)
max_row, max_col = np.max(r), np.max(c)
im_crop = img[min_row:max_row+1, min_col:max_col+1]
# Method #2
#cnt, _ = cv2.findContours(im.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
#x, y, w, h = cv2.boundingRect(cnt[0])
#im_crop = img[y:y+h, x:x+w]
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
819 次 |
最近记录: |