C#双三次插值缩放图像
来源:互联网 发布:捷易通怎么绑定淘宝 编辑:程序博客网 时间:2024/05/21 22:30
由于图像像素的灰度值是离散的, 因此一般的处理方法是对原来在整数点坐标上的像素值进行插值生成连续的曲面, 然后在插值曲面上重新采样以获得缩放图像像素的灰度值。缩放处理从输出图像出发,采用逆向映射方法,即在输出图像中找到与之对应的输入图像中的某个或某几个像素,采用这种方法能够保证输出图像中的每个像素都有一个确定值,否则,如果从输入图像出发来推算输出图像,输出图像的像素点可能出现无灰度值的情况。因为,对图像进行缩放处理时输出图像像素和输入图像之间可能不再存在着一一对应关系。
双三次插值不仅考虑到周围四个直接相邻像素点灰度值的影响,还考虑到它们灰度值变化率的影响。在这种方法中,函数 f 在点(x, y) 的值可以通过矩形网格中最近的十六个采样点的加权平均得到,在这里需要使用两个多项式插值三次函数,每个方向使用一个。通过双三次插值可以得到一个连续的插值函数,它的一阶偏导数连续,并且交叉导数处处连续。
双三次插值核如下:
这里参考AForge.NET实现了双三次插值算法,并将实验结果与GDI+及EmguCV自带的双三次插值算法效率进行比较(缩放同一幅图像到相同尺寸,计算平均耗时,同时参考缩放效果)。
(1)双三次插值算法实现代码:
/// <summary>/// 双三次插值缩放灰度图像。/// </summary>/// <param name="image">源图像。</param>/// <param name="newWidth">新宽度。</param>/// <param name="newHeight">新高度。</param>/// <returns>缩放后的图像。</returns>public static Bitmap ResizeGrayscaleImage(Bitmap image, int newWidth, int newHeight){ // 检查源图像格式 CheckSourceFormat(image); // 锁定源图像内存 BitmapData srcData = image.LockBits( new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, image.PixelFormat); // 新建目标图像 Bitmap dstImage = CreateGrayscaleImage(newWidth, newHeight); // 锁定目标图像内存 BitmapData dstData = dstImage.LockBits( new Rectangle(0, 0, newWidth, newHeight), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); try { Resize(srcData, ref dstData); } finally { // 解锁图像内存 dstImage.UnlockBits(dstData); image.UnlockBits(srcData); } return dstImage;}/// <summary>/// 双三次插值处理缩放。/// </summary>/// <param name="srcData">源图像数据。</param>/// <param name="dstData">目标图像数据。</param>private static void ResizeProcess(BitmapData srcData, ref BitmapData dstData){ // 获取源图像数据 int srcWidth = srcData.Width; int srcHeight = srcData.Height; int srcStride = srcData.Stride; IntPtr srcPtr = srcData.Scan0; // 获取目标图像数据 int dstWidth = dstData.Width; int dstHeight = dstData.Height; int dstStride = dstData.Stride; int dstOffset = dstStride - dstWidth; IntPtr dstPtr = dstData.Scan0; // 计算比例系数 double xFactor = (double)srcWidth / dstWidth; double yFactor = (double)srcHeight / dstHeight; // 将源图像数据复制到托管内存中 int srcBytes = srcStride * srcHeight; byte[] srcGrayData = new byte[srcBytes]; Marshal.Copy(srcPtr, srcGrayData, 0, srcBytes); // 保存目标图像数据 int dstBytes = dstStride * dstHeight; byte[] dstGrayData = new byte[dstBytes]; int dst = 0; // 下标 // 源图像坐标点及系数 double ox, oy, dx, dy, k1, k2; int ox1, oy1, ox2, oy2; // 目标图像像素值 double grayValue; // 边界 int ymax = srcHeight - 1; int xmax = srcWidth - 1; #region 插值 for (int y = 0; y < dstHeight; y++) { // Y坐标 oy = (double)y * yFactor - 0.5; oy1 = (int)oy; dy = oy - (double)oy1; for (int x = 0; x < dstWidth; x++, dst++) { // X坐标 ox = (double)x * xFactor - 0.5f; ox1 = (int)ox; dx = ox - (double)ox1; // 像素值归零 grayValue = 0; for (int n = -1; n < 3; n++) { // Y系数 k1 = BiCubicInterpolator(dy - (double)n); oy2 = oy1 + n; if (oy2 < 0) oy2 = 0; if (oy2 > ymax) oy2 = ymax; for (int m = -1; m < 3; m++) { // X系数 k2 = k1 * BiCubicInterpolator((double)m - dx); ox2 = ox1 + m; if (ox2 < 0) ox2 = 0; if (ox2 > xmax) ox2 = xmax; grayValue += k2 * srcGrayData[oy2 * srcStride + ox2]; } } dstGrayData[dst] = (byte)Math.Max(0, Math.Min(255, grayValue)); } dst += dstOffset; } Marshal.Copy(dstGrayData, 0, dstPtr, dstBytes); #endregion}/// <summary>/// 双三次插值器。/// coefficient is set to -0.5./// </summary>/// <param name="x">X Value.</param>/// <returns>Bicubic cooefficient.</returns>private static double BiCubicInterpolator(double x){ if (x < 0) { x = -x; } double biCoef = 0; if (x <= 1) { biCoef = (1.5 * x - 2.5) * x * x + 1; } else if (x < 2) { biCoef = ((-0.5 * x + 2.5) * x - 4) * x + 2; } return biCoef;}/// <summary>/// 检查格式。/// </summary>/// <param name="original">图像。</param>private static void CheckSourceFormat(Bitmap original){ if ((original.PixelFormat != PixelFormat.Format8bppIndexed) || (IsGrayscale(original) == false)) { throw new Exception("Source pixel format is not supported."); }}/// <summary>/// 判断位图是不是8位灰度。/// </summary>/// <param name="original">位图。</param>/// <returns>判断结果。</returns>public static bool IsGrayscale(Bitmap original){ bool ret = false; // 检查像素格式 if (original.PixelFormat == PixelFormat.Format8bppIndexed) { ret = true; // 检查调色板 ColorPalette palette = original.Palette; Color color; for (int i = 0; i < 256; i++) { color = palette.Entries[i]; if ((color.R != i) || (color.G != i) || (color.B != i)) { ret = false; break; } } } return ret;}/// <summary>/// 新建8位灰度位图。/// </summary>/// <param name="width">长。</param>/// <param name="height">宽。</param>/// <returns>新建8位灰度位图。</returns>public static Bitmap CreateGrayscaleImage(int width, int height){ // 新建图像 Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed); // 设置灰度图像的调色板 SetGrayscalePalette(bitmap); return bitmap;}/// <summary>/// 设置灰度位图调色板。/// </summary>/// <param name="original">灰度位图。</param>public static void SetGrayscalePalette(Bitmap original){ // 检查像素格式 if (original.PixelFormat != PixelFormat.Format8bppIndexed) throw new Exception("Source image is not 8 bpp image."); // 获取调色板 ColorPalette palette = original.Palette; // init palette for (int i = 0; i < 256; i++) { palette.Entries[i] = Color.FromArgb(i, i, i); } // 设置调色板 original.Palette = palette;}
算法效率:
图1.源图像
图2.ResizeGrayscaleImage方法耗时
(2)用GDI+的双三次插值算法实现代码:
/// <summary>/// 使用GDI+缩放图像。/// </summary>/// <param name="original">要缩放的图像。</param>/// <param name="newWidth">新宽度。</param>/// <param name="newHeight">新高度。</param>/// <returns>缩放后的图像。</returns>public static Bitmap ResizeUsingGDIPlus(Bitmap original, int newWidth, int newHeight){ try { Bitmap bitmap = new Bitmap(newWidth, newHeight); Graphics graphics = Graphics.FromImage(bitmap); // 插值算法的质量 graphics.InterpolationMode = InterpolationMode.HighQualityBicubic; graphics.SmoothingMode = SmoothingMode.HighQuality; graphics.DrawImage(original, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, original.Width, original.Height), GraphicsUnit.Pixel); graphics.Dispose(); return bitmap; } catch { return null; }}
算法效率:
图3.GDI+方法耗时
(3)用EmguCV的双三次插值算法实现代码:
/// <summary>/// 使用EmguCV缩放图像。/// </summary>/// <param name="original">要缩放的图像。</param>/// <param name="newWidth">新宽度。</param>/// <param name="newHeight">新高度。</param>/// <returns>缩放后的图像。</returns>public static Bitmap ResizeUsingEmguCV(Bitmap original, int newWidth, int newHeight){ try { Emgu.CV.Image<Emgu.CV.Structure.Gray, byte> image = new Emgu.CV.Image<Emgu.CV.Structure.Gray, byte>(original); Emgu.CV.Image<Emgu.CV.Structure.Gray, byte> newImage = image.Resize( newWidth, newHeight, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC); return newImage.Bitmap; } catch { return null; }}
算法效率:
(以上代码为封装在静态类中,直接使用EmguCV要快的多,eg.这个实验耗时为封装后的三分之一)
图4.EmguCV方法耗时
- C#双三次插值缩放图像
- C#双三次插值缩放图像
- C#双三次插值缩放图像
- 图像缩放之双三次插值法
- 图像缩放之双三次插值法
- 图像缩放之双三次插值法
- 图像缩放之双三次插值法
- 数字图像缩放之双三次插值
- C#图像缩放与裁剪
- 图像灰度的双三次插值的MATLAB实现
- 图像插值算法之双三次插值
- VB&C#裁切和缩放图像
- 转载:请教:c#中实现图像缩放?
- 视线语音鼠标 5 C#图像缩放
- C#图像中心缩放与移动
- OpenCV 图像插值计算(双线性插值/双三次插值)
- 图像插值算法--最邻近、双线性、双三次插值
- 图像缩放
- 定长报文
- 主存储器、辅存、缓存与内存、硬盘的关系
- Repeater中的textbox取值和赋值
- 两段锁协议
- java内存泄露快速检查方法
- C#双三次插值缩放图像
- 使用 GDB 调试多进程程序
- 《新人怎么学嵌入式》
- java中使用protobuf序列化(反序列化)
- 面向对象的一些理论
- 浅析Android线程模型一
- php解压缩文件方法汇总
- php学习笔记(十三)时间处理与日历的实现
- Java 文件监控,实时监控文件加载