فرض کنید نسخهٔ تازهٔ پیش‌پردازش، همان اندازهٔ تصویر قبلی را تحویل می‌دهد، اما برچسبِ بعضی خانه‌ها عوض شده است. بررسیِ شکلِ آرایه این تغییر را نمی‌بیند. در این درس، تغییر اندازهٔ نزدیک‌ترین همسایه را با قراردادِ مختصات و انتخابِ پیکسل پیاده می‌کنیم؛ سپس پاسخ دستی و مرجع هندسیِ مستقلی می‌سازیم. داده‌ها کاملاً ساختگی‌اند؛ نه دوربین و مدل بینایی اجرا می‌شود و نه بازگردانیِ واقعیِ نسخهٔ سامانه.

۱. تصویرِ برچسب و قراردادِ خروجی را مشخص کنیم

کاشی‌های رنگیِ برچسب و قاب ورودی و خروجیِ جدا
تصویر مفهومی: کاشی‌های رنگیِ برچسب و قاب ورودی و خروجیِ جدا؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

ورودی دو سطر و سه ستون دارد: سطر نخست ده، بیست و سی؛ سطر دوم چهل، پنجاه و شصت. عددها شناسهٔ برچسبِ دلخواه‌اند، نه روشناییِ کالیبره‌شده، رنگِ فیزیکی یا اندازه‌گیریِ پزشکی. شمارهٔ سطر و ستون از صفر آغاز می‌شود. خروجیِ اصلی سه سطر و چهار ستون خواهد داشت و ترتیبِ اندازه‌ها همیشه ارتفاع، سپس عرض است.

هر خانهٔ خروجی باید دقیقاً مقدارِ یکی از خانه‌های ورودی را کپی کند؛ میانگینِ دو شناسه معنا ندارد. بنابراین این روش برچسبِ تازه‌ای از ترکیبِ عددها نمی‌سازد، ولی حفظِ مساحتِ هر کلاس یا باقی‌ماندنِ تمامِ برچسب‌ها را هم تضمین نمی‌کند. کم‌نمونه‌برداری می‌تواند یک برچسبِ کوچک را کاملاً کنار بگذارد؛ این پیامد را نباید با تشخیصِ خطای قطعه اشتباه گرفت.

تابع آموزشی فقط تصویرِ مستطیلیِ غیرخالی از فهرست یا تاپل می‌پذیرد. طولِ هر محور از یک تا شصت‌وچهار و هر برچسب، عدد صحیحِ واقعیِ پایتون از صفر تا ۲۵۵ است؛ بولی و شناور پذیرفته نیستند. این سقف، نمایهٔ محدودِ آزمایش است، نه محدودیتِ استانداردِ تصویر یا ظرفیتِ اندازه‌گیری‌شدهٔ دستگاه. هیچ فایل یا کانالِ رنگی باز نمی‌کنیم.

برای هر آدرسِ خروجی، آدرسِ مبدأ را پیدا می‌کنیم؛ این نگاشتِ عقب‌رو است. خروجی را با پرتابِ هر پیکسلِ ورودی به قابِ جدید نمی‌سازیم، چون آن روش به سیاستِ خانه‌های خالی و برخوردها نیاز دارد. جدول جمعِ پیشوندِ تصویر نیز مسئلهٔ دیگری است: جمعِ یک ناحیه را می‌دهد، نه مقدارِ خانهٔ قابِ تازه را.

۲. مختصاتِ نامتقارن و نیم‌پیکسل را جدا کنیم

دو صفحهٔ شفاف نمونه‌برداری با جابه‌جاییِ مرکز نسبت به لبه
تصویر مفهومی: دو صفحهٔ شفاف نمونه‌برداری با جابه‌جاییِ مرکز نسبت به لبه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

یک محور با طولِ مبدأِ سه و طولِ مقصدِ چهار را در نظر بگیریم. در قراردادِ asymmetric، مختصاتِ مبدأ برابر آدرسِ مقصد ضرب‌در طولِ مبدأ تقسیم بر طولِ مقصد است. برای چهار آدرس، صفر، سه‌چهارم، سه‌دوم و نه‌چهارم به دست می‌آید. این‌ها هنوز شمارهٔ صحیحِ پیکسل نیستند؛ مرحلهٔ انتخاب را جدا نگه می‌داریم.

در قراردادِ half_pixel، ابتدا نیم واحد به آدرسِ مقصد اضافه می‌کنیم، نسبتِ طول‌ها را اعمال می‌کنیم و در پایان نیم واحد کم می‌کنیم. برای همان محور، پاسخ منفی یک‌هشتم، پنج‌هشتم، یازده‌هشتم و هفده‌هشتم است. تعریف Resize نسخهٔ ۱۹ در ONNX این دو نگاشت و قاعدهٔ انتخابِ نزدیک‌ترین خانه را به‌صورت ویژگی‌های جدا توضیح می‌دهد.

در کد، نسبت‌ها با Fraction از طول‌ها و آدرس‌های صحیح ساخته می‌شوند؛ پس تساویِ دقیقِ نصف، قربانیِ تقریبِ اعشاریِ نسبت نمی‌شود. مختصات در واحدِ شمارهٔ پیکسلِ مبدأ و با مرکزِ صفر برای خانهٔ نخست بیان شده‌اند. این چارچوب را با گوشه‌های فیزیکیِ سنسور، میلی‌متر یا قراردادِ یک‌مبنای مختصاتِ هندسی یکسان نمی‌گیریم.

دو عامل را مستقل عوض می‌کنیم. برای طولِ سه به دو، با انتخابِ نزدیک‌ترین و ترجیحِ پایین در تساوی، نگاشتِ نامتقارن شماره‌های صفر و یک را انتخاب می‌کند؛ نیم‌پیکسل، صفر و دو را برمی‌گزیند. پس حتی با ثابت‌بودنِ قاعدهٔ گردکردن، تغییرِ نگاشت می‌تواند داده را عوض کند. در نمونهٔ اصلی، دو پیکربندیِ کامل مقایسه می‌شوند؛ اختلاف را به‌تنهایی به یکی از دو عامل نسبت نمی‌دهیم.

۳. تساویِ فاصله و محدودکردنِ مرز را تعیین کنیم

گویِ نشانه میان دو خانهٔ هم‌فاصله و نگهدارندهٔ لبه
تصویر مفهومی: گویِ نشانه میان دو خانهٔ هم‌فاصله و نگهدارندهٔ لبه؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

گزینهٔ floor بزرگ‌ترین عددِ صحیحِ کوچک‌تر یا مساویِ مختصات را می‌گیرد. گزینهٔ round_prefer_floor نزدیک‌ترین صحیح را می‌گیرد و فقط در فاصلهٔ برابر، صحیحِ پایین‌تر را ترجیح می‌دهد. برای سه‌دوم، پاسخِ گزینهٔ دوم یک است؛ فراخوانیِ عمومیِ round روی همین کسر در اجرای ما دو می‌دهد. جایگزینیِ بی‌بررسیِ آن، قراردادِ تساوی را عوض می‌کند.

در محورِ چهار به سه با نیم‌پیکسل، مختصات یک‌ششم، سه‌دوم و هفده‌ششم‌اند؛ نزدیک‌ترینِ با ترجیح پایین، شماره‌های صفر، یک و سه را می‌دهد. اگر مقصد فقط یک خانه باشد، مختصاتِ نیم‌پیکسلِ مبدأِ چهارتایی سه‌دوم است؛ شمارهٔ یک انتخاب می‌شود. این قرارداد را با گزینهٔ جداگانهٔ pytorch_half_pixel در مشخصات ONNX، که برای طولِ مقصدِ یک رفتار دیگری دارد، اشتباه نمی‌گیریم.

پس از انتخابِ صحیح، شماره را به بازهٔ صفر تا آخرین خانه محدود می‌کنیم. برای نیم‌پیکسل همراهِ کف، نخستین مختصاتِ محورِ سه به چهار منفی یک‌هشتم و کفِ آن منفی یک است؛ محدودکردن، خانهٔ صفر را برمی‌گرداند. خواندنِ مستقیمِ اندیسِ منفی در پایتون، به آخرِ دنباله می‌رود و سیاستِ لبهٔ این درس نیست. اینجا بیرونِ تصویر با صفر پر نمی‌شود.

راهنمای interpolate در PyTorch 2.14 نیز حالت‌های نزدیک‌ترین و نزدیک‌ترینِ دقیق را جدا کرده و تفاوتِ سازگاریِ آن‌ها را بیان می‌کند. از این نام‌ها، برابریِ بیتیِ تابعِ خودمان با آن API نتیجه نمی‌گیریم. گزینهٔ هم‌ترازیِ گوشه‌ها در آن صفحه برای حالت‌های خطی توضیح داده شده است؛ آن را کلیدِ اصلاحِ خودکارِ نزدیک‌ترین نمی‌نامیم.

۴. کد کامل را با مرجعِ هندسیِ مستقل بسنجیم

دو مسیر بازبینی مستقل در میز بازرسیِ پنل رنگی
تصویر مفهومی: دو مسیر بازبینی مستقل در میز بازرسیِ پنل رنگی؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایلِ زیر با Python 3.14.5 و کتابخانهٔ استاندارد واقعاً اجرا شده است. فقط دو نگاشت و دو قاعدهٔ انتخابِ معرفی‌شده پذیرفته می‌شوند. برنامه طرحِ هر محور، مختصاتِ کسری، صحیحِ پیش از محدودکردن و آدرسِ نهایی را نگه می‌دارد. خروجی از تاپل‌های تازه ساخته می‌شود؛ تغییرِ فهرستِ اولیه بعد از محاسبه، تصویرِ برگردانده‌شده را تغییر نمی‌دهد.

"""Exact nearest-neighbor resampling contract on small synthetic label images."""
from dataclasses import dataclass
from fractions import Fraction
from itertools import product
import json
import platform


def dimension(value):
    if type(value) is not int or not 1 <= value <= 64:
        raise ValueError("actual integer dimension from 1 through 64 required")
    return value


def policies(coordinate, nearest):
    if type(coordinate) is not str or coordinate not in ("asymmetric", "half_pixel"):
        raise ValueError("unsupported coordinate policy")
    if type(nearest) is not str or nearest not in ("floor", "round_prefer_floor"):
        raise ValueError("unsupported nearest policy")


@dataclass(frozen=True)
class AxisPlan:
    source_size: int
    target_size: int
    coordinate: str
    nearest: str
    positions: tuple
    rounded_before_clipping: tuple
    source_indices: tuple


def axis_plan(source_size, target_size, coordinate, nearest):
    source_size, target_size = dimension(source_size), dimension(target_size)
    policies(coordinate, nearest)
    positions, rounded, indices = [], [], []
    for i in range(target_size):
        if coordinate == "asymmetric":
            value = Fraction(i * source_size, target_size)
        else:
            value = Fraction((2 * i + 1) * source_size - target_size, 2 * target_size)
        lower, remainder = divmod(value.numerator, value.denominator)
        selected = lower
        if nearest == "round_prefer_floor" and 2 * remainder > value.denominator:
            selected += 1
        positions.append(value)
        rounded.append(selected)
        indices.append(min(source_size - 1, max(0, selected)))
    return AxisPlan(source_size, target_size, coordinate, nearest,
                    tuple(positions), tuple(rounded), tuple(indices))


def label_image(image):
    if type(image) not in (tuple, list) or not image:
        raise ValueError("nonempty actual list/tuple of rows required")
    dimension(len(image))
    rows, width = [], None
    for row in image:
        if type(row) not in (tuple, list) or not row:
            raise ValueError("nonempty actual list/tuple row required")
        dimension(len(row))
        if width is None:
            width = len(row)
        if len(row) != width:
            raise ValueError("rectangular image required")
        if any(type(v) is not int or not 0 <= v <= 255 for v in row):
            raise ValueError("actual integer label from 0 through 255 required")
        rows.append(tuple(row))
    return tuple(rows)


@dataclass(frozen=True)
class Resized:
    pixels: tuple
    rows: AxisPlan
    columns: AxisPlan


def resize_labels(image, target_shape, coordinate, nearest):
    source = label_image(image)
    if type(target_shape) not in (tuple, list) or len(target_shape) != 2:
        raise ValueError("target shape is (rows, columns)")
    height, width = map(dimension, target_shape)
    row_plan = axis_plan(len(source), height, coordinate, nearest)
    column_plan = axis_plan(len(source[0]), width, coordinate, nearest)
    pixels = tuple(tuple(source[r][c] for c in column_plan.source_indices)
                   for r in row_plan.source_indices)
    return Resized(pixels, row_plan, column_plan)


def geometric_reference(source_size, target_size, coordinate, nearest):
    """Enumerate normalized anchors/centers; no backward-map/rounding helper."""
    result = []
    for i in range(target_size):
        if coordinate == "asymmetric":
            target = Fraction(i, target_size)
            centers = {j: Fraction(j, source_size) for j in range(-1, source_size + 1)}
        else:
            target = Fraction(2 * i + 1, 2 * target_size)
            centers = {j: Fraction(2 * j + 1, 2 * source_size)
                       for j in range(-1, source_size + 1)}
        if nearest == "floor":
            j = max(j for j, position in centers.items() if position <= target)
        else:
            j = min(centers, key=lambda index: (abs(centers[index] - target), index))
        result.append(0 if j < 0 else source_size - 1 if j >= source_size else j)
    return tuple(result)


def rejected(function, *args):
    try:
        function(*args)
    except ValueError:
        return True
    return False


def serialized(result):
    def axis(plan):
        return {"source_size": plan.source_size, "target_size": plan.target_size,
                "coordinate": plan.coordinate, "nearest": plan.nearest,
                "positions": [str(v) for v in plan.positions],
                "rounded_before_clipping": plan.rounded_before_clipping,
                "source_indices": plan.source_indices}
    return {"pixels": result.pixels, "rows": axis(result.rows),
            "columns": axis(result.columns)}


def main():
    image = ((10, 20, 30), (40, 50, 60))
    asymmetric = resize_labels(image, (3, 4), "asymmetric", "floor")
    centered = resize_labels(image, (3, 4), "half_pixel", "round_prefer_floor")
    checks = {}
    checks["literal_asymmetric_floor_rows_and_pixels"] = (
        asymmetric.rows.source_indices == (0, 0, 1)
        and asymmetric.columns.source_indices == (0, 0, 1, 2)
        and asymmetric.pixels == ((10, 10, 20, 30), (10, 10, 20, 30), (40, 40, 50, 60)))
    checks["literal_half_pixel_nearest_ties_lower_pixels"] = (
        centered.rows.source_indices == (0, 0, 1)
        and centered.columns.source_indices == (0, 1, 1, 2)
        and centered.pixels == ((10, 20, 20, 30), (10, 20, 20, 30), (40, 50, 50, 60)))
    boundary = axis_plan(3, 4, "half_pixel", "floor")
    checks["half_pixel_floor_negative_position_clips_to_first_label"] = (
        boundary.positions[0] == Fraction(-1, 8)
        and boundary.rounded_before_clipping == (-1, 0, 1, 2)
        and boundary.source_indices == (0, 0, 1, 2))
    tie = axis_plan(4, 3, "half_pixel", "round_prefer_floor")
    checks["exact_half_tie_is_not_bankers_rounding"] = (
        tie.positions[1] == Fraction(3, 2) and tie.source_indices == (0, 1, 3)
        and round(Fraction(3, 2)) == 2 and tie.source_indices[1] != 2)
    checks["single_output_half_pixel_center_differs_from_asymmetric_origin"] = (
        axis_plan(4, 1, "half_pixel", "round_prefer_floor").source_indices == (1,)
        and axis_plan(4, 1, "asymmetric", "floor").source_indices == (0,))
    modes = tuple(product(("asymmetric", "half_pixel"), ("floor", "round_prefer_floor")))
    checks["identity_shape_is_exact_for_all_four_declared_policies"] = all(
        resize_labels(image, (2, 3), *mode).pixels == image for mode in modes)
    checks["singleton_source_repeats_without_new_values"] = all(
        resize_labels(((73,),), (3, 4), *mode).pixels == ((73,) * 4,) * 3 for mode in modes)
    checks["coordinate_policy_changes_indices_with_same_rounding"] = (
        axis_plan(3, 2, "asymmetric", "round_prefer_floor").source_indices == (0, 1)
        and axis_plan(3, 2, "half_pixel", "round_prefer_floor").source_indices == (0, 2))
    transpose = tuple(zip(*image))
    transposed_result = resize_labels(transpose, (4, 3), "half_pixel", "round_prefer_floor")
    checks["non_square_transpose_swaps_plans_and_output_axes"] = (
        transposed_result.pixels == tuple(zip(*centered.pixels))
        and transposed_result.rows.source_indices == centered.columns.source_indices)
    axes, matrices = 0, 0
    for source, target in product(range(1, 10), repeat=2):
        for mode in modes:
            assert axis_plan(source, target, *mode).source_indices == geometric_reference(source, target, *mode)
            axes += 1
    for source_shape, target, mode in product(((1, 4), (2, 3), (3, 2), (3, 3)),
                                             ((1, 1), (3, 4), (4, 2)), modes):
        source = tuple(tuple(10 * r + c for c in range(source_shape[1]))
                       for r in range(source_shape[0]))
        rows = geometric_reference(source_shape[0], target[0], *mode)
        cols = geometric_reference(source_shape[1], target[1], *mode)
        expected = tuple(tuple(source[r][c] for c in cols) for r in rows)
        assert resize_labels(source, target, *mode).pixels == expected
        matrices += 1
    checks["normalized_geometry_oracle_324_axes_and_48_images_ONE_group"] = axes == 324 and matrices == 48
    mutable = [list(row) for row in image]
    saved = resize_labels(mutable, (3, 4), "half_pixel", "round_prefer_floor")
    unchanged = mutable == [list(row) for row in image]
    mutable[0][0] = 99
    checks["caller_lists_unchanged_and_later_mutation_does_not_alias_result"] = unchanged and saved.pixels == centered.pixels
    checks["invalid_images_rows_labels_and_actual_types_reject"] = all(
        rejected(resize_labels, value, (2, 2), "asymmetric", "floor")
        for value in (None, (), ((1,), ()), ((1, 2), (3,)), ((True,),), ((1.0,),),
                      ((-1,),), ((256,),), (("1",),), ((float("nan"),),)))
    checks["target_dimensions_container_actual_type_and_bounds_reject"] = all(
        rejected(resize_labels, image, shape, "asymmetric", "floor")
        for shape in ((0, 2), (65, 2), (True, 2), (2.0, 2), (10**400, 2), (2,), "23", None))
    checks["unknown_or_nonstring_coordinate_and_nearest_policy_reject"] = all(
        rejected(resize_labels, image, (2, 2), *mode)
        for mode in (("align_corners", "floor"), ("half_pixel", "ceil"),
                     (True, "floor"), ("asymmetric", None)))
    small = resize_labels(((10, 20, 30),), (1, 1), "half_pixel", "round_prefer_floor")
    restored_shape = resize_labels(small.pixels, (1, 3), "half_pixel", "round_prefer_floor")
    checks["restoring_shape_after_downsampling_does_not_restore_labels"] = (
        small.pixels == ((20,),) and restored_shape.pixels == ((20, 20, 20),)
        and restored_shape.pixels != ((10, 20, 30),))
    assert all(type(v) is bool and v for v in checks.values()), checks
    print(json.dumps({"python": platform.python_version(), "dependencies": "stdlib only",
        "frameworks_executed": False, "units": "integer label IDs; positions in zero-based source-pixel-index coordinates",
        "asymmetric_floor": serialized(asymmetric), "half_pixel_round_prefer_floor": serialized(centered),
        "half_pixel_floor_boundary": {"first_position": str(boundary.positions[0]),
            "unclipped": boundary.rounded_before_clipping, "clipped": boundary.source_indices},
        "tie_positions_for_source4_target3": [str(v) for v in tie.positions],
        "tie_indices": tie.source_indices, "oracle_axis_profiles_inside_ONE_group": axes,
        "oracle_2d_images_inside_same_ONE_group": matrices, "downsampled": small.pixels,
        "restored_shape_not_restored_data": restored_shape.pixels,
        "unique_check_groups": len(checks), "checks": checks,
        "limits": "Small authored categorical-image profile only; no file decoding, color conversion, interpolation averaging, antialiasing, physical calibration, model execution, API parity, quality/latency claim or actual software rollback."},
        ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()

در نمونهٔ نامتقارن همراهِ کف، سطر نخستِ خروجی ده، ده، بیست و سی است. در نیم‌پیکسل همراهِ نزدیک‌ترینِ با ترجیح پایین، همان سطر ده، بیست، بیست و سی می‌شود. شکلِ هر دو خروجی سه در چهار است؛ بنابراین آزمونِ صرفِ اندازه، تفاوت را نمی‌بیند. تمامِ سطرها و طرحِ دو محور در خروجی ثبت شده‌اند تا جابه‌جاییِ سطر و ستون پنهان نماند.

مرجعِ مستقل، فرمولِ عقب‌رو و تابعِ گردکردنِ مسیرِ اصلی را صدا نمی‌زند. جای مرکزها یا تکیه‌گاه‌ها را روی بازهٔ نرمال‌شده می‌چیند و میان نامزدهای صحیح، فاصله‌ها را مستقیم مقایسه می‌کند؛ تساوی با شمارهٔ کوچک‌تر حل می‌شود. برای کف، آخرین تکیه‌گاهِ پیش از مقصد انتخاب می‌شود. این صورت‌بندی همان قراردادِ هندسی را از مسیر محاسباتیِ دیگری می‌سنجد.

پانزده گروهِ نام‌دار موفق شدند. سیصدوبیست‌وچهار طرحِ محور و چهل‌وهشت تصویرِ دوبعدی با مرجع سنجیده شدند؛ همهٔ این حالت‌ها فقط یک گروه‌اند. پاسخ‌های دستی، تک‌پیکسل، شکلِ همانی، جابه‌جاییِ دو محور، مرز، تساوی، ورودی‌های نامعتبر و استقلالِ داده نیز پوشش دارند. حسابِ آدرس‌ها صحیح و کسری است و تلورانسِ ممیز شناور برای این خروجیِ برچسبی نداریم.

{
  "python": "3.14.5",
  "dependencies": "stdlib only",
  "frameworks_executed": false,
  "units": "integer label IDs; positions in zero-based source-pixel-index coordinates",
  "asymmetric_floor": {
    "pixels": [
      [
        10,
        10,
        20,
        30
      ],
      [
        10,
        10,
        20,
        30
      ],
      [
        40,
        40,
        50,
        60
      ]
    ],
    "rows": {
      "source_size": 2,
      "target_size": 3,
      "coordinate": "asymmetric",
      "nearest": "floor",
      "positions": [
        "0",
        "2/3",
        "4/3"
      ],
      "rounded_before_clipping": [
        0,
        0,
        1
      ],
      "source_indices": [
        0,
        0,
        1
      ]
    },
    "columns": {
      "source_size": 3,
      "target_size": 4,
      "coordinate": "asymmetric",
      "nearest": "floor",
      "positions": [
        "0",
        "3/4",
        "3/2",
        "9/4"
      ],
      "rounded_before_clipping": [
        0,
        0,
        1,
        2
      ],
      "source_indices": [
        0,
        0,
        1,
        2
      ]
    }
  },
  "half_pixel_round_prefer_floor": {
    "pixels": [
      [
        10,
        20,
        20,
        30
      ],
      [
        10,
        20,
        20,
        30
      ],
      [
        40,
        50,
        50,
        60
      ]
    ],
    "rows": {
      "source_size": 2,
      "target_size": 3,
      "coordinate": "half_pixel",
      "nearest": "round_prefer_floor",
      "positions": [
        "-1/6",
        "1/2",
        "7/6"
      ],
      "rounded_before_clipping": [
        0,
        0,
        1
      ],
      "source_indices": [
        0,
        0,
        1
      ]
    },
    "columns": {
      "source_size": 3,
      "target_size": 4,
      "coordinate": "half_pixel",
      "nearest": "round_prefer_floor",
      "positions": [
        "-1/8",
        "5/8",
        "11/8",
        "17/8"
      ],
      "rounded_before_clipping": [
        0,
        1,
        1,
        2
      ],
      "source_indices": [
        0,
        1,
        1,
        2
      ]
    }
  },
  "half_pixel_floor_boundary": {
    "first_position": "-1/8",
    "unclipped": [
      -1,
      0,
      1,
      2
    ],
    "clipped": [
      0,
      0,
      1,
      2
    ]
  },
  "tie_positions_for_source4_target3": [
    "1/6",
    "3/2",
    "17/6"
  ],
  "tie_indices": [
    0,
    1,
    3
  ],
  "oracle_axis_profiles_inside_ONE_group": 324,
  "oracle_2d_images_inside_same_ONE_group": 48,
  "downsampled": [
    [
      20
    ]
  ],
  "restored_shape_not_restored_data": [
    [
      20,
      20,
      20
    ]
  ],
  "unique_check_groups": 15,
  "checks": {
    "literal_asymmetric_floor_rows_and_pixels": true,
    "literal_half_pixel_nearest_ties_lower_pixels": true,
    "half_pixel_floor_negative_position_clips_to_first_label": true,
    "exact_half_tie_is_not_bankers_rounding": true,
    "single_output_half_pixel_center_differs_from_asymmetric_origin": true,
    "identity_shape_is_exact_for_all_four_declared_policies": true,
    "singleton_source_repeats_without_new_values": true,
    "coordinate_policy_changes_indices_with_same_rounding": true,
    "non_square_transpose_swaps_plans_and_output_axes": true,
    "normalized_geometry_oracle_324_axes_and_48_images_ONE_group": true,
    "caller_lists_unchanged_and_later_mutation_does_not_alias_result": true,
    "invalid_images_rows_labels_and_actual_types_reject": true,
    "target_dimensions_container_actual_type_and_bounds_reject": true,
    "unknown_or_nonstring_coordinate_and_nearest_policy_reject": true,
    "restoring_shape_after_downsampling_does_not_restore_labels": true
  },
  "limits": "Small authored categorical-image profile only; no file decoding, color conversion, interpolation averaging, antialiasing, physical calibration, model execution, API parity, quality/latency claim or actual software rollback."
}

۵. چهار پرسش برای بازبینی نسخهٔ پیش‌پردازش

دو ماژول پیش‌پردازشِ تعویض‌پذیر کنار الگوی رنگی ثابت
تصویر مفهومی: دو ماژول پیش‌پردازشِ تعویض‌پذیر کنار الگوی رنگی ثابت؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

نامِ الگوریتم و اندازهٔ مقصد برای قراردادِ نسخه کافی نیستند؛ نگاشت، گردکردن، سیاستِ لبه و نوعِ داده هم باید در آزمونِ سازگاری بمانند. این درس استقرار یا بازگردانی را اجرا نمی‌کند و موفقیتِ عددیِ آن، کیفیتِ مدل را ثابت نمی‌کند. هموارسازی جدایی‌پذیر تصویر نیز عملیاتِ جداگانه‌ای است؛ اینجا فیلترِ ضدهم‌پوشانیِ فرکانسی، میانگین‌گیری یا افزایشِ جزئیات نداریم.

پرسش اول: چرا دو خروجیِ سه در چهار کافی نیستند که نسخه‌ها را هم‌ارز بدانیم؟ پاسخ: مقدارِ ستونِ دومِ سطرِ نخست در دو پیکربندی ده و بیست است. شکلِ یکسان، انتخابِ مبدأِ یکسان را تضمین نمی‌کند؛ پاسخِ دستی باید مقدار و طرحِ نمونه‌برداری را هم پوشش دهد.

پرسش دوم: برای چهار برچسبِ ده، بیست، سی و چهل، مقصدِ سه‌خانه‌ای با نیم‌پیکسل و ترجیحِ پایین چیست؟ پاسخ: شماره‌های صفر، یک و سه، یعنی ده، بیست و چهل. مختصاتِ میانی سه‌دوم است و تساوی عمداً به شمارهٔ یک می‌رود، نه دو.

پرسش سوم: می‌توان صحیحِ منفیِ قبل از محدودکردن را مستقیم خواند؟ پاسخ: خیر؛ در پایتون منفی یک آخرین عضو را انتخاب می‌کند. قراردادِ ما نزدیک‌ترین لبه را ادامه می‌دهد، پس آن شماره را صفر می‌کند. این گسترش، مجوزِ تغییرِ خودکارِ قراردادِ برش یا هموارسازیِ دیگری نیست.

پرسش چهارم: کوچک‌کردن و سپس برگرداندنِ اندازه، داده را بازیابی می‌کند؟ پاسخ: خیر؛ ردیفِ ده، بیست، سی در مقصدِ تک‌خانه‌ایِ نیم‌پیکسل، بیست می‌شود و بازگشتِ اندازه فقط سه بیست می‌سازد. منابع در ۲۶ مهر ۱۴۰۵ خوانده شده‌اند؛ اجرای ONNX، PyTorch، MATLAB، دوربین، آزمونِ سرعت یا ادعای بهبودِ تشخیص نداریم و تصویرها مفهومی‌اند.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۶ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.