web-dev-qa-db-ja.com

Python、OpenCvで正方形のアスペクト比を維持するために画像キャンバスのサイズを変更する

入力がアスペクト比を失わないように、任意の入力画像からPythonで1000x 1000の画像を取得したいのですが、言い換えると、入力のサイズを変更して次のようにします。その長い方の寸法は1000ピクセルで、もう一方の寸法を背景色で1000 x 1000の正方形になるまで「塗りつぶし」ます。オリジナルは、最後の中央にある必要があります。

7
Hendrik

OpenCVの使用

OpenCVで resize() を使用して、画像を必要なサイズに上下にサイズ変更できます。ただし、resize()では、宛先サイズ(両方の次元)またはスケーリング(両方の次元)のいずれかを入力する必要があるため、どちらか一方を1000に入力して、計算させることはできません。あなたのための他。したがって、これを行う最も堅牢な方法は、アスペクト比を見つけて、大きい方の寸法を1000に伸ばしたときに小さい方の寸法がどうなるかを計算することです。その後、サイズを変更できます。

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

aspectが1より大きい場合、画像は水平方向に向けられ、1未満の場合、画像は垂直方向に向けられます(_aspect = 1_の場合は正方形)。

画像をより大きな解像度に拡大するか、より低い解像度に縮小するかに応じて、さまざまな補間方法が見栄えが良くなります。 resize()ドキュメントから:

画像を縮小するには、通常、CV_INTER_AREA補間を使用すると最もよく見えますが、画像を拡大するには、通常、CV_INTER_CUBIC(遅い)またはCV_INTER_LINEAR(高速ですが、それでも問題ないように見えます)を使用すると最もよく見えます。

したがって、サイズを変更した後、最終的に_1000xN_または_Nx1000_画像(ここで_N<=1000_)になり、両側に塗りつぶしたい背景色で埋める必要があります画像を_1000x1000_に。これには、純粋なOpenCV実装に copyMakeBorder() を使用できます。または、Pythonを使用しているため、 numpy.pad() 。追加のピクセルを左に移動するか、左に移動するかなど、奇数のピクセルを追加して_1000x1000_にする必要がある場合の対処方法を決定する必要があります。右(または画像の向きに応じて上または下)。

アスペクト比を自動的に計算し、それに応じてスケーリングし、必要に応じてパディングし、それを水平、垂直、および正方形の画像で使用するresizeAndPad()関数を定義するスクリプトを次に示します。

_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)
_

そしてこれは画像を与えます:

Scaled vertical imageScaled horizontal imageScaled square image

ImageMagickの使用

ImageMagickはシンプルですが、基本的な画像処理を行うための十分に構築されたコマンドラインインターフェイスです。 1つのコマンドでやりたいことをするのはとても簡単です。サイズ変更コマンドの説明については、 ここ を参照してください。

_$ 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
_

画像の作成:

Scaled vertical imageScaled horizontal imageScaled square image

24
alkasm

上記のAlexander-Reynoldsの回答に基づいて、考えられるすべてのサイズと状況を処理するコードを次に示します。

def resizeAndPad(img、size、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
1
London guy