如何找到图像的填充大小?

How to find the padding size of an image?

我有一张 dicom 图片,但图片被填充了。我有代码从图像中删除填充,以便只剩下扫描,但我必须使用 ImageJ 打开图像,并手动查找图像开始和结束位置的 x 轴和 y 轴的最小值和最大值。扫描的灰度值范围为-3000 to 2000。填充区域的值为 0。有没有一种无需手动查找这些最小值和最大值的方法?

原图:

想要的图片:

下面是使用 SimpleITK 裁剪背景的 Python 脚本。

基本思想是它创建一个由不是背景值的像素组成的蒙版图像。然后它使用 SimpleITK 的 LabelShapeStatisticsImageFilter 找到该蒙版图像中 non-zero 像素的边界框。

import SimpleITK as sitk

img = sitk.ReadImage("padded-image.png")

# Grey background in this example
bg_value = 161

# Create a mask image that is just non-background pixels
fg_mask = (img != bg_value)

# Compute shape statistics on the mask
lsif = sitk.LabelShapeStatisticsImageFilter()
lsif.Execute(fg_mask)

# Get the bounds of the mask.
# Bounds are given as [Xstart, Ystart, Xwidth, Ywidth]
bounds = lsif.GetBoundingBox(1)
print(bounds)

Xmin_crop = bounds[0]
Ymin_crop = bounds[1]

Xmax_crop = img.GetWidth() - (bounds[0]+bounds[2])
Ymax_crop = img.GetHeight() - (bounds[1]+bounds[3])

# Crop parameters are how much to crop off each side
cropped_img = sitk.Crop(img, [Xmin_crop, Ymin_crop], [Xmax_crop, Ymax_crop])

sitk.Show(cropped_img)

sitk.WriteImage(cropped_img, "cropped-image.png")

因为我用的是你的8位PNG图片,所以背景值设置为161。如果你用你原来的16位DICOM CT,你会使用背景值0。SimpleITK可以读取DICOM,与许多其他图像格式。

有关 LabelShapeStatisticsImageFilter class 的更多信息,这里是文档:https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1LabelShapeStatisticsImageFilter.html#details

这是 Python/OpenCV 中的另一种方法,使用颜色阈值和轮廓来查找边界框。

输入:

import cv2
import numpy as np

# read image
img = cv2.imread('scan.png')

# threshold on gray color (161,161,161)
lower = (161,161,161)
upper = (161,161,161)
thresh = cv2.inRange(img, lower, upper)

# invert threshold image so border is black and center box is white
thresh = 255 - thresh

# get external contours (presumably just one) 
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
cntr = contours[0]
x,y,w,h = cv2.boundingRect(cntr)

# crop to bounding rectangle
crop = img[y:y+h, x:x+w]

# save cropped image
cv2.imwrite('scan_thresh.png',thresh)
cv2.imwrite('scan_crop.png',crop)

cv2.imshow("THRESH", thresh)
cv2.imshow("CROP", crop)
cv2.waitKey(0)
cv2.destroyAllWindows()

阈值图像:

裁剪结果:

无需求助于像 SITK 或 CV 这样复杂的东西(并且 large/slow 需要导入)和复杂的图像分析 - 只需使用 numpy 即可轻松完成。

恕我直言,这会更快更可靠:

# if a is your image:

same_cols = np.all(a == a[0, :], axis=0)
same_cols_index = np.where(same_cols==False)[0]
C0,C1 = same_cols_index[0], same_cols_index[-1] + 1

same_rows = np.all(a == a[:, 0], axis=1)
same_rows_index = np.where(same_rows==False)[0]
R0,R1 = same_rows_index[0], same_rows_index[-1] + 1

print('rows', R0, R1)
print('cols', C0, C1)

a_snipped = a[R0:R1, C0:C1]

这里的逻辑是

  1. 查找所有值与第一行或第一列相同的所有行和列。 如果需要,您可以将其全部 rows/cols 替换为值 == 0
  2. 从 (1) 中获取 rows/columns 的索引 not 完全相同(即 == False)
  3. 获取不完全相同的第一个和最后一个索引
  4. 使用row/column第一个和最后一个索引获取数组的相应切片(注意您需要将最后一个索引加1以将其包含在切片中)

例子

# make a sample image

a = np.zeros((512,512), dtype=np.int32)
r0, r1 = 53, 421
c0, c1 = 43, 470
rnd = np.random.randint(-3000, 2000, (r1-r0, c1-c0))
a[r0:r1, c0:c1] = rnd
plt.imshow(a, cmap='gray', vmin=-50, vmax=50)

same_cols = np.all(a == a[0, :], axis=0)
same_cols_index = np.where(same_cols==False)[0]
C0,C1 = same_cols_index[0], same_cols_index[-1] + 1

same_rows = np.all(a == a[:, 0], axis=1)
same_rows_index = np.where(same_rows==False)[0]
R0,R1 = same_rows_index[0], same_rows_index[-1] + 1

print('rows', R0, R1)
print('cols', C0, C1)

a_snipped = a[R0:R1, C0:C1]

plt.imshow(a_snipped, cmap='gray', vmin=-3000, vmax=2000)

行 53 421 列 43 470