Favicon

Resize image

Peponi2/12/20257m

C#
NugetPackageOpenCvSharp4

1. Introduction

OpenCvSharp4는 이미지 크기 조절 기능을 제공하는데, Cv2.Resize()Cv2.PyrUp(), Cv2.PyrDown()을 제공한다. 여기서는 Cv2.Resize()에 대해 알아본다.

Cv2.Resize()는 사이즈 변경과 함께 다양한 픽셀 보간 방법을 제공한다. 사이즈 변경은 원하는 특정 크기 또는 스케일 팩터 적용이 가능하다. 픽셀 보간 방법은 기본적으로 InterpolationFlags.Linear가 적용되어 있으며 자세한 리스트는 아래 표를 참조한다.

InterpolationFlagsDescription
NearestNearest-neighbor interpolation
LinearBilinear interpolation (used by default)
CubicBicubic interpolation.
AreaResampling using pixel area relation. It is the preferred method for image decimation that gives moire-free results. In case of zooming it is similar to CV_INTER_NN method.
Lanczos4Lanczos interpolation over 8x8 neighborhood
LinearExactBit exact bilinear interpolation
Maxmask for interpolation codes
WarpFillOutliersFill all the destination image pixels. If some of them correspond to outliers in the source image, they are set to fillval.
WarpInverseMapIndicates that matrix is inverse transform from destination image to source and, thus, can be used directly for pixel interpolation. Otherwise, the function finds the inverse transform from map_matrix.

OpenCV docs에 의하면 이미지 축소를 할 때는 보통 InterpolationFlags.Area가 최상의 결과를 가져오며, 확대 시에는 InterpolationFlags.Cubic이 가장 결과가 좋다고 한다.

To shrink an image, it will generally look best with INTER_AREA interpolation, whereas to enlarge an image, it will generally look best with INTER_CUBIC (slow) or INTER_LINEAR (faster but still looks OK).

2. Sampling image

Picture by Manfred Richter
(좌측부터 original, upsampled, downsampled)

여기서는 이미지 로드 후 스케일을 변경하는 방법을 알아본다.

using OpenCvSharp;
using OpenCvSharp.Extensions;
 
namespace ResizeImage;
 
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        this.Controls.Add(AddComponents());
    }
 
    private void Load_Click(object? sender, EventArgs e)
    {
        OpenFileDialog dialog = new()
        {
            Filter = "All files|*.*",
            InitialDirectory = $@"C:\",
            CheckPathExists = true,
            CheckFileExists = true
        };
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            // Image load
            using var original = Cv2.ImRead(dialog.FileName);
 
            // Sampling
            // Mat.Resize(targetSize, scaleFactorX, scaleFactorY, InterpolationFlags)
            // targetSize가 0일 때는 호출자 (여기서는 original) 의 크기 사용
            using var upsampled = original.Resize(new(), 4, 4);
            using var downsampled = original.Resize(new(), 0.25, 0.25);
 
            SetImage(nameof(original), original);
            SetImage(nameof(upsampled), upsampled);
            SetImage(nameof(downsampled), downsampled);
        }
    }
 
    private TableLayoutPanel AddComponents()
    {
        PictureBox original = new()
        {
            Name = nameof(original),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox upsampled = new()
        {
            Name = nameof(upsampled),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox downsampled = new()
        {
            Name = nameof(downsampled),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        Button load = new()
        {
            Name = nameof(load),
            Dock = DockStyle.Fill,
            Text = nameof(load),
        };
        load.Click += Load_Click;
 
        TableLayoutPanel panel = new()
        {
            ColumnCount = 3,
            RowCount = 2,
            Dock = DockStyle.Fill,
        };
        panel.ColumnStyles.Add(new(SizeType.Percent, 33.34F));
        panel.ColumnStyles.Add(new(SizeType.Percent, 33.33F));
        panel.ColumnStyles.Add(new(SizeType.Percent, 33.33F));
        panel.RowStyles.Add(new(SizeType.Percent, 95));
        panel.RowStyles.Add(new(SizeType.Percent, 5));
 
        panel.Controls.Add(original, 0, 0);
        panel.Controls.Add(upsampled, 1, 0);
        panel.Controls.Add(downsampled, 2, 0);
        panel.Controls.Add(load, 0, 1);
 
        return panel;
    }
 
    private void SetImage(string controlName, Mat image)
    {
        if (this.Controls.Find(controlName, true).First() is PictureBox box)
        {
            box.Image = image.ToBitmap();
        }
    }
}

3. Upsampling

Upsampled
(좌측부터 Nearest, Linear, Cubic, Area, Lanczos4)

여기서는 몇몇 보간 방법을 적용하여 이미지의 크기를 키우고 결과를 확인한다.

using OpenCvSharp;
using OpenCvSharp.Extensions;
 
namespace ResizeImage;
 
public class Form2 : Form
{
    public Form2()
    {
        this.Size = new System.Drawing.Size(1280, 768);
        this.Controls.Add(AddComponents());
    }
 
    private void Load_Click(object? sender, EventArgs e)
    {
        OpenFileDialog dialog = new()
        {
            Filter = "All files|*.*",
            InitialDirectory = $@"C:\",
            CheckPathExists = true,
            CheckFileExists = true
        };
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            // Image load
            using var original = Cv2.ImRead(dialog.FileName);
 
            // Sampling
            // Mat.Resize(targetSize, scaleFactorX, scaleFactorY, InterpolationFlags)
            // targetSize가 0일 때는 호출자 (여기서는 original) 의 크기 사용
            using var nearest = original.Resize(new(), 4, 4, InterpolationFlags.Nearest);
            using var linear = original.Resize(new(), 4, 4, InterpolationFlags.Linear);
            using var cubic = original.Resize(new(), 4, 4, InterpolationFlags.Cubic);
            using var area = original.Resize(new(), 4, 4, InterpolationFlags.Area);
            using var lanczos4 = original.Resize(new(), 4, 4, InterpolationFlags.Lanczos4);
 
            SetImage(nameof(original), original);
            SetImage(nameof(nearest), nearest);
            SetImage(nameof(linear), linear);
            SetImage(nameof(cubic), cubic);
            SetImage(nameof(area), area);
            SetImage(nameof(lanczos4), lanczos4);
        }
    }
 
    private TableLayoutPanel AddComponents()
    {
        PictureBox original = new()
        {
            Name = nameof(original),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox nearest = new()
        {
            Name = nameof(nearest),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox linear = new()
        {
            Name = nameof(linear),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox cubic = new()
        {
            Name = nameof(cubic),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox area = new()
        {
            Name = nameof(area),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox lanczos4 = new()
        {
            Name = nameof(lanczos4),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        Button load = new()
        {
            Name = nameof(load),
            Dock = DockStyle.Fill,
            Text = nameof(load),
        };
        load.Click += Load_Click;
 
        TableLayoutPanel panel = new()
        {
            ColumnCount = 5,
            RowCount = 2,
            Dock = DockStyle.Fill,
        };
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.RowStyles.Add(new(SizeType.Percent, 50));
        panel.RowStyles.Add(new(SizeType.Percent, 50));
 
        panel.Controls.Add(original, 2, 0);
        panel.Controls.Add(load, 4, 0);
        panel.Controls.Add(nearest, 0, 1);
        panel.Controls.Add(linear, 1, 1);
        panel.Controls.Add(cubic, 2, 1);
        panel.Controls.Add(area, 3, 1);
        panel.Controls.Add(lanczos4, 4, 1);
 
        return panel;
    }
 
    private void SetImage(string controlName, Mat image)
    {
        if (this.Controls.Find(controlName, true).First() is PictureBox box)
        {
            box.Image = image.ToBitmap();
        }
    }
}

4. Downsampling

Downsampled
(좌측부터 Nearest, Linear, Cubic, Area, Lanczos4)

여기서는 몇몇 보간 방법을 적용하여 이미지의 크기를 낮추고 결과를 확인한다.

using OpenCvSharp;
using OpenCvSharp.Extensions;
 
namespace ResizeImage;
 
public class Form3 : Form
{
    public Form3()
    {
        this.Size = new System.Drawing.Size(1280, 768);
        this.Controls.Add(AddComponents());
    }
 
    private void Load_Click(object? sender, EventArgs e)
    {
        OpenFileDialog dialog = new()
        {
            Filter = "All files|*.*",
            InitialDirectory = $@"C:\",
            CheckPathExists = true,
            CheckFileExists = true
        };
        if (dialog.ShowDialog() == DialogResult.OK)
        {
            // Image load
            using var original = Cv2.ImRead(dialog.FileName);
 
            // Sampling
            // Mat.Resize(targetSize, scaleFactorX, scaleFactorY, InterpolationFlags)
            // targetSize가 0일 때는 호출자 (여기서는 original) 의 크기 사용
            using var nearest = original.Resize(new(), 0.5, 0.5, InterpolationFlags.Nearest);
            using var linear = original.Resize(new(), 0.5, 0.5, InterpolationFlags.Linear);
            using var cubic = original.Resize(new(), 0.5, 0.5, InterpolationFlags.Cubic);
            using var area = original.Resize(new(), 0.5, 0.5, InterpolationFlags.Area);
            using var lanczos4 = original.Resize(new(), 0.5, 0.5, InterpolationFlags.Lanczos4);
 
            SetImage(nameof(original), original);
            SetImage(nameof(nearest), nearest);
            SetImage(nameof(linear), linear);
            SetImage(nameof(cubic), cubic);
            SetImage(nameof(area), area);
            SetImage(nameof(lanczos4), lanczos4);
        }
    }
 
    private TableLayoutPanel AddComponents()
    {
        PictureBox original = new()
        {
            Name = nameof(original),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox nearest = new()
        {
            Name = nameof(nearest),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox linear = new()
        {
            Name = nameof(linear),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox cubic = new()
        {
            Name = nameof(cubic),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox area = new()
        {
            Name = nameof(area),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        PictureBox lanczos4 = new()
        {
            Name = nameof(lanczos4),
            Dock = DockStyle.Fill,
            SizeMode = PictureBoxSizeMode.StretchImage
        };
        Button load = new()
        {
            Name = nameof(load),
            Dock = DockStyle.Fill,
            Text = nameof(load),
        };
        load.Click += Load_Click;
 
        TableLayoutPanel panel = new()
        {
            ColumnCount = 5,
            RowCount = 2,
            Dock = DockStyle.Fill,
        };
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.ColumnStyles.Add(new(SizeType.Percent, 20));
        panel.RowStyles.Add(new(SizeType.Percent, 50));
        panel.RowStyles.Add(new(SizeType.Percent, 50));
 
        panel.Controls.Add(original, 2, 0);
        panel.Controls.Add(load, 4, 0);
        panel.Controls.Add(nearest, 0, 1);
        panel.Controls.Add(linear, 1, 1);
        panel.Controls.Add(cubic, 2, 1);
        panel.Controls.Add(area, 3, 1);
        panel.Controls.Add(lanczos4, 4, 1);
 
        return panel;
    }
 
    private void SetImage(string controlName, Mat image)
    {
        if (this.Controls.Find(controlName, true).First() is PictureBox box)
        {
            box.Image = image.ToBitmap();
        }
    }
}

5. 참조 자료