Saltar al contenido

cambiar el tamaño del lienzo de la imagen para mantener la relación de aspecto cuadrada en Python, OpenCv

Recuerda que en la informática cualquier problema casi siempre tiene diversas resoluciones, no obstante te enseñamos lo mejor y más óptimo.

Solución:

Usando OpenCV

Puedes usar resize() en OpenCV para cambiar el tamaño de la imagen hacia arriba o hacia abajo al tamaño que necesita. Sin embargo, resize() requiere que ingrese el tamaño de destino (en ambas dimensiones) o la escala (en ambas dimensiones), por lo que no puede simplemente poner uno u otro en 1000 y dejar que calcule el otro por usted. Entonces, la forma más sólida de hacer esto es encontrar la relación de aspecto y calcular cuál sería la dimensión más pequeña cuando la más grande se estira a 1000. Luego, puede cambiar el tamaño.

h, w = img.shape[:2]
aspect = w/h

Tenga en cuenta que si aspect es mayor que 1, entonces la imagen está orientada horizontalmente, mientras que si es menor que 1, la imagen está orientada verticalmente (y es cuadrada si aspect = 1).

Los diferentes métodos de interpolación se verán mejor dependiendo de si está ampliando la imagen a una resolución más grande o reduciéndola a una resolución más baja. Desde el resize() docs:

Para reducir una imagen, generalmente se verá mejor con la interpolación CV_INTER_AREA, mientras que para agrandar una imagen, generalmente se verá mejor con CV_INTER_CUBIC (lento) o CV_INTER_LINEAR (más rápido pero aún se ve bien).

Entonces, después de cambiar el tamaño, terminaremos con un 1000xN o Nx1000 imagen (donde N<=1000) y tendremos que rellenarlo con el color de fondo que desee en ambos lados para rellenar la imagen 1000x1000. Para esto puedes usar copyMakeBorder() para una implementación pura de OpenCV, o como está usando Python, puede usar numpy.pad(). Deberá decidir qué hacer en caso de que sea necesario agregar un número impar de píxeles para convertirlo 1000x1000, como si el píxel adicional va hacia la izquierda o hacia la derecha (o hacia arriba o hacia abajo, según la orientación de su imagen).

Aquí hay un guión que define un resizeAndPad() función que calcula automáticamente la relación de aspecto, escala en consecuencia y almohadillas según sea necesario, y luego la usa en una imagen horizontal, vertical y cuadrada:

import cv2
import numpy as np

def resizeAndPad(img, size, padColor=0):

    h, w = img.shape[:2]
    sh, sw = size

    # interpolation method
    if h > sh or w > sw: # shrinking image
        interp = cv2.INTER_AREA
    else: # stretching image
        interp = cv2.INTER_CUBIC

    # aspect ratio of image
    aspect = w/h  # if on Python 2, you might need to cast as a float: float(w)/h

    # compute scaling and pad sizing
    if aspect > 1: # horizontal image
        new_w = sw
        new_h = np.round(new_w/aspect).astype(int)
        pad_vert = (sh-new_h)/2
        pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
        pad_left, pad_right = 0, 0
    elif aspect < 1: # vertical image
        new_h = sh
        new_w = np.round(new_h*aspect).astype(int)
        pad_horz = (sw-new_w)/2
        pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
        pad_top, pad_bot = 0, 0
    else: # square image
        new_h, new_w = sh, sw
        pad_left, pad_right, pad_top, pad_bot = 0, 0, 0, 0

    # set pad color
    if len(img.shape) is 3 and not isinstance(padColor, (list, tuple, np.ndarray)): # color image but only one color provided
        padColor = [padColor]*3

    # scale and pad
    scaled_img = cv2.resize(img, (new_w, new_h), interpolation=interp)
    scaled_img = cv2.copyMakeBorder(scaled_img, pad_top, pad_bot, pad_left, pad_right, borderType=cv2.BORDER_CONSTANT, value=padColor)

    return scaled_img

v_img = cv2.imread('v.jpg') # vertical image
scaled_v_img = resizeAndPad(v_img, (200,200), 127)

h_img = cv2.imread('h.jpg') # horizontal image
scaled_h_img = resizeAndPad(h_img, (200,200), 127)

sq_img = cv2.imread('sq.jpg') # square image
scaled_sq_img = resizeAndPad(sq_img, (200,200), 127)

Y esto da las imágenes:

Imagen vertical a escala

Imagen horizontal escaladaImagen cuadrada a escala

Usando ImageMagick

ImageMagick es una interfaz de línea de comandos simple pero bien construida para realizar el procesamiento básico de imágenes. Es muy fácil hacer lo que quiera con un solo comando. Consulte aquí las descripciones de los comandos de cambio de tamaño.

$ convert v.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-v-im.jpg
$ convert h.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-h-im.jpg
$ convert sq.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-sq-im.jpg

Produciendo las imágenes:

Imagen vertical a escalaImagen horizontal escaladaImagen cuadrada a escala

Sobre la base de la respuesta de Alexander-Reynolds anterior, aquí está el código que maneja todos los tamaños y situaciones posibles.

def resizeAndPad (img, tamaño, padColor = 255):

h, w = img.shape[:2]
sh, sw = size

# interpolation method
if h > sh or w > sw: # shrinking image
    interp = cv2.INTER_AREA

else: # stretching image
    interp = cv2.INTER_CUBIC

# aspect ratio of image
aspect = float(w)/h 
saspect = float(sw)/sh

if (saspect > aspect) or ((saspect == 1) and (aspect <= 1)):  # new horizontal image
    new_h = sh
    new_w = np.round(new_h * aspect).astype(int)
    pad_horz = float(sw - new_w) / 2
    pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
    pad_top, pad_bot = 0, 0

elif (saspect < aspect) or ((saspect == 1) and (aspect >= 1)):  # new vertical image
    new_w = sw
    new_h = np.round(float(new_w) / aspect).astype(int)
    pad_vert = float(sh - new_h) / 2
    pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
    pad_left, pad_right = 0, 0

# set pad color
if len(img.shape) is 3 and not isinstance(padColor, (list, tuple, np.ndarray)): # color image but only one color provided
    padColor = [padColor]*3

# scale and pad
scaled_img = cv2.resize(img, (new_w, new_h), interpolation=interp)
scaled_img = cv2.copyMakeBorder(scaled_img, pad_top, pad_bot, pad_left, pad_right, borderType=cv2.BORDER_CONSTANT, value=padColor)

return scaled_img

valoraciones y reseñas

Eres capaz de añadir valor a nuestra información cooperando tu veteranía en los comentarios.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *