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

问题描述

我有一个dicom图片,但该图片已填充。我有代码从图像中删除填充,以便仅保留扫描,但是我必须使用ImageJ打开图像,并手动找到图像开始和结束的x和y轴的最小值和最大值。扫描的灰度值范围为-3000 to 2000。填充区域的值为0。有没有一种方法可以手动找到这些最小值和最大值?

原始图片

enter image description here

所需图像:

enter image description here

解决方法

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

基本思想是创建一个不是背景值的像素的蒙版图像。然后,它使用SimpleITK的LabelShapeStatisticsImageFilter查找该蒙版图像中非零像素的边界框。

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类的更多信息,请参见文档:https://simpleitk.org/doxygen/latest/html/classitk_1_1simple_1_1LabelShapeStatisticsImageFilter.html#details

,

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

输入:

enter image description here

import cv2
import numpy as np

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

# threshold on gray color (161,161,161)
lower = (161,161)
upper = (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()

阈值图片:

enter image description here

裁剪结果:

enter image description here

,

无需借助像SITK或CV这样复杂(且导入速度大/慢)的东西进行复杂的图像分析-您只需使用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. 查找所有值与第一行或第一列相同的所有行和列。 如果需要,可以将其替换为所有行/列,其值== 0
  2. 从(1)所在的行/列的索引不是都相同(即== False)
  3. 获取第一个索引和最后一个索引都不相同的索引
  4. 使用行/列的第一个和最后一个索引来获取数组的相应切片(请注意,您需要在最后一个索引上加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)

original

same_cols = np.all(a == a[0,C0:C1]

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

行53421 列数43470

snipped