当前位置:首页>python>从砾石照片到颗粒参数:Python 自动提取粒径、圆度和形状分布(含代码)

从砾石照片到颗粒参数:Python 自动提取粒径、圆度和形状分布(含代码)

  • 2026-08-18 23:11:42
从砾石照片到颗粒参数:Python 自动提取粒径、圆度和形状分布(含代码)
案例代码见文末,感谢您关注PFC小姐姐,麻烦您多多对推文点赞、收藏及转发,并衷心希望您多多指教🙏,帮助PFC小姐姐进步提升。

引言

在岩土工程、颗粒材料、碎石土、堆石料、河床沉积物以及离散元建模中,颗粒的粒径和形状是非常基础的参数。粒径分布会影响材料的孔隙结构、级配特征、渗透性和密实程度;颗粒形状则会影响颗粒之间的接触方式、滚动阻力、嵌挤效应和剪切强度。传统的粒径分析通常依赖筛分试验,而颗粒形状分析往往需要人工测量或借助图像软件逐个标注。当样本数量较少时,人工处理还能接受;但如果一张照片中包含大量天然砾石颗粒,手动统计等效粒径、长短轴比、圆度、实心度等参数就会非常耗时,而且不同图片之间的处理标准也很难保持一致。本文以一张天然砾石堆积照片为例,演示如何用 Python 对颗粒图像进行自动后处理。代码通过灰度增强、边缘识别、分水岭分割和形态学分析,从原始照片中提取可见颗粒的二维表观参数,并进一步绘制粒径分布图、形状参数分布图和粒径—形状关系图。需要注意的是,这类图像分析结果属于二维表观统计。照片中的颗粒存在遮挡、堆叠、阴影和透视变形,因此识别得到的等效粒径和形状参数并不完全等同于筛分试验中的真实三维粒径。如果照片中没有比例尺,粒径单位只能用像素表示;如果需要换算为实际毫米,需要在拍摄时放入标尺,并在代码中设置像素与实际长度之间的换算关系。

1、砾石图像分割与颗粒识别检查

下图展示的是砾石图像处理的第一步结果。左侧为原始砾石照片,中间为分割后的颗粒标签图,右侧为在原图上叠加颗粒边界后的识别检查图。这张图最重要的作用是检查分割效果。图像分析不是直接从照片中得到可靠参数,而是先要判断颗粒是否被合理识别出来。如果一个颗粒被切成很多碎块,或者多个相邻颗粒被识别成一个整体,后续计算得到的粒径和形状参数都会出现偏差。与白色颗粒铺在蓝色背景上的照片不同,天然砾石图像中颗粒颜色接近、边界不完全清晰,并且存在明显的接触、遮挡和阴影。因此,代码没有采用简单的颜色阈值,而是通过局部对比度增强、边缘检测和分水岭分割来识别颗粒边界。在实际使用时,应优先查看图1。如果红色边界大致贴合颗粒轮廓,说明后续统计结果具有一定参考价值;如果边界明显过碎或粘连严重,则需要调整分水岭参数、最小面积阈值和边缘检测参数。

2、粒径分布直方图与累计粒径曲线

下图展示的是从识别颗粒中提取得到的粒径分布结果。横坐标为等效粒径,单位为像素;左侧纵坐标为颗粒数量,右侧纵坐标为累计百分比。蓝色直方图表示不同粒径区间内的颗粒数量,橙色曲线表示累计粒径分布。这里的等效粒径是基于颗粒二维投影面积计算得到的圆等效直径。也就是说,代码先计算每个识别颗粒在图像中的投影面积,再换算成具有相同面积的圆的直径。这个指标适合用于二维图像中的表观粒径统计。图中自动标出了 D10、D30和 D60。其中,D10表示累计百分比达到 10% 时对应的粒径,D30和 D60分别对应累计百分比为 30% 和 60% 时的粒径。需要强调的是,图像识别得到的粒径分布通常是表观粒径分布。代码中可以选择按颗粒数量统计,也可以按投影面积加权统计。若要与土工筛分试验结果严格对比,还需要考虑三维颗粒质量、厚度、遮挡和比例尺换算等因素。

    3、砾石颗粒形状参数分布

    下图展示的是识别颗粒的形状参数分布,包括长短轴比、圆度、圆形度和实心度。每个子图对应一个形状指标,直方图表示该指标在全部识别颗粒中的分布情况,黑色虚线表示平均值。长短轴比用于描述颗粒的拉长程度。数值越接近 1,说明颗粒越接近等轴形;数值越大,说明颗粒越细长、越扁平或越不规则。对于天然砾石来说,不同颗粒的形态差异通常比较明显,因此长短轴比分布可以反映样本中是否存在较多片状或条状颗粒。圆度和圆形度用于描述颗粒轮廓接近圆形的程度。数值越接近 1,颗粒越接近圆形;数值较低时,说明颗粒轮廓可能更不规则,也可能受到遮挡、阴影或分割边界误差的影响。实心度表示颗粒投影面积与其凸包面积之间的比例。实心度较高说明颗粒轮廓较饱满;实心度较低则可能说明颗粒边界凹凸明显,或者该颗粒存在遮挡、破碎边缘和不规则轮廓。

    4、粒径与形状参数关系图

    下图展示的是粒径与形状参数之间的关系。三个子图分别对应等效粒径与长短轴比、圆度、实心度之间的关系。每个散点代表一个识别到的砾石颗粒,点的颜色表示该颗粒的投影面积大小。这张图可以用来观察不同尺寸颗粒是否具有不同的形状特征。例如,大颗粒是否更容易呈现较高的长短轴比,小颗粒是否更接近圆形,某些粒径范围内是否存在实心度较低的异常颗粒。这些异常点可能是真实的不规则颗粒,也可能来自遮挡、粘连或分割误差。相比单独的粒径分布图和形状分布图,粒径—形状关系图可以同时展示颗粒尺寸和颗粒形态之间的联系。对于天然砾石、碎石土和堆石料来说,材料性质并不只由粒径决定,颗粒是否圆滑、是否细长、轮廓是否规则,同样会影响颗粒体系的力学行为。在离散元建模中,这类结果也有参考价值。通过图像统计获得粒径分布和形状参数后,可以进一步用于 PFC 颗粒半径分布设计、clump 形状构建或真实颗粒形态的近似模拟。实际使用时,建议结合图1的分割检查结果,对异常点和明显误分割颗粒进行筛选,再用于后续参数分析。

    具体Python如下:

    import osimport numpy as npimport pandas as pdimport cv2import matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltfrom scipy import ndimage as ndifrom skimage import morphology, measure, segmentation, feature, colorfrom skimage.filters import rank# 当前目录下的图片名称# 改成你的砾石图片文件名即可input_image = "example.jpg"out_dir = "gravel_image_analysis_results"os.makedirs(out_dir, exist_ok=True)np.random.seed(2026)plt.rcParams["font.family"] = "Times New Roman"plt.rcParams["axes.unicode_minus"] = Falseplt.rcParams["figure.dpi"] = 160plt.rcParams["savefig.dpi"] = 500# 如果有比例尺,在这里填写每个像素对应多少 mm# 例如:MM_PER_PIXEL = 0.25# 如果不知道,就保持 None,输出单位为 pxMM_PER_PIXEL = None# 颗粒过滤参数MIN_AREA_PX = 80MAX_AREA_PX = 50000MIN_EQ_DIAM_PX = 6MAX_EQ_DIAM_PX = 260# 如果过滤后颗粒太少,代码会自动放宽筛选MIN_VALID_PARTICLES = 30# 去掉贴边颗粒,避免边缘半颗粒影响统计BORDER_MARGIN = 4# Canny 边缘参数# 如果分割太碎:适当增大 CANNY_LOW / CANNY_HIGH# 如果颗粒粘连严重:适当减小 CANNY_LOW / CANNY_HIGHCANNY_LOW = 45CANNY_HIGH = 115# 距离变换局部峰值参数# 如果颗粒被切太碎:增大 PEAK_MIN_DISTANCE# 如果相邻颗粒分不开:减小 PEAK_MIN_DISTANCEPEAK_MIN_DISTANCE = 8PEAK_THRESHOLD_ABS = 3.0# 梯度分水岭参数GRADIENT_DISK_SIZE = 2EDGE_DILATE_RADIUS = 1WATERSHED_COMPACTNESS = 0.01# 过暗区域剔除阈值# 如果很多深色砾石漏掉,可以调低,例如 20 或 25OBJECT_MIN_GRAY = 25# 粒径累计曲线模式# "area":按颗粒投影面积加权,更接近二维面积级配# "number":按颗粒数量加权DISTRIBUTION_MODE = "area"# 读取图片if not os.path.exists(input_image):    raise FileNotFoundError(        f"没有找到图片:{input_image}\n"        f"请确认图片在当前目录下,或者把 input_image 改成完整路径。"    )img_bgr = cv2.imread(input_image)if img_bgr is None:    raise ValueError("图片读取失败,请检查文件格式。")img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)H, W = img_rgb.shape[:2]# 3. 天然灰色砾石图像分割#    CLAHE增强 + Canny边缘 + 距离变换 + 分水岭gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)# 局部对比度增强,突出颗粒边界clahe = cv2.createCLAHE(    clipLimit=2.5,    tileGridSize=(88))gray_eq = clahe.apply(gray)# 中值滤波,降低纹理噪声gray_blur = cv2.medianBlur(gray_eq, 5)# Canny 边缘检测edges = cv2.Canny(gray_blur, CANNY_LOW, CANNY_HIGH)# 边界膨胀,形成颗粒之间的分隔线edge_bool = morphology.binary_dilation(    edges > 0,    morphology.disk(EDGE_DILATE_RADIUS))# 候选颗粒内部区域:非边缘 + 非极暗区域interior = (~edge_bool) & (gray_blur > OBJECT_MIN_GRAY)interior = morphology.remove_small_objects(interior, min_size=25)interior = morphology.binary_closing(interior, morphology.disk(1))# 距离变换,用局部极大值作为颗粒中心标记distance = ndi.distance_transform_edt(interior)coords = feature.peak_local_max(    distance,    min_distance=PEAK_MIN_DISTANCE,    threshold_abs=PEAK_THRESHOLD_ABS,    labels=interior,    exclude_border=BORDER_MARGIN)# 如果标记点太少,自动放宽一次if len(coords) < 20:    coords = feature.peak_local_max(        distance,        min_distance=max(4, PEAK_MIN_DISTANCE - 3),        threshold_abs=max(1.5, PEAK_THRESHOLD_ABS - 1.5),        labels=interior,        exclude_border=BORDER_MARGIN    )markers = np.zeros_like(distance, dtype=np.int32)for i, (r, c) in enumerate(coords, start=1):    markers[r, c] = imarkers = ndi.label(markers > 0)[0]if markers.max() == 0:    raise RuntimeError(        "没有生成有效分水岭标记。建议降低 CANNY_LOW/CANNY_HIGH,"        "或降低 PEAK_THRESHOLD_ABS。"    )# 梯度图作为分水岭地形gradient = rank.gradient(    gray_blur,    morphology.disk(GRADIENT_DISK_SIZE))# 当前图基本全是砾石,mask 只剔除极暗区域和边界区域mask_clean = gray_blur > OBJECT_MIN_GRAYmask_clean[:BORDER_MARGIN, :] = Falsemask_clean[-BORDER_MARGIN:, :] = Falsemask_clean[:, :BORDER_MARGIN] = Falsemask_clean[:, -BORDER_MARGIN:] = Falselabels_ws = segmentation.watershed(    gradient,    markers,    mask=mask_clean,    compactness=WATERSHED_COMPACTNESS)# 去掉贴边区域labels_ws = segmentation.clear_border(labels_ws)# 提取单颗粒形状与粒径参数def px_to_unit(x):    if MM_PER_PIXEL is None:        return x    return x * MM_PER_PIXELdef px2_to_unit2(x):    if MM_PER_PIXEL is None:        return x    return x * MM_PER_PIXEL * MM_PER_PIXELdef get_unit_name():    return "px" if MM_PER_PIXEL is None else "mm"def get_equivalent_diameter(region):    try:        return region.equivalent_diameter_area    except AttributeError:        return region.equivalent_diameterunit_name = get_unit_name()candidate_records = []regions = measure.regionprops(labels_ws)for region in regions:    source_label = region.label    area = region.area    minr, minc, maxr, maxc = region.bbox    # 去掉明显贴边半颗粒    if (        minr <= BORDER_MARGIN        or minc <= BORDER_MARGIN        or maxr >= H - BORDER_MARGIN        or maxc >= W - BORDER_MARGIN    ):        continue    if area <= 10:        continue    equiv_d = get_equivalent_diameter(region)    perimeter = region.perimeter    major_axis = region.major_axis_length    minor_axis = region.minor_axis_length    solidity = region.solidity    eccentricity = region.eccentricity    extent = region.extent    orientation = np.degrees(region.orientation)    if perimeter <= 0 or minor_axis <= 0 or major_axis <= 0:        continue    circularity = 4 * np.pi * area / (perimeter ** 2 + 1e-12)    aspect_ratio = major_axis / (minor_axis + 1e-12)    roundness = 4 * area / (np.pi * major_axis ** 2 + 1e-12)    coords_region = region.coords    pts = coords_region[:, ::-1].astype(np.float32)    rect = cv2.minAreaRect(pts)    rect_w, rect_h = rect[1]    rect_long = max(rect_w, rect_h)    rect_short = min(rect_w, rect_h)    if rect_short <= 0:        continue    rect_aspect = rect_long / rect_short    cy, cx = region.centroid    candidate_records.append({        "source_label": source_label,        "centroid_x_px": cx,        "centroid_y_px": cy,        "area_px2": area,        f"area_{unit_name}2": px2_to_unit2(area),        "perimeter_px": perimeter,        f"perimeter_{unit_name}": px_to_unit(perimeter),        f"equivalent_diameter_{unit_name}": px_to_unit(equiv_d),        f"major_axis_{unit_name}": px_to_unit(major_axis),        f"minor_axis_{unit_name}": px_to_unit(minor_axis),        f"rect_long_axis_{unit_name}": px_to_unit(rect_long),        f"rect_short_axis_{unit_name}": px_to_unit(rect_short),        "aspect_ratio": aspect_ratio,        "rect_aspect_ratio": rect_aspect,        "circularity": circularity,        "roundness": roundness,        "solidity": solidity,        "extent": extent,        "eccentricity": eccentricity,        "orientation_deg": orientation    })raw_df = pd.DataFrame(candidate_records)if len(raw_df) == 0:    raise RuntimeError(        "分水岭完成后没有得到候选颗粒。建议降低 OBJECT_MIN_GRAY,"        "或减小 CANNY_LOW/CANNY_HIGH。"    )diam_col = f"equivalent_diameter_{unit_name}"# 先按正常标准筛选valid_mask = (    (raw_df["area_px2"] >= MIN_AREA_PX)    & (raw_df["area_px2"] <= MAX_AREA_PX)    & (raw_df[diam_col] >= MIN_EQ_DIAM_PX)    & (raw_df[diam_col] <= MAX_EQ_DIAM_PX)    & (raw_df["aspect_ratio"] <= 12.0)    & (raw_df["circularity"] > 0.03)    & (raw_df["solidity"] > 0.35))metrics_df = raw_df[valid_mask].copy()# 如果筛选后颗粒太少,自动放宽筛选if len(metrics_df) < MIN_VALID_PARTICLES:    print("提示:正常筛选后颗粒数量偏少,已自动放宽筛选条件。")    valid_mask_relaxed = (        (raw_df["area_px2"] >= 30)        & (raw_df["area_px2"] <= max(MAX_AREA_PX, raw_df["area_px2"].quantile(0.995)))        & (raw_df[diam_col] >= 4)        & (raw_df[diam_col] <= max(MAX_EQ_DIAM_PX, raw_df[diam_col].quantile(0.995)))        & (raw_df["aspect_ratio"] <= 20.0)        & (raw_df["solidity"] > 0.15)    )    metrics_df = raw_df[valid_mask_relaxed].copy()# 重新编号metrics_df = metrics_df.reset_index(drop=True)metrics_df.insert(0"label", np.arange(1len(metrics_df) + 1))if len(metrics_df) == 0:    raise RuntimeError(        "仍然没有识别到有效砾石颗粒。建议检查图片名称是否正确,"        "或进一步降低 MIN_AREA_PX、OBJECT_MIN_GRAY。"    )# 生成有效标签图valid_label_mask = np.zeros_like(labels_ws, dtype=np.int32)for _, row in metrics_df.iterrows():    source_label = int(row["source_label"])    new_label = int(row["label"])    valid_label_mask[labels_ws == source_label] = new_labelmetrics_path = os.path.join(out_dir, "gravel_particle_metrics.csv")metrics_df.to_csv(metrics_path, index=False, encoding="utf-8-sig")raw_path = os.path.join(out_dir, "gravel_raw_candidate_regions.csv")raw_df.to_csv(raw_path, index=False, encoding="utf-8-sig")# 5. 计算粒径分布与 D10/D30/D50/D60diameters = metrics_df[diam_col].valuesarea_weights = metrics_df["area_px2"].valuesorder = np.argsort(diameters)diam_sorted = diameters[order]if DISTRIBUTION_MODE == "area":    weights_sorted = area_weights[order]    cum_percent = np.cumsum(weights_sorted) / np.sum(weights_sorted) * 100.0    cum_label = "Cumulative area percent (%)"else:    cum_percent = np.arange(1len(diam_sorted) + 1) / len(diam_sorted) * 100.0    cum_label = "Cumulative number percent (%)"def interpolate_D_value(d_sorted, cum_percent_arr, target_percent):    d_sorted = np.asarray(d_sorted)    cum_percent_arr = np.asarray(cum_percent_arr)    if target_percent < cum_percent_arr.min() or target_percent > cum_percent_arr.max():        return np.nan    log_d = np.log10(d_sorted + 1e-12)    log_D = np.interp(target_percent, cum_percent_arr, log_d)    return float(10 ** log_D)D10 = interpolate_D_value(diam_sorted, cum_percent, 10)D30 = interpolate_D_value(diam_sorted, cum_percent, 30)D50 = interpolate_D_value(diam_sorted, cum_percent, 50)D60 = interpolate_D_value(diam_sorted, cum_percent, 60)Cu = D60 / D10 if D10 > 0 else np.nanCc = D30 ** 2 / (D10 * D60) if D10 > 0 and D60 > 0 else np.nansummary_df = pd.DataFrame([{    "distribution_mode": DISTRIBUTION_MODE,    "particle_count"len(metrics_df),    f"D10_{unit_name}": D10,    f"D30_{unit_name}": D30,    f"D50_{unit_name}": D50,    f"D60_{unit_name}": D60,    "Cu_D60_over_D10": Cu,    "Cc_D30_square_over_D10_D60": Cc,    f"mean_diameter_{unit_name}": np.mean(diameters),    f"median_diameter_{unit_name}": np.median(diameters),    f"std_diameter_{unit_name}": np.std(diameters),    "mean_aspect_ratio": metrics_df["aspect_ratio"].mean(),    "mean_rect_aspect_ratio": metrics_df["rect_aspect_ratio"].mean(),    "mean_circularity": metrics_df["circularity"].mean(),    "mean_solidity": metrics_df["solidity"].mean(),    "mean_roundness": metrics_df["roundness"].mean()}])summary_path = os.path.join(out_dir, "gravel_size_shape_summary.csv")summary_df.to_csv(summary_path, index=False, encoding="utf-8-sig")# 图1:分割结果检查图fig, axes = plt.subplots(13, figsize=(156))axes[0].imshow(img_rgb)axes[0].set_title("Original gravel image", fontsize=13)axes[0].axis("off")label_vis = color.label2rgb(    valid_label_mask,    image=img_rgb,    bg_label=0,    alpha=0.45)axes[1].imshow(label_vis)axes[1].set_title("Separated particle labels", fontsize=13)axes[1].axis("off")overlay = img_rgb.copy()boundaries = segmentation.find_boundaries(valid_label_mask, mode="outer")overlay[boundaries] = [25500]axes[2].imshow(overlay)axes[2].set_title(f"Detected particles: n = {len(metrics_df)}", fontsize=13)axes[2].axis("off")plt.tight_layout()fig1_path = os.path.join(out_dir, "fig1_segmentation_check.png")plt.savefig(fig1_path, bbox_inches="tight", facecolor="white")plt.close(fig)# 图2:粒径分布直方图 + 累计曲线fig, ax1 = plt.subplots(figsize=(8.86.4))bins = np.logspace(    np.log10(max(diameters.min() * 0.851e-3)),    np.log10(diameters.max() * 1.15),    28)ax1.hist(    diameters,    bins=bins,    color="#A6CEE3",    edgecolor="white",    alpha=0.90)ax1.set_xscale("log")ax1.set_xlabel(f"Equivalent particle diameter ({unit_name})", fontsize=12)ax1.set_ylabel("Particle count", fontsize=12)ax1.grid(True, which="both", alpha=0.18)ax2 = ax1.twinx()ax2.plot(    diam_sorted,    cum_percent,    color="#D55E00",    linewidth=2.4,    label=cum_label)ax2.set_ylabel(cum_label, fontsize=12)ax2.set_ylim(0100)for D_val, P_val, name in [    (D10, 10"D10"),    (D30, 30"D30"),    (D60, 60"D60")]:    ax1.axvline(        D_val,        color="gray",        linestyle="--",        linewidth=1.0,        alpha=0.65    )    ax2.scatter(        D_val,        P_val,        s=58,        color="red",        edgecolors="white",        linewidths=0.8,        zorder=5    )    ax2.text(        D_val * 1.05,        P_val + 3.0,        f"{name}={D_val:.2f}",        fontsize=10,        ha="left",        va="bottom"    )txt = (    f"n = {len(metrics_df)}\n"    f"D10 = {D10:.2f}{unit_name}\n"    f"D30 = {D30:.2f}{unit_name}\n"    f"D60 = {D60:.2f}{unit_name}\n"    f"Cu = {Cu:.2f}\n"    f"Cc = {Cc:.2f}")ax1.text(    0.04,    0.96,    txt,    transform=ax1.transAxes,    ha="left",    va="top",    fontsize=10,    bbox=dict(        boxstyle="round,pad=0.35",        facecolor="white",        edgecolor="#999999",        alpha=0.92    ))ax1.set_title("Particle size distribution from gravel image", fontsize=15)ax1.spines["top"].set_visible(False)ax2.spines["top"].set_visible(False)line_handles, line_labels = ax2.get_legend_handles_labels()ax2.legend(    line_handles,    line_labels,    loc="lower right",    frameon=True,    fontsize=10)plt.tight_layout()fig2_path = os.path.join(out_dir, "fig2_particle_size_distribution.png")plt.savefig(fig2_path, bbox_inches="tight", facecolor="white")plt.close(fig)# 图3:形状参数分布图fig, axes = plt.subplots(22, figsize=(11.28.4))axes = axes.ravel()shape_items = [    ("aspect_ratio""Aspect ratio""#4C72B0"),    ("circularity""Circularity""#55A868"),    ("roundness""Roundness""#C44E52"),    ("solidity""Solidity""#8172B2")]for ax, (col, title, color_val) in zip(axes, shape_items):    vals = metrics_df[col].replace([np.inf, -np.inf], np.nan).dropna().values    ax.hist(        vals,        bins=24,        color=color_val,        edgecolor="white",        alpha=0.88    )    mean_val = np.mean(vals)    ax.axvline(        mean_val,        color="black",        linestyle="--",        linewidth=1.3,        label=f"Mean = {mean_val:.2f}"    )    ax.set_title(title, fontsize=13)    ax.set_xlabel(title, fontsize=11)    ax.set_ylabel("Count", fontsize=11)    ax.grid(alpha=0.18)    ax.legend(frameon=True, fontsize=9)    ax.spines["top"].set_visible(False)    ax.spines["right"].set_visible(False)fig.suptitle("Shape parameter distributions of detected gravel particles", fontsize=16, y=0.98)plt.tight_layout(rect=[0010.96])fig3_path = os.path.join(out_dir, "fig3_shape_parameter_distribution.png")plt.savefig(fig3_path, bbox_inches="tight", facecolor="white")plt.close(fig)# 图4:粒径-形状参数关系图fig, axes = plt.subplots(13, figsize=(14.84.8))scatter_items = [    ("aspect_ratio""Aspect ratio"),    ("circularity""Circularity"),    ("solidity""Solidity")]sc = Nonefor ax, (col, ylabel) in zip(axes, scatter_items):    sc = ax.scatter(        metrics_df[diam_col],        metrics_df[col],        c=metrics_df["area_px2"],        s=28,        cmap="viridis",        alpha=0.78,        edgecolors="none"    )    ax.set_xscale("log")    ax.set_xlabel(f"Equivalent diameter ({unit_name})", fontsize=11)    ax.set_ylabel(ylabel, fontsize=11)    ax.set_title(f"Size vs. {ylabel}", fontsize=13)    ax.grid(True, which="both", alpha=0.18)    ax.spines["top"].set_visible(False)    ax.spines["right"].set_visible(False)fig.suptitle(    "Relationship between particle size and shape parameters",    fontsize=16,    y=1.02)plt.tight_layout(rect=[000.880.95])cax = fig.add_axes([0.910.180.0180.62])cbar = fig.colorbar(sc, cax=cax)cbar.set_label("Projected area (px²)", fontsize=11)fig4_path = os.path.join(out_dir, "fig4_size_shape_scatter.png")plt.savefig(fig4_path, bbox_inches="tight", facecolor="white")plt.close(fig)

    运行后先看 fig1_segmentation_check.png。如果轮廓太碎,把 PEAK_MIN_DISTANCE = 8 调到 10 或 12;如果很多颗粒粘在一起,把它调到 5 或 6!!

    特别声明:

    以上代码与文案均为网上资料整合而成,仅供广大同行们参考学习,如有侵权请联系删除。

    如有其他需要,欢迎关注我的咸鱼号:pfc小姐姐

    最新文章

    随机文章

    基本 文件 流程 错误 SQL 调试
    1. 请求信息 : 2026-08-21 20:56:30 HTTP/2.0 GET : https://f.mffb.com.cn/a/508751.html
    2. 运行时间 : 0.260661s [ 吞吐率:3.84req/s ] 内存消耗:4,545.05kb 文件加载:140
    3. 缓存信息 : 0 reads,0 writes
    4. 会话信息 : SESSION_ID=6af6c097ca1a9e89c4002529a588ebf2
    1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
    2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
    3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
    4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
    5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
    6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
    7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
    8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
    9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
    10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
    11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
    12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
    13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
    14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
    15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
    16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
    17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
    18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
    19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
    20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
    21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
    22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
    23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
    24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
    25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
    26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
    27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
    28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
    29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
    30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
    31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
    32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
    33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
    34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
    35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
    36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
    37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
    38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
    39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
    40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
    41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
    42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
    43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
    44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
    45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
    46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
    47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
    48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
    49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
    50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
    51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
    52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
    53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
    54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
    55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
    56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
    57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
    58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
    59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
    60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
    61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
    62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
    63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
    64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
    65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
    66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
    67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
    68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
    69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
    70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
    71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
    72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
    73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
    74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
    75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
    76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
    77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
    78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
    79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
    80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
    81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
    82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
    83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
    84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
    85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
    86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
    87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
    88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
    89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
    90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
    91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
    92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
    93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
    94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
    95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
    96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
    97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
    98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
    99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
    100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
    101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
    102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
    103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
    104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
    105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
    106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
    107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
    108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
    109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
    110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
    111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
    112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
    113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
    114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
    115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
    116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
    117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
    118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
    119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
    120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
    121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
    122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
    123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
    124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
    125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
    126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
    127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
    128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
    129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
    130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
    131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
    132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
    133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
    134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
    135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
    136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
    137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
    138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
    139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
    140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
    1. CONNECT:[ UseTime:0.001038s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
    2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001466s ]
    3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.021975s ]
    4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.003319s ]
    5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001396s ]
    6. SELECT * FROM `set` [ RunTime:0.003658s ]
    7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001566s ]
    8. SELECT * FROM `article` WHERE `id` = 508751 LIMIT 1 [ RunTime:0.005543s ]
    9. UPDATE `article` SET `lasttime` = 1787316990 WHERE `id` = 508751 [ RunTime:0.002490s ]
    10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.004118s ]
    11. SELECT * FROM `article` WHERE `id` < 508751 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.004443s ]
    12. SELECT * FROM `article` WHERE `id` > 508751 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.009026s ]
    13. SELECT * FROM `article` WHERE `id` < 508751 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.016462s ]
    14. SELECT * FROM `article` WHERE `id` < 508751 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.025602s ]
    15. SELECT * FROM `article` WHERE `id` < 508751 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.004105s ]
    0.263020s