Заполнение вывода scipy affine_transform для отображения неперекрывающихся областей преобразованных изображений

У меня есть исходное (src) изображение (я), которое я хочу выровнять по целевому (dst) изображению с помощью аффинного преобразования, сохраняя при этом полный экстент обоих изображений во время выравнивания (даже неперекрывающиеся области).

Я уже могу рассчитать матрицу поворота и смещения аффинного преобразования, которую я передаю в scipy.ndimage.interpolate.affine_transform для восстановления dst-выровненного src изображения.

Проблема в том, что, когда изображения не перекрываются полностью, результирующее изображение обрезается только до общей площади двух изображений. Что мне нужно, так это полный экстент обоих изображений, размещенных в одной и той же системе координат пикселей. Этот вопрос почти дублирует Вот этот, и отличный ответ и репозиторий предоставляют эту функциональность для преобразований OpenCV. К сожалению, мне это нужно для реализации scipy.

Слишком поздно, после того, как я неоднократно натыкался на кирпичную стену, пытаясь перевести ответ на приведенный выше вопрос в scipy, я наткнулся на Эта проблема, а затем последовал на этот вопрос. Последний вопрос действительно дал некоторое представление о чудесном мире аффинной трансформации scipy, но я пока не смог раскрыть свои конкретные потребности.

Преобразования из src в dst могут иметь переводы и повороты. Я могу заставить работать только переводы (пример показан ниже) и я могу заставить работать только вращения (в основном взламывая приведенное ниже и черпая вдохновение из использования аргумента reshape в scipy.ndimage.interpolation.rotate). Тем не менее, я полностью теряюсь, объединяя два. Я попытался рассчитать, что должно быть правильным offset (см. ответы на этот вопрос еще раз), но я не могу заставить его работать во всех сценариях.

Рабочий пример аффинного преобразования с дополнением только для перевода, который в значительной степени следует это репо, описанному в этот ответ:

from scipy.ndimage import rotate, affine_transform
import numpy as np
import matplotlib.pyplot as plt

nblob = 50
shape = (200, 100)
buffered_shape = (300, 200)  # buffer for rotation and translation


def affine_test(angle=0, translate=(0, 0)):
    np.random.seed(42)
    # Maxiumum translation allowed is half difference between shape and buffered_shape

    # Generate a buffered_shape-sized base image with random blobs
    base = np.zeros(buffered_shape, dtype=np.float32)
    random_locs = np.random.choice(np.arange(2, buffered_shape[0] - 2), nblob * 2, replace=False)
    i = random_locs[:nblob]
    j = random_locs[nblob:]
    for k, (_i, _j) in enumerate(zip(i, j)):
        # Use different values, just to make it easier to distinguish blobs
        base[_i - 2 : _i + 2, _j - 2 : _j + 2] = k + 10

    # Impose a rotation and translation on source
    src = rotate(base, angle, reshape=False, order=1, mode = "constant")
    bsc = (np.array(buffered_shape) / 2).astype(int)
    sc = (np.array(shape) / 2).astype(int)
    src = src[
        bsc[0] - sc[0] + translate[0] : bsc[0] + sc[0] + translate[0],
        bsc[1] - sc[1] + translate[1] : bsc[1] + sc[1] + translate[1],
    ]
    # Cut-out destination from the centre of the base image
    dst = base[bsc[0] - sc[0] : bsc[0] + sc[0], bsc[1] - sc[1] : bsc[1] + sc[1]]

    src_y, src_x = src.shape

    def get_matrix_offset(centre, angle, scale):
        """Follows OpenCV.getRotationMatrix2D"""
        angle = angle * np.pi / 180
        alpha = scale * np.cos(angle)
        beta = scale * np.sin(angle)
        return (
            np.array([[alpha, beta], [-beta, alpha]]),
            np.array(
                [
                    (1 - alpha) * centre[0] - beta * centre[1],
                    beta * centre[0] + (1 - alpha) * centre[1],
                ]
            ),
        )
    # Obtain the rotation matrix and offset that describes the transformation
    # between src and dst
    matrix, offset = get_matrix_offset(np.array([src_y / 2, src_x / 2]), angle, 1)
    offset = offset - translate

    # Determine the outer bounds of the new image
    lin_pts = np.array([[0, src_x, src_x, 0], [0, 0, src_y, src_y]])
    transf_lin_pts = np.dot(matrix.T, lin_pts) - offset[::-1].reshape(2, 1)

    # Find min and max bounds of the transformed image
    min_x = np.floor(np.min(transf_lin_pts[0])).astype(int)
    min_y = np.floor(np.min(transf_lin_pts[1])).astype(int)
    max_x = np.ceil(np.max(transf_lin_pts[0])).astype(int)
    max_y = np.ceil(np.max(transf_lin_pts[1])).astype(int)

    # Add translation to the transformation matrix to shift to positive values
    anchor_x, anchor_y = 0, 0
    if min_x < 0:
        anchor_x = -min_x
    if min_y < 0:
        anchor_y = -min_y
    shifted_offset = offset - np.dot(matrix, [anchor_y, anchor_x])

    # Create padded destination image
    dst_h, dst_w = dst.shape[:2]
    pad_widths = [anchor_y, max(max_y, dst_h) - dst_h, anchor_x, max(max_x, dst_w) - dst_w]
    dst_padded = np.pad(
        dst,
        ((pad_widths[0], pad_widths[1]), (pad_widths[2], pad_widths[3])),
        "constant",
        constant_values=-1,
    )
    dst_pad_h, dst_pad_w = dst_padded.shape

    # Create the aligned and padded source image
    source_aligned = affine_transform(
        src,
        matrix.T,
        offset=shifted_offset,
        output_shape=(dst_pad_h, dst_pad_w),
        order=3,
        mode = "constant",
        cval=-1,
    )

    # Plot the images
    fig, axes = plt.subplots(1, 4, figsize=(10, 5), sharex=True, sharey=True)
    axes[0].imshow(src, cmap = "viridis", vmin=-1, vmax=nblob)
    axes[0].set_title("Source")
    axes[1].imshow(dst, cmap = "viridis", vmin=-1, vmax=nblob)
    axes[1].set_title("Dest")
    axes[2].imshow(source_aligned, cmap = "viridis", vmin=-1, vmax=nblob)
    axes[2].set_title("Source aligned to Dest padded")
    axes[3].imshow(dst_padded, cmap = "viridis", vmin=-1, vmax=nblob)
    axes[3].set_title("Dest padded")
    plt.show()

например.:

affine_test(0, (-20, 40))

дает:

Заполнение вывода scipy affine_transform для отображения неперекрывающихся областей преобразованных изображений

С увеличением, показывающим выровненные в дополненных изображениях:

Заполнение вывода scipy affine_transform для отображения неперекрывающихся областей преобразованных изображений

Мне нужен полный экстент изображений src и dst, выровненных по одним и тем же пиксельным координатам, с поворотами и переводами.

Любая помощь приветствуется сильно!

Пожалуйста, опубликуйте образцы входных srcs и dsts, чтобы мы могли с ними работать.

Ann Zen 21.03.2022 17:41

Функция affine_test выше создаст src и dst, применив поворот (angle) и перевод (translation) на основе переданных аргументов.

Jdog 21.03.2022 18:46

Для меня ваши изображения немного сбивают с толку, так как я не вижу выравнивания. Не могли бы вы рассказать о них немного, чтобы я мог найти ответ?

Ann Zen 23.03.2022 21:39

На изображениях «Источник» — это переведенная версия «Пункт назначения»: если вы переместите «Источник» на -20 пикселей по оси y и на +40 пикселей по оси x, вы получите «Пункт назначения». Дополненные версии могут выглядеть не выровненными, потому что глаз обманывает, но они идеально выровнены, и они - принципиально - были дополнены значениями -1, так что каждое соответствующее изображение сохраняет свой полный начальный след и отпечаток другого изображения. В этом смысле, если я наложу их друг на друга, я получу изображение, покрывающее объединение их исходного следа. Нижний ряд — это просто увеличение того же ряда изображений.

Jdog 24.03.2022 14:05
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
7
4
217
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Если у вас есть два похожих (или одинаковых) изображения и вы хотите их выровнять, вы можете сделать это, используя обе функции rotate и shift :

from scipy.ndimage import rotate, shift

Вам нужно найди сначала разность углов между двумя изображениями angle_to_rotate, при этом вы применить вращение в src:

angle_to_rotate = 25
rotated_src = rotate(src, angle_to_rotate , reshape=True, order=1, mode = "constant")

С помощью reshape=True вы избегаете потери информации из исходной матрицы src и дополняет результат, чтобы изображение можно было перевести вокруг индексов 0,0. Вы можете рассчитать этот перевод как есть (x*cos(angle),y*sin(angle), где x и y — размеры изображения, но, вероятно, это не будет иметь значения.

Теперь вам нужно перевести изображение в источник, для этого вы можете использовать функция сдвига:

rot_translated_src = shift(rotated_src , [distance_x, distance_y])

В этом случае нет изменения формы (потому что иначе у вас не было бы реального перевода), поэтому, если изображение не было предварительно дополнено, некоторая информация будет потеряна.

Но вы можете сделать некоторые дополнения с помощью

np.pad(src, number, mode='constant')

Чтобы рассчитать distance_x и distance_y, вам нужно будет найти точку, которая служит вам ориентиром между rotated_src и пунктом назначения, а затем просто вычислить расстояние по осям x и y.

Резюме

  1. Сделайте прокладку в src и dst
  2. Найдите угловое расстояние между ними.
  3. Повернутьsrc с scipy.ndimage.rotate с использованием reshape=True
  4. Найдите горизонтальное и вертикальное расстояниеdistance_x, distance_y между повернутым изображением и dst
  5. Перевести ваш 'rotated_src' с scipy.ndimage.shift

Код

from scipy.ndimage import rotate, shift
import matplotlib.pyplot as plt
import numpy as np

Сначала мы делаем целевое изображение:

# make and plot dest
dst = np.ones([40,20])
dst = np.pad(dst,10)
dst[17,[14,24]]=4
dst[27,14:25]=4
dst[26,[14,25]]=4
rotated_dst = rotate(dst, 20, order=1)

plt.imshow(dst) # plot it
plt.imshow(rotated_dst)
plt.show()

Делаем Исходный образ:

# make_src image and plot it
src = np.zeros([40,20])
src = np.pad(src,10)
src[0:20,0:20]=1
src[7,[4,14]]=4
src[17,4:15]=4
src[16,[4,15]]=4
plt.imshow(src)
plt.show()

Затем мы выравниваем src по месту назначения:

rotated_src = rotate(src, 20, order=1) # find the angle 20, reshape true is by default
plt.imshow(rotated_src)
plt.show()
distance_y = 8 # find this distances from rotated_src and dst
distance_x = 12 # use any visual reference or even the corners
translated_src = shift(rotated_src, [distance_y,distance_x])
plt.imshow(translated_src)
plt.show()

пд: Если вы обнаружите проблемы с нахождением угла и расстояний программным способом, оставьте комментарий, предоставляющий немного больше информации о том, что можно использовать в качестве эталона, например, кадр изображения или некоторые функции изображения. / данные)

Анализ сложности

Задача состоит в том, чтобы определить три параметра

Предположим, что у вас есть сетка для углов, смещений по осям x и y, каждое из которых имеет размер O(n), и что ваши изображения имеют размер O(n x n), поэтому вращение, перемещение и сравнение изображений занимают O(n^2), поскольку у вас есть O(n^3) преобразования-кандидаты, чтобы попробовать , вы получаете сложность O(n^5), и, вероятно, поэтому вы задаете вопрос.

Однако часть смещения можно вычислить несколько более эффективно, если вычислить максимальную корреляцию с использованием преобразований Фурье. Преобразования Фурье могут быть выполнены со сложностью O(n log n) по каждой оси, и мы должны выполнить их для двух пространственных измерений, полная корреляционная матрица может быть вычислена в O(n^2 log^2 n), затем мы найдем максимум со сложностью O(n^2), поэтому общая временная сложность определения лучший расклад O(n^2 log^2 n). Однако вы все равно хотите найти лучший ракурс, так как у нас есть O(n) ракурсы-кандидаты, общая сложность этого поиска будет O(n^3 log^2 n). Помните, что мы используем Python, и у нас могут быть значительные накладные расходы, поэтому эта сложность дает нам только представление о том, насколько это будет сложно, и я справлялся с такими проблемами раньше, поэтому я начинаю уверенно.

Подготовка некоторого примера

Я начну с загрузки изображения и применения поворота и центрирования изображения нулями.


def centralized(a, width, height):
    '''
    Image centralized to the given width and height
    by padding with zeros (black)
    '''
    assert width >= a.shape[0] and height >= a.shape[1]
    ap = np.zeros((width, height) + a.shape[2:], a.dtype)
    ccx = (width - a.shape[0])//2
    ccy = (height - a.shape[1])//2
    ap[ccx:ccx+a.shape[0], ccy:ccy+a.shape[1], ...] = a
    return ap
def image_pair(im, width, height, displacement=(0,0), angle=0):
    '''
    this build an a pair of images as numpy arrays
    from the input image.
    Both images will be padded with zeros (black)
    and roughly centralized.
    and will have the specified shape
    
    make sure that the width and height chosen are enough 
    to fit the rotated image
    '''
    a = np.array(im)
    a1 = centralized(a, width, height)
    a2 = centralized(ndimage.rotate(a, angle), width, height)
    a2 = np.roll(a2, displacement, axis=(0,1))
    return a1, a2

def random_transform():
    angle = np.random.rand() * 360
    displacement = np.random.randint(-100, 100, 2)
    return displacement, angle

a1, a2 = image_pair(im, 512, 512, *random_transform())
plt.subplot(121)
plt.imshow(a1)
plt.subplot(122)
plt.imshow(a2)

Поиск смещения

Первое, что нужно сделать, это вычислить корреляцию изображения

def compute_correlation(a1, a2):
    A1 = np.fft.rfftn(a1, axes=(0,1))
    A2 = np.fft.rfftn(a2, axes=(0,1))
    C = np.fft.irfftn(np.sum(A1 * np.conj(A2), axis=2))
    return C

Затем давайте создадим пример без поворота и подтвердим, что с индексом максимальной корреляции мы можем найти смещение, которое соответствует одному изображению другому.

displacement, _ = random_transform()
a1, a2 = image_pair(im, 521, 512, displacement, angle=0)
C = compute_correlation(a1, a2)
np.unravel_index(np.argmax(C), C.shape), displacement
a3 = np.roll(a2, np.unravel_index(np.argmax(C), C.shape), axis=(0,1))
assert np.all(a3 == a1)

С вращением или интерполяцией этот результат может быть неточным, но он дает смещение, которое даст нам самое близкое возможное выравнивание.

Давайте поместим это в функцию для будущего использования

def get_aligned(a1, a2, angle):
    a1_rotated = ndimage.rotate(a1, angle, reshape=False)
    C = compute_correlation(a2, a1_rotated)
    found_displacement = np.unravel_index(np.argmax(C), C.shape)
    a1_aligned = np.roll(a1_rotated, found_displacement, axis=(0,1))
    return a1_aligned

В поисках угла

Теперь мы можем сделать что-то в два шага,

в одном мы вычисляем корреляцию для каждого угла, затем с углом, который дает максимальную корреляцию, находим выравнивание.

displacement, angle = random_transform()
a1, a2 = image_pair(im, 521, 512, displacement, angle)
C_max = []
C_argmax = []
angle_guesses = np.arange(0, 360, 5)
for angle_guess in angle_guesses:
    a1_rotated = ndimage.rotate(a1, angle_guess, reshape=False)
    C = compute_correlation(a1_rotated, a2)
    i = np.argmax(C)
    v = C.reshape(-1)[i]
    C_max.append(v)
    C_argmax.append(i)

Посмотрим, как будет выглядеть корреляция

plt.plot(angle_guesses, C_max);

У нас есть явный победитель, глядя на эту кривую, даже если у подсолнуха есть какая-то вращательная симметрия.

Давайте применим преобразование к исходному изображению и посмотрим, как оно выглядит.

a1_aligned = get_aligned(a1, a2, angle_guesses[np.argmax(C_max)])
plt.subplot(121)
plt.imshow(a2)
plt.subplot(122)
plt.imshow(a1_aligned)

Отлично, я бы не сделал лучше, чем это вручную.

Я использую изображение подсолнуха из соображений красоты, но процедура одинакова для любого типа изображения. Я использую RGB, показывая, что изображение может иметь одно дополнительное измерение, то есть оно использует вектор признаков, вместо скалярного признака, вы можете изменить форму своих данных на (width, height, 1), если ваш признак является скалярным.

Хорошо, я думаю, мой вопрос не так ясен, как я думал. Я уже есть преобразование между двумя моими изображениями. Я знаю это очень точно - на самом деле это не одно и то же выравниваемое изображение, и они очень большие, поэтому ffts становятся дорогими и неточными. Моя единственная забота — сохранить контур моих соответствующих изображений, когда я применяю преобразование, чтобы поместить их в одну и ту же пиксельную систему координат. Ваш пример здесь сделал это, просто искусственно дополнив изображения большим количеством. Это может быть вариант, но вопрос правильно применяется [1/2]

Jdog 24.03.2022 13:57

affine_transform остается, учитывая взаимодействие между смещением и матрицей поворота. Кроме того, изображения, как уже упоминалось, очень большие, и заполнение их на величину, покрывающую наибольшее потенциальное преобразование, вероятно, невозможно [2/2].

Jdog 24.03.2022 13:59

Не могли бы вы более четко определить, что вы подразумеваете под сохранением посадочного места и как вы выполняете преобразование, которое не сохраняет посадочное место?

Bob 24.03.2022 15:22

Возьмем пример вопроса. Если я не попытаюсь учесть преобразование, при использовании scipy.ndimage.affine_transform на исходном (src) изображении мне будет возвращено изображение формы (100 200), где единственная сохранившаяся часть изображения — это та, которая перекрывается с местом назначения (dst) . Мне нужно сохранить все части источника, независимо от того, пересекаются ли они с пунктом назначения или нет (и наоборот)

Jdog 24.03.2022 15:43

Итак, вам нужно преобразование, которое сохраняет перекос и вращение, но может переводить изображение так, чтобы оно перемещало все в целевую область изображения?

Bob 24.03.2022 15:53

Типа, да. Мне нужно изменить преобразование и дополнить изображение dst, чтобы преобразованное src и дополненное dst находились в одних и тех же пиксельных координатах. Ответ и репозиторий реализации openCV, связанные с вопросом, показывают, как это можно сделать, но, как показывает проблема scipy, также связанная, scipy, похоже, делает некоторые странные вещи с преобразованием, так что метод, который работает для аффинного преобразования OpenCV , не работает.

Jdog 24.03.2022 16:09

Но заполнение может только увеличить координаты пикселей. Предположим, что координаты пикселей для выровненного dst больше, чем координаты пикселей для src, как вы ожидаете, что координаты пикселей будут выровнены? Также похоже, что вас интересуют точки, не можете передать массив с координатами точек и найти преобразование для их выравнивания? Тогда вы работаете только с координатами и нет тяжелых манипуляций с изображениями.

Bob 25.03.2022 09:22
Ответ принят как подходящий

Рабочий код ниже на случай, если кому-то еще понадобятся аффинные преобразования scipy:

def affine_test(angle=0, translate=(0, 0), shape=(200, 100), buffered_shape=(300, 200), nblob=50):
    # Maxiumum translation allowed is half difference between shape and buffered_shape

    np.random.seed(42)

    # Generate a buffered_shape-sized base image
    base = np.zeros(buffered_shape, dtype=np.float32)
    random_locs = np.random.choice(np.arange(2, buffered_shape[0] - 2), nblob * 2, replace=False)
    i = random_locs[:nblob]
    j = random_locs[nblob:]
    for k, (_i, _j) in enumerate(zip(i, j)):
        base[_i - 2 : _i + 2, _j - 2 : _j + 2] = k + 10

    # Impose a rotation and translation on source
    src = rotate(base, angle, reshape=False, order=1, mode = "constant")
    bsc = (np.array(buffered_shape) / 2).astype(int)
    sc = (np.array(shape) / 2).astype(int)
    src = src[
        bsc[0] - sc[0] + translate[0] : bsc[0] + sc[0] + translate[0],
        bsc[1] - sc[1] + translate[1] : bsc[1] + sc[1] + translate[1],
    ]
    # Cut-out destination from the centre of the base image
    dst = base[bsc[0] - sc[0] : bsc[0] + sc[0], bsc[1] - sc[1] : bsc[1] + sc[1]]

    src_y, src_x = src.shape

    def get_matrix_offset(centre, angle, scale):
        """Follows OpenCV.getRotationMatrix2D"""
        angle_rad = angle * np.pi / 180
        alpha = np.round(scale * np.cos(angle_rad), 8)
        beta = np.round(scale * np.sin(angle_rad), 8)
        return (
            np.array([[alpha, beta], [-beta, alpha]]),
            np.array(
                [
                    (1 - alpha) * centre[0] - beta * centre[1],
                    beta * centre[0] + (1 - alpha) * centre[1],
                ]
            ),
        )

    matrix, offset = get_matrix_offset(np.array([((src_y - 1) / 2) - translate[0], ((src_x - 1) / 2) - translate[
    1]]), angle, 1)

    offset += np.array(translate)

    M = np.column_stack((matrix, offset))
    M = np.vstack((M, [0, 0, 1]))
    iM = np.linalg.inv(M)
    imatrix = iM[:2, :2]
    ioffset = iM[:2, 2]

    # Determine the outer bounds of the new image
    lin_pts = np.array([[0, src_y-1, src_y-1, 0], [0, 0, src_x-1, src_x-1]])
    transf_lin_pts = np.dot(matrix, lin_pts) + offset.reshape(2, 1) # - np.array(translate).reshape(2, 1) # both?

    # Find min and max bounds of the transformed image
    min_x = np.floor(np.min(transf_lin_pts[1])).astype(int)
    min_y = np.floor(np.min(transf_lin_pts[0])).astype(int)
    max_x = np.ceil(np.max(transf_lin_pts[1])).astype(int)
    max_y = np.ceil(np.max(transf_lin_pts[0])).astype(int)

    # Add translation to the transformation matrix to shift to positive values
    anchor_x, anchor_y = 0, 0
    if min_x < 0:
        anchor_x = -min_x
    if min_y < 0:
        anchor_y = -min_y

    dot_anchor = np.dot(imatrix, [anchor_y, anchor_x])
    shifted_offset = ioffset - dot_anchor

    # Create padded destination image
    dst_y, dst_x = dst.shape[:2]
    pad_widths = [anchor_y, max(max_y, dst_y) - dst_y, anchor_x, max(max_x, dst_x) - dst_x]
    dst_padded = np.pad(
        dst,
        ((pad_widths[0], pad_widths[1]), (pad_widths[2], pad_widths[3])),
        "constant",
        constant_values=-10,
    )

    dst_pad_y, dst_pad_x = dst_padded.shape
    # Create the aligned and padded source image
    source_aligned = affine_transform(
        src,
        imatrix,
        offset=shifted_offset,
        output_shape=(dst_pad_y, dst_pad_x),
        order=3,
        mode = "constant",
        cval=-10,
    )

Например. Бег:

affine_test(angle=-25, translate=(10, -40))

покажет:

и увеличил:

Извините, код не очень хорошо написан как есть.

Обратите внимание, что, запуская это в дикой природе, я заметил, что он не может обрабатывать какие-либо изменения масштаба изображений, но я не уверен, что это не связано с тем, как я вычисляю преобразование, поэтому предостережение, которое стоит отметить и проверить, если вы выравниваете изображения с разными масштабами.

Вам следует подумать о присуждении награды за другой ответ, потому что она не может перейти к задавшему вопрос, даже если он ответит), так что репутация не будет потрачена впустую.

richardec 26.03.2022 02:12

@richardec Нет, репутация не будет «напрасной». Репутация, потраченная на вознаграждение, предназначена для привлечения большего внимания к посту, и именно это и было сделано.

Ann Zen 27.03.2022 15:22

@Jdog Как хорошо, не каждый день пользователь изо всех сил старается ответить на свой вопрос на благо будущих читателей! Кстати, можешь исправить отступ первой функции? Спасибо :)

Ann Zen 27.03.2022 15:29

Другие вопросы по теме