问题描述
我正在寻找二进制图像(0 或 1 的二维 numpy 数组)中连接的 blob 的坐标。
skimage 库提供了一种非常快速的方法来标记数组中的 blob(我从类似的 SO 帖子中找到了它)。但是我想要一个 blob 坐标列表,而不是一个带标签的数组。我有一个从标记图像中提取坐标的解决方案。但它很慢。远比初始标记慢。
最小可重现示例:
import timeit
from skimage import measure
import numpy as np
binary_image = np.array([
[0,1,1],[0,0],])
print(f"\n\n2d array of type: {type(binary_image)}:")
print(binary_image)
labels = measure.label(binary_image)
print(f"\n\n2d array with connected blobs labelled of type {type(labels)}:")
print(labels)
def extract_blobs_from_labelled_array(labelled_array):
# The goal is to obtain lists of the coordinates
# Of each distinct blob.
blobs = []
label = 1
while True:
indices_of_label = np.where(labelled_array==label)
if not indices_of_label[0].size > 0:
break
else:
blob =list(zip(*indices_of_label))
label+=1
blobs.append(blob)
if __name__ == "__main__":
print("\n\nBeginning extract_blobs_from_labelled_array timing\n")
print("Time taken:")
print(
timeit.timeit(
'extract_blobs_from_labelled_array(labels)',globals=globals(),number=1
)
)
print("\n\n")
输出:
2d array of type: <class 'numpy.ndarray'>:
[[0 1 0 0 1 1 0 1 1 0 0 1]
[0 1 0 1 1 1 0 1 1 1 0 1]
[0 0 0 0 0 0 0 1 1 1 0 0]
[0 1 1 1 1 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 1 1 1 0 0]
[0 0 1 0 0 0 0 0 0 0 0 0]
[0 1 0 0 1 1 0 1 1 0 0 1]
[0 0 0 0 0 0 0 1 1 1 0 0]
[0 1 1 1 1 0 0 0 0 1 0 0]]
2d array with connected blobs labelled of type <class 'numpy.ndarray'>:
[[ 0 1 0 0 2 2 0 3 3 0 0 4]
[ 0 1 0 2 2 2 0 3 3 3 0 4]
[ 0 0 0 0 0 0 0 3 3 3 0 0]
[ 0 5 5 5 5 0 0 0 0 3 0 0]
[ 0 0 0 0 0 0 0 3 3 3 0 0]
[ 0 0 6 0 0 0 0 0 0 0 0 0]
[ 0 6 0 0 7 7 0 8 8 0 0 9]
[ 0 0 0 0 0 0 0 8 8 8 0 0]
[ 0 10 10 10 10 0 0 0 0 8 0 0]]
Beginning extract_blobs_from_labelled_array timing
Time taken:
9.346099977847189e-05
9e-05 很小,但此示例图像也很小。实际上,我正在处理非常高分辨率的图像,该函数大约需要 10 分钟。
有没有更快的方法来做到这一点?
旁注:我只使用 list(zip())
来尝试将 numpy 坐标转换为我习惯的东西(我只使用 Python 很少使用 numpy)。我应该跳过这个而只使用坐标按原样索引吗?这会加快速度吗?
解决方法
慢的部分代码在这里:
const [messageRecord,setMessageRecord] = useState([]);
const { userInfo } = useContext(UserInfoContext);
const { projectInfo } = props.route.params;
const sendMessage = (msg) => {
const payload = {
action: "message",msg,projectId: projectInfo.PK,senderId: userInfo.userId,senderName: userInfo.userGivenName,type: "update",};
socket.send(JSON.stringify(payload));
};
const startConnection = () => {
const payload = {
action: "message",msg: "",senderId: "",senderName: "",type: "create",};
socket.send(JSON.stringify(payload));
};
const getMessageRecord = () => {
const payload = {
action: "message",type: "get",};
socket.send(JSON.stringify(payload));
};
socket.onmessage = (e) => {
const server_message = e.data;
const sortedRecord = sortMessageByDate(JSON.parse(server_message).message);
setMessageRecord(sortedRecord);
};
useEffect(() => {
startConnection();
getMessageRecord();
},[]);
首先,一个完整的旁白:当您知道将迭代的元素数量时,您应该避免使用 while True:
indices_of_label = np.where(labelled_array==label)
if not indices_of_label[0].size > 0:
break
else:
blob =list(zip(*indices_of_label))
label+=1
blobs.append(blob)
。这是难以找到的无限循环错误的秘诀。
相反,您应该使用:
while True
然后您可以忽略 for label in range(np.max(labels)):
。
第二个问题确实是您使用的是 if ...: break
,与 NumPy 函数相比,这很慢。在这里,您可以使用 list(zip(*))
获得大致相同的结果,这将为您提供形状为 np.transpose(indices_of_label)
的二维数组,即 (n_coords,n_dim)
。
但最大的问题是表达式 (n_coords,2)
。这将为每个标签检查一次图像的每个像素。 (实际上两次,因为然后您运行 labelled_array == label
,这需要另一次传递。)这是很多不必要的工作,因为可以在一次传递中找到坐标。
scikit-image 函数 skimage.measure.regionprops
可以为您做到这一点。 np.where()
遍历图像一次并返回一个列表,每个标签包含一个 regionprops
对象。该对象有一个 RegionProps
属性,其中包含 blob 中每个像素的坐标。所以,这是您的代码,经过修改以使用该功能:
.coords