0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像処理(Lanczos, Spline36, PixelMixing)

Last updated at Posted at 2025-05-19

画像データの拡大縮小とおまけでシャープネスです

Lanczos() と Spline36() は下のサイトを参考にアルファ値も処理するように変更しました。
https://www.rainorshine.asia/2015/10/12/post2602.html

PixelMixing() はネットで概要を知り自分で実装したものです。
縮小専用です。

【サンプル画像】
ファイル共有-Windowsの機能-SMB1.0.jpg
【縮小画像】縦横1/3
sample.jpg
上段左:Lanczos
上段中:Spline36
上段右:PixelMixing
中段左:InterpolationMode.HighQualityBicubic
中段中:InterpolationMode.Bicubic
中段右:InterpolationMode.HighQualityBilinear
下段左:InterpolationMode.Bilinear
下段中:InterpolationMode.NearestNeighbor

PixelMixing と InterpolationMode.Bilinear 以外は
左端と上端のピクセルの色が濃くなり、右端と下端が薄くなっているのが分かります

Sharpness() は下のサイトを参考にしました
https://dk521123.hatenablog.com/entry/37837353#google_vignette

ImageFilter.cs
using System.Diagnostics;
using System.Drawing.Imaging;

namespace System.Drawing
{
    public static class ImageFilter
    {
        static byte ClipByte(int value)
        {
            if (value > 255)
            {
                return (byte)255;
            }

            if (value < 0)
            {
                return (byte)0;
            }

            return (byte)value;
        }

        static byte ClipByte(long value)
        {
            if (value > 255)
            {
                return (byte)255;
            }

            if (value < 0)
            {
                return (byte)0;
            }

            return (byte)value;
        }

        static byte ClipByte(double value)
        {
            if (value > 255.0)
            {
                return (byte)255;
            }

            if (value < 0.0)
            {
                return (byte)0;
            }

            return (byte)value;
        }

        const string format1_unsupported_pixel_format = "未対応の PixelFormat です ({0})";

        static double sinc(double x)
        {
            return Math.Sin(x * Math.PI) / (x * Math.PI);
        }

        static double lanczosWeight(double d, int n = 3)
        {
            return d == 0 ? 1 : (Math.Abs(d) < n ? sinc(d) * sinc(d / n) : 0);
        }

        public static Bitmap? Lanczos(Bitmap src_bitmap, int dst_width, int dst_height, out CustomError? ce, int n = 3)
        {
            int bpp;

            PixelFormat pixel_format;

            if (src_bitmap.PixelFormat == PixelFormat.Format24bppRgb
            || src_bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                bpp = 3;

                pixel_format = PixelFormat.Format24bppRgb;
            }
            else if (src_bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                bpp = 4;

                pixel_format = PixelFormat.Format32bppArgb;
            }
            else
            {
                ce = new CustomError(format1_unsupported_pixel_format, src_bitmap.PixelFormat);

                return null;
            }

            Bitmap? dst_bitmap = null;

            try
            {
                int src_width = src_bitmap.Width;
                int src_height = src_bitmap.Height;

                double wf = (double)src_width / (double)dst_width;
                double hf = (double)src_height / (double)dst_height;

                dst_bitmap = new Bitmap(dst_width, dst_height, pixel_format);

                var src_bitmapdata = src_bitmap.LockBits(new Rectangle(Point.Empty, src_bitmap.Size), ImageLockMode.ReadOnly, pixel_format);
                var dst_bitmapdata = dst_bitmap.LockBits(new Rectangle(Point.Empty, dst_bitmap.Size), ImageLockMode.WriteOnly, pixel_format);

                int src_stride = src_bitmapdata.Stride;
                int dst_stride = dst_bitmapdata.Stride;

                unsafe
                {
                    byte* src_data = (byte*)src_bitmapdata.Scan0;
                    byte* dst_data = (byte*)dst_bitmapdata.Scan0;

                    Parallel.For(0, dst_height, iy =>
                    {
                        double[] value = new double[bpp];

                        for (var ix = 0; ix < dst_width; ix++)
                        {
                            var wfx = wf * ix;
                            var wfy = hf * iy;

                            var x = (int)wfx;
                            var y = (int)wfy;

                            for (int i = 0; i < bpp; i++)
                            {
                                value[i] = 0;
                            }

                            for (int jy = y - n + 1; jy <= y + n; jy++)
                            {
                                for (int jx = x - n + 1; jx <= x + n; jx++)
                                {
                                    var w = lanczosWeight(wfx - jx, n) * lanczosWeight(wfy - jy, n);

                                    if (w == 0) continue;

                                    var sx = (jx < 0 || jx >= src_width) ? x : jx;
                                    var sy = (jy < 0 || jy >= src_height) ? y : jy;

                                    var src_pos = bpp * sx + src_stride * sy;

                                    for (int i = 0; i < bpp; i++)
                                    {
                                        value[i] += src_data[src_pos + i] * w;
                                    }
                                }
                            }

                            var dst_pos = bpp * ix + dst_stride * iy;

                            for (int i = 0; i < bpp; i++)
                            {
                                dst_data[dst_pos + i] = ClipByte(value[i]);
                            }
                        }
                    });
                }

                src_bitmap.UnlockBits(src_bitmapdata);
                dst_bitmap.UnlockBits(dst_bitmapdata);

                ce = null;

                return dst_bitmap;
            }
            catch (Exception ex)
            {
                ce = new CustomException(ex);

                if (dst_bitmap != null)
                {
                    dst_bitmap.Dispose();
                }

                return null;
            }
        }

        static double spline36Weight(double d)
        {
            d = Math.Abs(d);

            if (d < 1.0)
            {
                return (((247.0 * d - 453.0) * d - 3.0) * d + 209.0) / 209.0;
            }

            if (d < 2.0)
            {
                return (((-114.0 * d + 612.0) * d - 1038.0) * d + 540.0) / 209.0;
            }

            if (d < 3.0)
            {
                return (((19.0 * d - 159.0) * d + 434.0) * d - 384.0) / 209.0;
            }

            return 0.0;
        }

        public static Bitmap? Spline36(Bitmap src_bitmap, int dst_width, int dst_height, out CustomError? ce, int n = 3)
        {
            int bpp;

            PixelFormat pixel_format;

            if (src_bitmap.PixelFormat == PixelFormat.Format24bppRgb
            || src_bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                bpp = 3;

                pixel_format = PixelFormat.Format24bppRgb;
            }
            else if (src_bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                bpp = 4;

                pixel_format = PixelFormat.Format32bppArgb;
            }
            else
            {
                ce = new CustomError(format1_unsupported_pixel_format, src_bitmap.PixelFormat);

                return null;
            }

            Bitmap? dst_bitmap = null;

            try
            {
                int src_width = src_bitmap.Width;
                int src_height = src_bitmap.Height;

                double wf = (double)src_width / (double)dst_width;
                double hf = (double)src_height / (double)dst_height;

                dst_bitmap = new Bitmap(dst_width, dst_height, pixel_format);

                var src_bitmapdata = src_bitmap.LockBits(new Rectangle(Point.Empty, src_bitmap.Size), ImageLockMode.ReadOnly, pixel_format);
                var dst_bitmapdata = dst_bitmap.LockBits(new Rectangle(Point.Empty, dst_bitmap.Size), ImageLockMode.WriteOnly, pixel_format);

                int src_stride = src_bitmapdata.Stride;
                int dst_stride = dst_bitmapdata.Stride;

                unsafe
                {
                    byte* src_data = (byte*)src_bitmapdata.Scan0;
                    byte* dst_data = (byte*)dst_bitmapdata.Scan0;

                    Parallel.For(0, dst_height, iy =>
                    {
                        double[] value = new double[bpp];

                        for (var ix = 0; ix < dst_width; ix++)
                        {
                            var wfx = wf * ix;
                            var wfy = hf * iy;

                            var x = (int)wfx;
                            var y = (int)wfy;

                            for (int i = 0; i < bpp; i++)
                            {
                                value[i] = 0;
                            }

                            for (int jy = y - n + 1; jy <= y + n; jy++)
                            {
                                for (int jx = x - n + 1; jx <= x + n; jx++)
                                {
                                    var w = spline36Weight(wfx - jx) * spline36Weight(wfy - jy);

                                    if (w == 0) continue;

                                    var sx = (jx < 0 || jx >= src_width) ? x : jx;
                                    var sy = (jy < 0 || jy >= src_height) ? y : jy;

                                    var src_pos = bpp * sx + src_stride * sy;

                                    for (int i = 0; i < bpp; i++)
                                    {
                                        value[i] += src_data[src_pos + i] * w;
                                    }
                                }
                            }

                            var dst_pos = bpp * ix + dst_stride * iy;

                            for (int i = 0; i < bpp; i++)
                            {
                                dst_data[dst_pos + i] = ClipByte(value[i]);
                            }
                        }
                    });
                }

                src_bitmap.UnlockBits(src_bitmapdata);
                dst_bitmap.UnlockBits(dst_bitmapdata);

                ce = null;

                return dst_bitmap;
            }
            catch (Exception ex)
            {
                ce = new CustomException(ex);

                if (dst_bitmap != null)
                {
                    dst_bitmap.Dispose();
                }

                return null;
            }
        }

        public static Bitmap? PixelMixing(Bitmap src_bitmap, int dst_width, int dst_height, out CustomError? ce)
        {
            int bpp;

            PixelFormat pixel_format;

            if (src_bitmap.PixelFormat == PixelFormat.Format24bppRgb
            || src_bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                bpp = 3;

                pixel_format = PixelFormat.Format24bppRgb;
            }
            else if (src_bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                bpp = 4;

                pixel_format = PixelFormat.Format32bppArgb;
            }
            else
            {
                ce = new CustomError(format1_unsupported_pixel_format, src_bitmap.PixelFormat);

                return null;
            }

            Bitmap? dst_bitmap = null;

            try
            {
                int src_width = src_bitmap.Width;
                int src_height = src_bitmap.Height;

                double wf = (double)src_width / (double)dst_width;
                double hf = (double)src_height / (double)dst_height;

                dst_bitmap = new Bitmap(dst_width, dst_height, pixel_format);

                var src_bitmapdata = src_bitmap.LockBits(new Rectangle(Point.Empty, src_bitmap.Size), ImageLockMode.ReadOnly, pixel_format);
                var dst_bitmapdata = dst_bitmap.LockBits(new Rectangle(Point.Empty, dst_bitmap.Size), ImageLockMode.WriteOnly, pixel_format);

                int src_stride = src_bitmapdata.Stride;
                int dst_stride = dst_bitmapdata.Stride;

                unsafe
                {
                    byte* src_data = (byte*)src_bitmapdata.Scan0;
                    byte* dst_data = (byte*)dst_bitmapdata.Scan0;

                    //for (int dy = 0; dy < dst_height; dy++)
                    //{
                    Parallel.For(0, dst_height, dy =>
                    {
                        double sy1 = dy * hf;
                        double sy2 = sy1 + hf;

                        int isy1 = (int)sy1;
                        int isy2 = (int)sy2;

                        double[] value = new double[bpp];

                        for (int dx = 0; dx < dst_width; dx++)
                        {
                            double sx1 = dx * wf;
                            double sx2 = sx1 + wf;

                            int isx1 = (int)sx1;
                            int isx2 = (int)sx2;

                            for (int i = 0; i < bpp; i++)
                            {
                                value[i] = 0;
                            }

                            double w_sum = 0;

                            double wy = (1.0 - (sy1 - isy1));
                            double wx = (1.0 - (sx1 - isx1));

                            double w = wy * wx;

                            int src_pos = bpp * isx1 + src_stride * isy1;

                            for (int i = 0; i < bpp; i++)
                            {
                                value[i] += src_data[src_pos + i] * w;
                            }

                            w_sum += w;

                            for (int x = isx1 + 1; x < isx2; x++)
                            {
                                src_pos = bpp * x + src_stride * isy1;

                                for (int i = 0; i < bpp; i++)
                                {
                                    value[i] += src_data[src_pos + i] * wy;
                                }

                                w_sum += wy;
                            }

                            for (int y = isy1 + 1; y < isy2; y++)
                            {
                                src_pos = bpp * isx1 + src_stride * y;

                                for (int i = 0; i < bpp; i++)
                                {
                                    value[i] += src_data[src_pos + i] * wx;
                                }

                                w_sum += wx;
                            }

                            for (int y = isy1 + 1; y < isy2; y++)
                            {
                                for (int x = isx1 + 1; x < isx2; x++)
                                {
                                    src_pos = bpp * x + src_stride * y;

                                    for (int i = 0; i < bpp; i++)
                                    {
                                        value[i] += src_data[src_pos + i];
                                    }

                                    w_sum += 1.0;
                                }
                            }

                            if (isx2 < src_width)
                            {
                                w = sx2 - isx2;

                                for (int y = isy1 + 1; y < isy2; y++)
                                {
                                    src_pos = bpp * isx2 + src_stride * y;

                                    for (int i = 0; i < bpp; i++)
                                    {
                                        value[i] += src_data[src_pos + i] * w;
                                    }

                                    w_sum += w;
                                }
                            }

                            if (isy2 < src_height)
                            {
                                w = sy2 - isy2;

                                for (int x = isx1 + 1; x < isx2; x++)
                                {
                                    src_pos = bpp * x + src_stride * isy2;

                                    for (int i = 0; i < bpp; i++)
                                    {
                                        value[i] += src_data[src_pos + i] * w;
                                    }

                                    w_sum += w;
                                }
                            }

                            if (isx2 < src_width && isy2 < src_height)
                            {
                                w = (sy2 - isy2) * (sx2 - isx2);

                                src_pos = bpp * isx2 + src_stride * isy2;

                                for (int i = 0; i < bpp; i++)
                                {
                                    value[i] += src_data[src_pos + i] * w;
                                }

                                w_sum += w;
                            }

                            int dst_pos = bpp * dx + dst_stride * dy;

                            for (int i = 0; i < bpp; i++)
                            {
                                dst_data[dst_pos + i] = ClipByte(value[i] / w_sum);
                            }
                        }
                    });
                    //}
                }

                src_bitmap.UnlockBits(src_bitmapdata);
                dst_bitmap.UnlockBits(dst_bitmapdata);

                ce = null;

                return dst_bitmap;
            }
            catch (Exception ex)
            {
                ce = new CustomException(ex);

                if (dst_bitmap != null)
                {
                    dst_bitmap.Dispose();
                }

                return null;
            }
        }

        public static Bitmap? Sharpness(Bitmap src_bitmap, double level, out CustomError? ce)
        {
            int bpp;

            PixelFormat pixel_format;

            if (src_bitmap.PixelFormat == PixelFormat.Format24bppRgb
            || src_bitmap.PixelFormat == PixelFormat.Format8bppIndexed)
            {
                bpp = 3;

                pixel_format = PixelFormat.Format24bppRgb;
            }
            else if (src_bitmap.PixelFormat == PixelFormat.Format32bppArgb)
            {
                bpp = 4;

                pixel_format = PixelFormat.Format32bppArgb;
            }
            else
            {
                ce = new CustomError(format1_unsupported_pixel_format, src_bitmap.PixelFormat);

                return null;
            }

            Bitmap? dst_bitmap = null;

            try
            {

                int width = src_bitmap.Width;
                int height = src_bitmap.Height;

                dst_bitmap = new Bitmap(width, height, pixel_format);

                var src_bitmapdata = src_bitmap.LockBits(new Rectangle(Point.Empty, src_bitmap.Size), ImageLockMode.ReadOnly, pixel_format);
                var dst_bitmapdata = dst_bitmap.LockBits(new Rectangle(Point.Empty, dst_bitmap.Size), ImageLockMode.WriteOnly, pixel_format);

                int stride = src_bitmapdata.Stride;

                unsafe
                {
                    byte* src_data = (byte*)src_bitmapdata.Scan0;
                    byte* dst_data = (byte*)dst_bitmapdata.Scan0;

                    Parallel.For(1, height - 1, y =>
                    {
                        int dy1 = (y - 1) * stride;
                        int dy = y * stride;
                        int dy2 = (y + 1) * stride;

                        for (int x = 1; x < width - 1; x++)
                        {
                            int dx1 = (x - 1) * bpp;
                            int dx = x * bpp;
                            int dx2 = (x + 1) * bpp;

                            for (int k = 0; k < 3; k++)
                            {
                                double value = (src_data[dy + dx + k] * (1.0 + level * 4)) - ((src_data[dy1 + dx + k] + src_data[dy + dx1 + k] + src_data[dy + dx2 + k] + src_data[dy2 + dx + k]) * level);

                                dst_data[dy + dx + k] = ClipByte(value);
                            }

                            if (bpp == 4)
                            {
                                dst_data[dy + dx + 3] = src_data[dy + dx + 3];
                            }
                        }
                    });

                    int offset = stride * (height - 1);

                    for (int x = 0; x < width; x++)
                    {
                        int p1 = x * bpp;

                        for (int i = 0; i < bpp; i++)
                        {
                            dst_data[p1 + i] = src_data[p1 + i];
                        }

                        int p2 = offset + p1;

                        for (int i = 0; i < bpp; i++)
                        {
                            dst_data[p2 + i] = src_data[p2 + i];
                        }
                    }

                    offset = bpp * (width - 1);

                    for (int y = 1; y < height - 1; y++)
                    {
                        int p1 = stride * y;

                        for (int i = 0; i < bpp; i++)
                        {
                            dst_data[p1 + i] = src_data[p1 + i];
                        }

                        int p2 = offset + p1;

                        for (int i = 0; i < bpp; i++)
                        {
                            dst_data[p2 + i] = src_data[p2 + i];
                        }
                    }
                }

                src_bitmap.UnlockBits(src_bitmapdata);
                dst_bitmap.UnlockBits(dst_bitmapdata);

                ce = null;

                return dst_bitmap;
            }
            catch (Exception ex)
            {
                ce = new CustomException(ex);

                if (dst_bitmap != null)
                {
                    dst_bitmap.Dispose();
                }

                return null;
            }
        }
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?