Numba加速计算:坐标索引(CPU + 多线程)


在这里插入图片描述

使用Numba索引图像中每个像素值的最小最大坐标(CPU + 多线程)

输入图像时耗
(1)多线程(每一个像素值遍历一次图像)ZYX=(2182, 431, 548)80.41秒
(2)Numba-CPU(每一个像素值遍历一次图像)ZYX=(2182, 431, 548)81.68秒
(3)Numba-CPU(只遍历一次图像并匹配像素值)ZYX=(2182, 431, 548)1.56秒

一、测试样本 —— 创建样本mask,具有 N 个唯一像素值,每个值有 M 个坐标。

import numpy as np

def create_mask(shape, num_values):
    """创建一个 uint16 类型的 mask,并为每个像素值设置多个坐标点。
	    :param shape: mask 数组的形状 (z, y, x)
	    :param num_values: 不同的像素值数量
	    :return: mask 数组
    """
    # 初始化 mask 数组
    mask = np.zeros(shape, dtype=np.uint16)

    # 遍历每个像素值并设置多个坐标点
    for value in range(1, num_values + 1):
        # 生成随机坐标
        num_coords = int(shape[0] * shape[1] * shape[2] / num_values * 0.1)  # 每个像素值设置N个坐标点
        for _ in range(num_coords):
            z = np.random.randint(0, shape[0])
            y = np.random.randint(0, shape[1])
            x = np.random.randint(0, shape[2])
            mask[z, y, x] = value

    return mask


if __name__ == '__main__':
	shape = (1024, 1024, 1024)  # 设置 mask 的大小
	num_values = 619  # 不同的像素值数量
	mask = create_mask(shape, num_values)
	
	print(f"mask 数组的形状: {mask.shape}")
	print(f"mask 数组的唯一像素值: {np.unique(mask)}")

二、加速方法

(1)多线程加速 —— 每次提取一个像素值,然后遍历图像,匹配并判断其与初始化坐标的关系。

import numpy as np
import tifffile
import concurrent.futures
import pandas as pd
import time


def calculate_coordinates(target_value, array):
    """在满足条件的N个坐标中,提取最小x/y/z值。
	    备注1:x/y/z可以不在同一个坐标中。
	    备注2:由于需要使用np.argwhere进行坐标索引,故有时耗问题。
    """
    start_time = time.time()
    mask = (array == target_value)  # 创建布尔掩码
    coordinates = np.argwhere(mask)  # 查找True值的坐标

    min_coords = np.min(coordinates, axis=0)  # 获取所有列中每一列的最小值  # 坐标: [z, y, x]
    max_coords = np.max(coordinates, axis=0)  # 获取所有列中每一列的最大值  # 坐标: [z, y, x]
    value = [target_value, *min_coords[::-1], *max_coords[::-1]]  # 坐标: [gray, min_x, min_y, min_z, max_x, max_y, max_z]
    print(f"gray={target_value}", f"最小坐标={min_coords[::-1]}", f"最大坐标:{max_coords[::-1]}", time.time()-start_time)
    return value


def main(image_path, output_path):
    start_time = time.time()

    image = tifffile.imread(image_path)  # 读取图像
    unique_values = np.unique(image)  # 获取图像中的像素唯一值
    sorted_unique_values = np.sort(unique_values)  # 排序
    box3DMap = []

    flag = 1  # 是否加速
    if flag == 1:
        with concurrent.futures.ThreadPoolExecutor(max_workers=os.cpu_count()) as executor:
            futures = [executor.submit(calculate_coordinates, value, image) for value in sorted_unique_values[1:]]

            for future in concurrent.futures.as_completed(futures):
                result = future.result()
                box3DMap.append(result)
    else:  # 不加速版本
        for ii in range(1, len(sorted_unique_values)):
            start_time = time.time()
            
            target_value = ii  # 指定要查找的灰度值
            coordinates = np.argwhere(image == target_value)  # 提取满足条件的坐标
            min_coords = np.min(coordinates, axis=0)  # 获取所有列中每一列的最小值  # 坐标: [z, y, x]
            max_coords = np.max(coordinates, axis=0)  # 获取所有列中每一列的最大值  # 坐标: [z, y, x]
            temp = [ii, *min_coords, *max_coords]
            print(f"gray={ii}", f"最小坐标={min_coords}", f"最大坐标:{max_coords}")
            
            print(f"共用时:{time.time() - start_time}")
            box3DMap.append(temp)

    df = pd.DataFrame(box3DMap, columns=["Gray", "Min_X", "Min_Y", "Min_Z", "Max_X", "Max_Y", "Max_Z"])
    df.to_excel(output_path, index=False, engine='openpyxl')
    print(f'Data saved to={output_path}')
    print(f"计算共用时={time.time()-start_time} 秒")


if __name__ == '__main__':
    image_path = r"F:\annotation.tif"
    output_path = r"F:\output.xlsx"
    main(image_path, output_path)

(2)Numba-CPU加速 —— 每次提取一个像素值,然后遍历图像,匹配并判断其与初始化坐标的关系。

import numpy as np
import tifffile
import time
from numba import njit


@njit
def calculate_coordinates(num_values, array):
    """计算每个唯一值的最小和最大坐标。
	    num_values: 排序后的唯一像素值数组
	    array: 输入图像
	    返回值:每个像素值的最小和最大坐标
    """
    shape = array.shape
    num_values_size = num_values.size
    box3DMap = np.empty((num_values_size, 7), dtype=np.int64)  # 初始化结果数组

    for idx in range(num_values_size):
        target_value = num_values[idx]
        min_coords = np.array([shape[0], shape[1], shape[2]], dtype=np.int64)  # 初始化为图像的最大坐标值
        max_coords = np.array([-1, -1, -1], dtype=np.int64)  # 初始化为图像的最小坐标值

        for i in range(shape[0]):
            for j in range(shape[1]):
                for k in range(shape[2]):
                    if array[i, j, k] == target_value:
                        min_coords[0] = min(min_coords[0], i)
                        min_coords[1] = min(min_coords[1], j)
                        min_coords[2] = min(min_coords[2], k)
                        max_coords[0] = max(max_coords[0], i)
                        max_coords[1] = max(max_coords[1], j)
                        max_coords[2] = max(max_coords[2], k)
        coors = [target_value, min_coords[2], min_coords[1], min_coords[0], max_coords[2], max_coords[1], max_coords[0]]
        print(coors)
        box3DMap[idx] = coors
        
    return box3DMap


def main(image_path, output_path):
    image = tifffile.imread(image_path)  # 读取图像
    unique_values = np.unique(image)  # 获取图像中的像素唯一值
    sorted_unique_values = np.sort(unique_values[unique_values != 0])  # 排序并排除0
    print(f"唯一像素值数量(排除0):{len(sorted_unique_values)}")

    # Numba 加速
    start_time = time.time()
    box3DMap = calculate_coordinates(sorted_unique_values, image)
	print(f"计算共用时:{time.time() - start_time}")
    
    # 保存到EXCEL中
    import pandas as pd
    df = pd.DataFrame(box3DMap, columns=["Gray", "Min_X", "Min_Y", "Min_Z", "Max_X", "Max_Y", "Max_Z"])
    df.to_excel(output_path, index=False, engine='openpyxl')
    print(f'Data saved to={output_path}')


if __name__ == '__main__':
    image_path = r'F:\annotation.tif'
    output_path = r'F:\output.xls'
    main(image_path, output_path)

(3)Numba-CPU加速 —— 只遍历一次图像,匹配并判断其与初始化坐标的关系。

import numpy as np
import tifffile
import time
from numba import njit


@njit
def calculate_coordinates(num_values, array):
    """计算每个唯一值的最小和最大坐标。
	    num_values: 排序后的唯一像素值数组(排除0)
	    array: 输入图像
	    返回值:每个像素值的最小和最大坐标
    """
    shape = array.shape
    num_values_size = num_values.size
    box3DMap = np.empty((num_values_size, 7), dtype=np.int64)

    # 随机初始化:每个像素值的最小和最大坐标
    min_coords = np.empty((num_values_size, 3), dtype=np.int64)
    max_coords = np.empty((num_values_size, 3), dtype=np.int64)

    # 用最大坐标初始化 min_coords,用最小坐标初始化 max_coords
    for i in range(num_values_size):
        min_coords[i, 0] = shape[0]
        min_coords[i, 1] = shape[1]
        min_coords[i, 2] = shape[2]
        max_coords[i, 0] = -1
        max_coords[i, 1] = -1
        max_coords[i, 2] = -1

    # 为每个唯一值设置索引
    value_to_index = np.zeros(100000, dtype=np.int64)  # 假设最大像素值为255
    for i in range(num_values_size):
        value_to_index[num_values[i]] = i

    print("开始遍历图像,计算坐标...")
    # 遍历图像的所有坐标点
    for i in range(shape[0]):
        for j in range(shape[1]):
            for k in range(shape[2]):
                pixel_value = array[i, j, k]
                if pixel_value == 0:
                    continue
                idx = value_to_index[pixel_value]
                if idx >= 0:
                    min_coords[idx, 0] = min(min_coords[idx, 0], i)
                    min_coords[idx, 1] = min(min_coords[idx, 1], j)
                    min_coords[idx, 2] = min(min_coords[idx, 2], k)
                    max_coords[idx, 0] = max(max_coords[idx, 0], i)
                    max_coords[idx, 1] = max(max_coords[idx, 1], j)
                    max_coords[idx, 2] = max(max_coords[idx, 2], k)

    # 组合最小和最大坐标
    for idx in range(num_values_size):
        coors = [num_values[idx],
                 min_coords[idx, 2], min_coords[idx, 1], min_coords[idx, 0],
                 max_coords[idx, 2], max_coords[idx, 1], max_coords[idx, 0]]
        box3DMap[idx] = coors

    return box3DMap


def main(image_path, output_path):
    print(f"开始读取图像:{image_path}")
    image = tifffile.imread(image_path)  # 读取图像
    unique_values = np.unique(image)  # 获取图像中的像素唯一值
    sorted_unique_values = np.sort(unique_values[unique_values != 0])  # 排序并排除0
    print(f"图像读取完成。唯一像素值数量(排除0):{len(sorted_unique_values)}")

    # Numba加速
    start_time = time.time()
    print(f"开始计算坐标...")
    box3DMap = calculate_coordinates(sorted_unique_values, image)
    print(f"坐标计算完成,结果条目数:{box3DMap.shape[0]}")
    print(f"计算共用时:{time.time() - start_time}")

    # 保存到EXCEL中
    import pandas as pd
    df = pd.DataFrame(box3DMap, columns=["Gray", "Min_X", "Min_Y", "Min_Z", "Max_X", "Max_Y", "Max_Z"])
    df.to_excel(output_path, index=False, engine='openpyxl')
    print(f"数据已保存到={output_path}")


if __name__ == '__main__':
    # 示例路径,修改为实际文件路径
    image_path = r'F:\annotation.tif'
    output_path = r'F:\output.xls'
    main(image_path, output_path)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖墩会武术

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值