Изменение размера изображения без искажений OpenCV

Я использую python 3 и последнюю версию openCV. Я пытаюсь изменить размер изображения с помощью функции изменения размера, но после изменения размера изображение очень искажено. Код:

import cv2
file = "/home/tanmay/Desktop/test_image.png"
img = cv2.imread(file , 0)
print(img.shape)
cv2.imshow('img' , img)
k = cv2.waitKey(0)
if k == 27:
    cv2.destroyWindow('img')
resize_img = cv2.resize(img  , (28 , 28))
cv2.imshow('img' , resize_img)
x = cv2.waitKey(0)
if x == 27:
    cv2.destroyWindow('img')

Исходное изображение 480 x 640 (RGB поэтому я передал 0, чтобы получить его в оттенках серого)

Можно ли каким-либо образом изменить его размер и избежать искажений с помощью OpenCV или любой другой библиотеки? Я намереваюсь сделать рукописный распознаватель цифр, и я обучил свою нейронную сеть, используя данные MNIST, поэтому мне нужно, чтобы изображение было 28x28.

Ответ 1

Вы можете попробовать ниже. Функция сохранит частоту исходного изображения.

def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]

    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image

    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)

    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)

    # return the resized image
    return resized

Вот пример использования.

image = image_resize(image, height = 800)

Надеюсь, что это поможет.

Ответ 2

Если вам нужно изменить разрешение изображения и сохранить соотношение сторон, используйте функцию imutils (см. документацию). как то так:

img = cv2.imread(file , 0)
img = imutils.resize(img, width=1280)
cv2.imshow('image' , img)

надеюсь, это поможет, удачи!

Ответ 3

Ответ, предоставленный @vijay jha, слишком специфичен для конкретного случая. Также включает дополнительное ненужное дополнение. Я предлагаю фиксированный код ниже:

def resize2SquareKeepingAspectRation(img, size, interpolation):
  h, w = img.shape[:2]
  c = None if len(img.shape) < 3 else img.shape[2]
  if h == w: return cv2.resize(img, (size, size), interpolation)
  if h > w: dif = h
  else:     dif = w
  x_pos = int((dif - w)/2.)
  y_pos = int((dif - h)/2.)
  if c is None:
    mask = np.zeros((dif, dif), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
  else:
    mask = np.zeros((dif, dif, c), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]
  return cv2.resize(mask, (size, size), interpolation)

Код изменяет размер изображения, делая его квадратным и сохраняя аспектный рацион в одно и то же время. Также код подходит для 3-х канальных (цветных) изображений. Пример использования:

resized = resize2SquareKeepingAspectRation(img, size, cv2.INTER_AREA)

Ответ 4

Попробуйте эту простую функцию в Python, которая использует OpenCV. просто передайте изображение и укажите желаемый размер квадрата.

def resize_image(img, size=(28,28)):

    h, w = img.shape[:2]

    if h == w: 
        return cv2.resize(img, size, cv2.INTER_AREA)

    dif = h if h > w else w

    interpolation = cv2.INTER_AREA if dif > (size[0]+size[1])//2 else 
                    cv2.INTER_CUBIC

    x_pos = (dif - w)//2
    y_pos = (dif - h)//2

    if len(img.shape) == 2:
        mask = np.zeros((dif, dif), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
    else:
        mask = np.zeros((dif, dif, c), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]

    return cv2.resize(mask, size, interpolation)

использование: squared_image = get_square (изображение, размер = (28,28))

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

Надеюсь, что это поможет вам

Ответ 5

У меня есть набор данных для ручных рисунков, и мне нужно создать маленькие квадратные изображения из асимметричных рисунков.

enter image description here

Благодаря @vijay jha я создал квадратные изображения, сохраняя соотношение сторон исходного изображения. Одна из проблем заключалась в том, что чем больше вы уменьшали масштаб, тем больше информации терялось.

От 512x256 до 64x64 будет выглядеть так:

64x64

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

from skimage.transform import resize, pyramid_reduce


def get_square(image, square_size):

    height, width = image.shape    
    if(height > width):
      differ = height
    else:
      differ = width
    differ += 4

    # square filler
    mask = np.zeros((differ, differ), dtype = "uint8")

    x_pos = int((differ - width) / 2)
    y_pos = int((differ - height) / 2)

    # center image inside the square
    mask[y_pos: y_pos + height, x_pos: x_pos + width] = image[0: height, 0: width]

    # downscale if needed
    if differ / square_size > 1:
      mask = pyramid_reduce(mask, differ / square_size)
    else:
      mask = cv2.resize(mask, (square_size, square_size), interpolation = cv2.INTER_AREA)
    return mask

512x256 → 64x64

enter image description here

512x256 → 28x28

enter image description here

Ответ 6

img = cv2.resize(img, (int(img.shape[1]/2), int(img.shape[0]/2)))

изменит размер изображения до половины первоначального размера. Вы можете изменить его для любого другого отношения. Обратите внимание, что первый аргумент, переданный для resize(), - img.shape [1], а не img.shape [0]. Это может противоречить интуиции. Легко упустить этот поворот и получить очень искаженную картину.

Ответ 7

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

import cv2

def resize(self,image,window_height = 500):
    aspect_ratio = float(image.shape[1])/float(image.shape[0])
    window_width = window_height/aspect_ratio
    image = cv2.resize(image, (int(window_height),int(window_width)))
    return image

img = cv2.imread(img_source)         #image location
img_resized = resize(img,window_height = 800)
cv2.imshow("Resized",img_resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

Ответ 8

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

import cv2
def resize_and_letter_box(image, rows, cols):
    """
    Letter box (black bars) a color image (think pan & scan movie shown 
    on widescreen) if not same aspect ratio as specified rows and cols. 
    :param image: numpy.ndarray((image_rows, image_cols, channels), dtype=numpy.uint8)
    :param rows: int rows of letter boxed image returned  
    :param cols: int cols of letter boxed image returned
    :return: numpy.ndarray((rows, cols, channels), dtype=numpy.uint8)
    """
    image_rows, image_cols = image.shape[:2]
    row_ratio = rows / float(image_rows)
    col_ratio = cols / float(image_cols)
    ratio = min(row_ratio, col_ratio)
    image_resized = cv2.resize(image, dsize=(0, 0), fx=ratio, fy=ratio)
    letter_box = np.zeros((int(rows), int(cols), 3))
    row_start = int((letter_box.shape[0] - image_resized.shape[0]) / 2)
    col_start = int((letter_box.shape[1] - image_resized.shape[1]) / 2)
    letter_box[row_start:row_start + image_resized.shape[0], col_start:col_start + image_resized.shape[1]] = image_resized
    return letter_box

Ответ 9

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

    def crop_and_resize(img, w, h):
        im_h, im_w, channels = img.shape
        res_aspect_ratio = w/h
        input_aspect_ratio = im_w/im_h

        if input_aspect_ratio > res_aspect_ratio:
            im_w_r = int(input_aspect_ratio*h)
            im_h_r = h
            img = cv2.resize(img, (im_w_r , im_h_r))
            x1 = int((im_w_r - w)/2)
            x2 = x1 + w
            img = img[:, x1:x2, :]
        if input_aspect_ratio < res_aspect_ratio:
            im_w_r = w
            im_h_r = int(w/input_aspect_ratio)
            img = cv2.resize(img, (im_w_r , im_h_r))
            y1 = int((im_h_r - h)/2)
            y2 = y1 + h
            img = img[y1:y2, :, :]
        if input_aspect_ratio == res_aspect_ratio:
            img = cv2.resize(img, (w, h))

        return img