图像处理 最近邻插值

2020-03-04 14:16:43 qq_41140138 阅读数 287
  • 语义分割-上采样方法:去池化、反卷积

    1.熟悉Flink大数据生态圈以及未来发展趋势 2.快速上手开发Flink批处理程序和流处理程序 3.掌握了Flink核心知识与编程模型,深入理解Flink计算框架 4.掌握了Flink HA分布式集群安装部署

    155人学习 白勇
    免费试看

一、插值与图像缩放

  首先举个例子说明插值过程,先看看matlab的插值函数 interp() 吧:

x = -2 : 1 : 2;
y = -2 : 1 : 2;
[X, Y] = meshgrid(x, y);
Z = -X.^2 - Y.^2;

p = -2 : 0.33 : 2;
q = -2 : 0.33 : 2;
[P, Q] = meshgrid(p, q);
V1 = interp2(X, Y, Z, P, Q, 'nearest'); % 最邻近插值
V2 = interp2(X, Y, Z, P, Q, 'linear'); % 双线性插值
V3 = interp2(X, Y, Z, P, Q, 'spline'); % 球面插值

figure
subplot(221)
surf(X, Y, Z);
colormap('autumn')
title('z = x^2 + y^2')

subplot(222)
surf(P, Q, V1);
colormap('autumn')
title('z = x^2 + y^2,最邻近插值')

subplot(223)
surf(P, Q, V2);
colormap('autumn')
title('z = x^2 + y^2,是双线性插值')

subplot(224)
surf(P, Q, V3);
colormap('autumn')
title('z = x^2 + y^2,球面插值')

我们先绘制一个粗糙的曲面,然后通过不同的方式插入一些点,得到的图像如下:
在这里插入图片描述
  可见,不同的插值效果不一样,图一很粗糙,绘图点数少,图二为最邻近插值,得到类似 ‘阶跃’ 的效果,而图三图四的曲面过去看起来更加圆滑。类比到图像,使用最邻近插值得到的图像相当于把像素点放大了,而使用双线性插值和球面插值相当于让图像像素点之间过度更加平滑。



二、最近邻插值

1、原理

最近邻插值,是将距离目标点最近的像素点的值作为插值的值
在这里插入图片描述
  如图为将一张MxN的图像src放大到PxQ的图像dst,我们要创建一个新的PXQ的数组,像素点中随便取一个点A(i, j),假设我们使用的灰度图,那么A点的灰度值应该是多少呢?
  首先我们把P点的坐标映射到原图像中(不知道说映射贴不贴切 😂),假设是B点,那么我们可以类比相似三角形的相似性质计算出B点的坐标(x, y):(x, y) = (M/P*i, N/Q*j)
由于数组的索引只能是整数,所以需要将x和y化为整数,使用最近邻插值时,我们可以四舍五入:

x = round(M/P*i);
y = round(N/Q*j));
dst(i, j) = src(x, y);

  于是我们得到了计算新图像每个像素点灰度值的方法,来一个循环就可以构建新图像。(对于rgb类型的图像,我们可以使用矩阵运算,一次性对rgb三个分量进行插值)


2、代码实现

话不多说,我们看看代码效果吧~
注意: 代码的第十行 dst = zeros(row, col, color, class(src)); 创建了一个row*col的彩色图像,类型是uint8

clear all
src = imread('lena_color_256.tif');

ratio = 3;
[row, col, color] = size(src);
row = round(ratio * row);
col = round(ratio * col);

% 生成新的图片
dst = zeros(row, col, color, class(src));

for i = 1 : row
    for j = 1 : col
        x = round(i / ratio);
        y = round(j / ratio);
        if x == 0
            x = x + 1;
        end
        if y == 0
            y = y + 1;
        end
        dst(i, j, : ) = src(x, y, :);
    end
end

figure
imshow(src)
figure
imshow(dst)

原图像为256x256的彩色图像:
在这里插入图片描述
使用最邻近插值后变成大小为768x768的彩色图(建议点击放大观察):
在这里插入图片描述



三、双线性插值

1、原理

  双线性插值不是只考虑距离最近的像素点,而是考虑周围四个像素点的加权值
在这里插入图片描述
  如图,假设ABCD均为相邻的像素点,而(x, y)落在中间
  插值时考虑距离的影响,离像素点越远,则权值越小,比如说图中的A点,考虑到ABCD为边长为1的正方形,可以用(1-dx)(1-dy)表示A点灰度值的权值(详细的证明可以参考大佬的博客),其中dx和dy分别表示x和y方向的距离
  假设ABCD处的灰度分别是a, b, c和d,于是我们可以得到(x, y)处的灰度应该是:
intensity=(1dx)(1dy)a+(1dx)dyb+dx(1dy)c+dxdyd) intensity = (1-dx)(1-dy)a + (1-dx)dy b + dx(1-dy) c + dxdy d)
  在matlab中,ABCD的坐标可以勇敢取整函数来获得,注意在matlab中,图像的数组从1开始,应该用向上取整,并且考虑数组越界

设 A(x1, y1), B(x1, y2), C(x2, y1), D(x2, y2)

x1 = ceil(M/P*i);
y1 = ceil(N/Q*j);
x2 = ceil(M/P*i) + 1;
y2 = ceil(N/Q*j) + 1;

2、代码实现

原图像为256x256的彩色,使用双线性插值:
需要注意的点

  • 数组越界判断
  • 图像下标是从1开始的,所以直接使用向上取整
clear all
src = imread('lena_color_256.tif');

ratio = 3;
[row, col, color] = size(src);
row = round(ratio * row);
col = round(ratio * col);

% 生成新的图片
dst = zeros(row, col, color, class(src));

for i = 1 : row
    for j = 1 : col
        x = i / ratio;
        y = j / ratio;
        x1 = ceil(x);         % 向上取整
        y1 = ceil(y);
        x2 = ceil(x) + 1;
        y2 = ceil(y) + 1;
        if x2 >= size(src, 1)           % 溢出检查
            x2 = x2 - 1;
        end
        if y2 >= size(src, 2)
            y2 = x2 - 1;
        end
        du = (x+1) - x1;
        dv = (y+1) - y1;
        dst(i, j, :) = (1-du)*(1-dv)*src(x1, y1, :) + (1-du)*dv*src(x1, y2, :) + du*(1-dv)*src(x2, y1, :) + du*dv*src(x2, y2, :);
    end
end

figure
imshow(src)
figure
imshow(dst)

结果为768x768的图像(建议点击放大观察):
在这里插入图片描述

注意对比两种插值方式的差别

  • 使用最近邻插值得到的图像由于相邻像素点的值可能一样,所以图像类似加上了马赛克,可以类比函数interp2对曲面的插值效果
  • 使用双线性插值法得到的图像看起来更光滑,但是由于双线性插值后,图像失去了部分高频成分,所以图像看起来有点模糊

完结 🍻~
发现数字图像处理还是很有趣的 ( ̄︶ ̄)↗

2018-09-17 19:37:13 xuan_zizizi 阅读数 8211
  • 语义分割-上采样方法:去池化、反卷积

    1.熟悉Flink大数据生态圈以及未来发展趋势 2.快速上手开发Flink批处理程序和流处理程序 3.掌握了Flink核心知识与编程模型,深入理解Flink计算框架 4.掌握了Flink HA分布式集群安装部署

    155人学习 白勇
    免费试看

最近在复习图像算法,对于一些简单的图像算法进行一个代码实现,由于找工作比较忙,具体原理后期补上,先上代码。今天先给出最近邻插值,双线性插值,三次插值。
1.最近邻插值
原始图中影响点数为1
(1)代码

# include<iostream>
# include<opencv2/highgui/highgui.hpp>

using namespace std;
using namespace cv;

Mat NearInter(Mat &srcImage, double kx, double ky)
{
	int rows = cvRound(srcImage.rows*kx);
	int cols = cvRound(srcImage.cols*ky);
	Mat resultImg(rows, cols, srcImage.type());
	int i, j, x, y;
	for (i = 0; i < rows; i++)
	{
		x = static_cast<int>((i + 1) / kx + 0.5) - 1;
		for (j = 0; j < cols; j++)
		{
			y = static_cast<int>((j + 1) / ky + 0.5) - 1;
			resultImg.at<Vec3b>(i, j) = srcImage.at<Vec3b>(x, y);
		}
	}
	return resultImg;
}

int main()
{
	Mat srcImg = imread("D:\\Visual Studio 2015\\lena.bmp");
	Mat resultImg = NearInter(srcImg, 0.6, 1.2);
	imshow("src", srcImg);
	imshow("0.6,1.2", resultImg);
	waitKey(0);
	return 0;
}

(2)结果:
在这里插入图片描述
2.线性插值
与原图中相关的点数为4
(1)代码

# include<iostream>
# include<opencv2/highgui/highgui.hpp>

using namespace cv;

Mat LinerInter(Mat &srcImage, double kx, double ky);

int main()
{
	Mat srcImg = imread("D:\\Visual Studio 2015\\lena.bmp");
	Mat resultImg = LinerInter(srcImg, 0.6, 1.2);
	imshow("src", srcImg);
	imshow("0.6, 1.2", resultImg);
	waitKey(0);
	return 0;
}

Mat LinerInter(Mat &srcImage, double kx, double ky)
{
	int rows = cvRound(srcImage.rows*kx);
	int cols = cvRound(srcImage.cols*ky);
	Mat resultImg(rows, cols, srcImage.type());
	int i, j;
	int xi;
	int yi;
	int x11;
	int y11;
	double xm;
	double ym;
	double dx;
	double dy;

	for (i = 0; i < rows; i++)
	{
		xm = i / kx;
		xi = (int)xm;
		x11 = xi + 1;
		dx = xm - xi;
		for (j = 0; j < cols; j++)
		{
			ym = j / ky;
			yi = (int)ym;
			y11 = yi + 1;
			dy = ym - yi;
			//判断边界
			if (x11 >(srcImage.rows - 1))
			{
				x11 = xi - 1;
			}
			if (y11 > (srcImage.cols - 1))
			{
				y11 = yi - 1;
			}
			//bgr
			resultImg.at<Vec3b>(i, j)[0] = (int)(srcImage.at<Vec3b>(xi, yi)[0] * (1 - dx)*(1 - dy)
				+ srcImage.at<Vec3b>(x11, yi)[0] * dx*(1 - dy)
				+ srcImage.at<Vec3b>(xi, y11)[0] * (1 - dx)*dy
				+ srcImage.at<Vec3b>(x11, y11)[0] * dx*dy);
			resultImg.at<Vec3b>(i, j)[1] = (int)(srcImage.at<Vec3b>(xi, yi)[1] * (1 - dx)*(1 - dy)
				+ srcImage.at<Vec3b>(x11, yi)[1] * dx*(1 - dy)
				+ srcImage.at<Vec3b>(xi, y11)[1] * (1 - dx)*dy
				+ srcImage.at<Vec3b>(x11, y11)[1] * dx*dy);
			resultImg.at<Vec3b>(i, j)[2] = (int)(srcImage.at<Vec3b>(xi, yi)[2] * (1 - dx)*(1 - dy)
				+ srcImage.at<Vec3b>(x11, yi)[2] * dx*(1 - dy)
				+ srcImage.at<Vec3b>(xi, y11)[2] * (1 - dx)*dy
				+ srcImage.at<Vec3b>(x11, y11)[2] * dx*dy);
		}

	}
	return resultImg;
}

(2)结果:
在这里插入图片描述
3.三次插值
基于bicubic函数的权值计算,取a = -0.5
在这里插入图片描述
与原图相关的点数为16
(1)代码

# include<iostream>
# include<cmath>
# include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;

float Bicubic(float x);
Mat ThreeInter(Mat &srcImage, double kx, double ky);

int main()
{
	Mat srcImg = imread("D:\\Visual Studio 2015\\lena.bmp");
	if (!srcImg.data)
	{
		cout << "图片不存在" << endl;
	}
	Mat resultImg = ThreeInter(srcImg, 0.6, 1.2);
	imshow("src", srcImg);
	imshow("0.6,1.2", resultImg);
	waitKey(0);
	return 0;
}


float Bicubic(float y)
{
	float x = abs(y);
	float a = -0.5;
	if (x <= 1.0)
	{
		return (a + 2)*pow(x, 3) - (a + 3)*pow(x, 2) + 1;
	}
	else if (x < 2.0)
	{
		return a*pow(x, 3) + 5 * a*pow(x, 2) - 4 * a;
	}
	else
	{
		return 0.0;
	}
}

Mat ThreeInter(Mat &srcImage, double kx, double ky)
{
	int rows = cvRound(srcImage.rows * kx);
	int cols = cvRound(srcImage.cols * ky);
	Mat resultImg(rows, cols, srcImage.type());
	int i, j;
	int xm, ym;
	int x0, y0, xi, yi, x1, y1, x2, y2;
	float wx0, wy0, wxi, wyi, wx1, wy1, wx2, wy2;
	float w00, w01, w02, w0i, w10, w11, w12, w1i, w20, w21, w22, w2i, wi0, wi1, wi2, wii;
	for (i = 0; i < rows; i++)
	{
		xm = i / kx;
		xi = (int)xm;
		x0 = xi - 1;
		x1 = xi + 1;
		x2 = xi + 2;
		wx0 = Bicubic(x0 - xm);
		wxi = Bicubic(xi - xm);
		wx1 = Bicubic(x1 - xm);
		wx2 = Bicubic(x2 - xm);
		for (j = 0; j < cols; j++)
		{
			ym = j / ky;
			yi = (int)ym;
			y0 = yi - 1;
			y1 = yi + 1;
			y2 = yi + 2;
			wy0 = Bicubic(y0 - ym);
			wyi = Bicubic(yi - ym);
			wy1 = Bicubic(y1 - ym);
			wy2 = Bicubic(y2 - ym);
			w00 = wx0*wy0;
			w01 = wx0*wy1;
			w02 = wx0*wy2;
			w0i = wx0*wyi;
			w10 = wx1*wy0;
			w11 = wx1*wy1;
			w12 = wx1*wy2;
			w1i = wx1*wyi;
			w20 = wx2*wy0;
			w21 = wx2*wy1;
			w22 = wx2*wy2;
			w2i = wx2*wyi;
			wi0 = wxi*wy0;
			wi1 = wxi*wy1;
			wi2 = wxi*wy2;
			wii = wxi*wyi;
			if ((x0 >= 0) && (x2 < srcImage.rows) && (y0 >= 0) && (y2 < srcImage.cols))
			{
				resultImg.at<Vec3b>(i, j) = (srcImage.at<Vec3b>(x0, y0)*w00 + srcImage.at<Vec3b>(x0, y1)*w01 + srcImage.at<Vec3b>(x0, y2)*w02 + srcImage.at<Vec3b>(x0, yi)*w0i
					+ srcImage.at<Vec3b>(x1, y0)*w10 + srcImage.at<Vec3b>(x1, y1)*w11 + srcImage.at<Vec3b>(x1, y2)*w12 + srcImage.at<Vec3b>(x1, yi)*w1i
					+ srcImage.at<Vec3b>(x2, y0)*w20 + srcImage.at<Vec3b>(x2, y1)*w21 + srcImage.at<Vec3b>(x2, y2)*w22 + srcImage.at<Vec3b>(x2, yi)*w2i
					+ srcImage.at<Vec3b>(xi, y0)*wi0 + srcImage.at<Vec3b>(xi, y1)*wi1 + srcImage.at<Vec3b>(xi, y2)*wi2 + srcImage.at<Vec3b>(xi, yi)*wii);
			}
		}
	}
	return resultImg;
}

(2)结果
在这里插入图片描述
推荐参考博客:https://me.csdn.net/linqianbi

2019-06-02 16:37:58 tesla233 阅读数 925
  • 语义分割-上采样方法:去池化、反卷积

    1.熟悉Flink大数据生态圈以及未来发展趋势 2.快速上手开发Flink批处理程序和流处理程序 3.掌握了Flink核心知识与编程模型,深入理解Flink计算框架 4.掌握了Flink HA分布式集群安装部署

    155人学习 白勇
    免费试看
function nearest_neighbor = nearest_neighbor( filename,N )
%最近邻插值
%输入图像文件及放大倍数
%输出根据放大倍数变化后的新图像
ima=imread(filename);

[row,col,color]=size(ima);
row_n=round(row*N);
col_n=round(col*N);

ima_new=zeros(row_n,col_n,color,class(ima));

for i=1:row_n
    for j=1:col_n
        for n=1:color
            m=round(i/N);
            k=round(j/N);
            if m<1
                m=1;
            end
            if k<1
                k=1;
            end
            if m>row
                m=row;
            end
            if k>col
                k=col;
            end
            ima_new(i,j,n)=ima(m,k,n);
        end
    end
end

imwrite(ima_new,'C:\Users\10594\Desktop\new_filename.jpg');

end

 

2019-05-19 21:27:23 qq_41879767 阅读数 356
  • 语义分割-上采样方法:去池化、反卷积

    1.熟悉Flink大数据生态圈以及未来发展趋势 2.快速上手开发Flink批处理程序和流处理程序 3.掌握了Flink核心知识与编程模型,深入理解Flink计算框架 4.掌握了Flink HA分布式集群安装部署

    155人学习 白勇
    免费试看
	关于图像处理的最近邻差值算法主要用于图像的放大和缩小处理,但这种算法具有放大缩小易出现失				
	真现象,不具有普适性,且可以说是图像处理的初级操作。
    针对m×n的图像,我们如果想要对它实现p×q的格式操作,我们通过对图像像素的某个点进行处理即可。
    首先我们需要用zeros创建p×q的矩阵空间来作为目标图像的显示。
    我们可以把源图像和目标图像理解为一个小矩形,具有宽度和高度。
    那么这两个图像在x和y方向上存在着比例放大的关系,而我们恰恰是利用这种比例放大的关系来实现              
    图像矩阵的转换。
    我们设定水平方向的增量为d1 = m/p,垂直方向的增量为d2 = n/q;
    那么我们在代码中只需要对数组下标进行操作即可。
    这里需要说明,在图像中从MATLAB中读取进来的格式可能是M*N*P的行列页形式,那么这种情况,可以
    将页分割开来,利用一般的矩阵操作,然后对每一页中的二维数组进行处理,实现该页元素的叠加,最后将这三页相加即可还原为原图的效果。或者不对页分割直接处理。
%%最近邻插值算法
function   zuijinlin(I,t1,t2)
%t1为宽度,t2为高度,y是目标图像
    [m,n,k1] = size(I);
    d1 = m/t1;
    d2 = n/t2;
    %I = im2double(I);
    y = zeros(t1,t2,k1);
    for k = 1:k1
        for i = 1:m
            for j = 1:n
            y(ceil(i*d1),ceil(j*d2),k) = I(i,j,k);   
             y = im2uint8(y);
            end
        end
    end
    figure(1);
%   I = im2uint8(I);
    imshow(I);
    title('原图');
    figure(2);
    imshow(y);
    title('最近邻插值算法处理');
    
2018-11-19 16:50:42 u010608296 阅读数 1433
  • 语义分割-上采样方法:去池化、反卷积

    1.熟悉Flink大数据生态圈以及未来发展趋势 2.快速上手开发Flink批处理程序和流处理程序 3.掌握了Flink核心知识与编程模型,深入理解Flink计算框架 4.掌握了Flink HA分布式集群安装部署

    155人学习 白勇
    免费试看

图片缩放的两种常见算法:

最近邻域内插法(Nearest Neighbor interpolation)

双向性内插法(bilinear interpolation)

本文主要讲述最近邻插值(Nearest Neighbor interpolation算法的原理以及python实现

基本原理
最简单的图像缩放算法就是最近邻插值。顾名思义,就是将目标图像各点的像素值设为源图像中与其最近的点。算法优点在与简单、速度快。

如下图所示,一个4*4的图片缩放为8*8的图片。步骤:

1. 生成一张空白的8*8的图片,然后在缩放位置填充原始图片值(可以这么理解)

2. 在图片的未填充区域(黑色部分),填充为原有图片最近的位置的像素值。

è¿éåå¾çæè¿°

实现代码如下:
def nearest_neighbor_resize(img, new_w, new_h):
    # height and width of the input img
    h, w = img.shape[0], img.shape[1]
    # new image with rgb channel
    ret_img = np.zeros(shape=(new_h, new_w, 3), dtype='uint8')
    # scale factor
    s_h, s_c = (h * 1.0) / new_h, (w * 1.0) / new_w

    # insert pixel to the new img
    for i in xrange(new_h):
        for j in xrange(new_w):
            p_x = int(j * s_c)
            p_y = int(i * s_h)

            ret_img[i, j] = img[p_y, p_x]

    return ret_img


测试代码如下:

def test():
    img_path = 'F:/nearest_neighbor.jpg'
    img = cv2.imread(img_path)

    ret_img = nearest_neighbor_resize(img, 222, 220)

    cv2.imshow("source image", img)
    cv2.imshow("after bilinear image", ret_img)
    cv2.waitKey()
    cv2.destroyAllWindows()

运行结果如下:

è¿éåå¾çæè¿°

主要参考:

http://tech-algorithm.com/articles/nearest-neighbor-image-scaling/