图像处理之线性插值旋转算法详解
创作时间:
作者:
@小白创作中心
图像处理之线性插值旋转算法详解
引用
CSDN
1.
https://blog.csdn.net/jia20003/article/details/8159587
图像处理中的线性插值旋转算法是一种高质量的图像旋转方法,能够实现图像旋转时的反锯齿效果。本文将详细介绍该算法的基本数学知识、相关算法原理、角度旋转处理方法以及具体的程序实现。
基本数学知识
- 三角函数基本知识:sin, cos
- 反三角函数基本知识:知道任意一点坐标P(x, y)求取该点的角度a = atan2(y/x)
- 极坐标与笛卡尔坐标系转换知识
图像旋转矩阵
通过旋转矩阵可以计算图像旋转以后的新的高度与宽度。
相关算法
双线性插值算法:实现图像旋转反锯齿效果,同时是一种高质量的图像旋转方法,缺点是计算量比较大。但是对现在的计算机硬件来说,速度还可以。
关于角度旋转
- 90度,180度,270度可以直接旋转坐标取得,像素直接映射取得。
- 对于任何角度angle可以如下处理n = mod(angle, 90) = 1, 2, 3, 然后将角度旋转90,180,270然后再旋转角度(angle - n * 90)。
程序实现
- 首先根据输入角度参数angle, 背景填充颜色bgcolor初始化
- 计算出旋转以后的图像width与height
- 循环每个输出像素,计算机坐标
- 反旋转输入角度到输入的目标像素浮点数坐标
- 使用双线性插值完成目标像素填充,如果不在范围之内填充背景色。
- 得到输出像素数据,返回旋转后图像
原图
旋转45度的效果,背景填充为灰色
程序代码(特殊角度旋转自己实现吧,有点懒)
package com.gloomyfish.filter.study;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
public class RotateFilter extends AbstractBufferedImageOp {
private double angle;
private Color background;
private int outw;
private int outh;
public RotateFilter() {
this.angle = (45.0d/180.0d) * Math.PI;
background = Color.BLACK;
outw = -1;
outh = -1;
}
public void setDegree(double angle) {
this.angle = (angle/180.0d) * Math.PI;
}
public void setBackgroud(Color background) {
this.background = background;
}
public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel dstCM) {
if ( dstCM == null )
dstCM = src.getColorModel();
return new BufferedImage(dstCM, dstCM.createCompatibleWritableRaster(outw, outh), dstCM.isAlphaPremultiplied(), null);
}
@Override
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
int width = src.getWidth();
int height = src.getHeight();
int[] inPixels = new int[width*height];
outw = (int)(width*Math.cos(angle)+height*Math.sin(angle));
outh = (int)(height*Math.cos(angle)+width*Math.sin(angle));
System.out.println("after rotate, new width : " + outw);
System.out.println("after rotate, new height: " + outh);
int[] outPixels = new int[outw*outh];
getRGB( src, 0, 0, width, height, inPixels );
int index = 0;
int centerPixel = inPixels[height/2 * width + width/2];
// calculate new center coordinate
float centerX = outw / 2.0f + 0.5f;
float centerY = outh /2.0f + 0.5f;
// calculate the original center coordinate
float ocenterX = width / 2.0f + 0.5f;
float ocenterY = height /2.0f + 0.5f;
float rx =0, ry = 0; //after rotated coordinate
float px = 0, py = 0; // original coordinate
float prow = 0, pcol = 0;
for(int row=0; row<outh; row++) {
for(int col=0; col<outw; col++) {
rx = col - centerX;
ry = centerY - row;
float fDistance = (float)Math.sqrt(rx * rx + ry * ry);
float fPolarAngle = 0; //;
if(rx != 0) {
fPolarAngle = (float)Math.atan2((double)ry, (double)rx);
} else {
if(rx == 0) {
if(ry == 0) {
outPixels[index] = centerPixel;
continue;
}
else if(ry < 0) {
fPolarAngle = 1.5f * (float)Math.PI;
} else {
fPolarAngle = 0.5f * (float)Math.PI;
}
}
}
// "reverse" rotate, so minus instead of plus
fPolarAngle -= angle;
px = fDistance * (float)Math.cos(fPolarAngle);
py = fDistance * (float)Math.sin(fPolarAngle);
// get original pixel float point
prow = ((float)ocenterY) - py;
pcol = ((float)ocenterX) + px;
// now start the biline-interpolation algorithm here!!!
int[] rgb = bilineInterpolation(inPixels, width, height, prow, pcol);
index = row * outw + col;
outPixels[index] = (255 << 24) | (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];
}
}
if ( dest == null )
dest = createCompatibleDestImage( src, null );
setRGB( dest, 0, 0, outw, outh, outPixels );
return dest;
}
private int[] bilineInterpolation(int[] input, int width, int height, float prow, float pcol) {
double row = Math.floor(prow);
double col = Math.floor(pcol);
if(row < 0 || row >= height) {
return new int[]{background.getRed(), background.getGreen(), background.getBlue()};
}
if(col < 0 || col >= width) {
return new int[]{background.getRed(), background.getGreen(), background.getBlue()};
}
int rowNext = (int)row + 1, colNext = (int)col + 1;
if((row + 1) >= height) {
rowNext = (int)row;
}
if((col + 1) >= width) {
colNext = (int)col;
}
double t = prow - row;
double u = pcol - col;
double coffiecent1 = (1.0d-t)*(1.0d-u);
double coffiecent2 = (t)*(1.0d-u);
double coffiecent3 = t*u;
double coffiecent4 = (1.0d-t)*u;
int index1 = (int)(row * width + col);
int index2 = (int)(row * width + colNext);
int index3 = (int)(rowNext * width + col);
int index4 = (int)(rowNext * width + colNext);
int tr1, tr2, tr3, tr4;
int tg1, tg2, tg3, tg4;
int tb1, tb2, tb3, tb4;
tr1 = (input[index1] >> 16) & 0xff;
tg1 = (input[index1] >> 8) & 0xff;
tb1 = input[index1] & 0xff;
tr2 = (input[index2] >> 16) & 0xff;
tg2 = (input[index2] >> 8) & 0xff;
tb2 = input[index2] & 0xff;
tr3 = (input[index3] >> 16) & 0xff;
tg3 = (input[index3] >> 8) & 0xff;
tb3 = input[index3] & 0xff;
tr4 = (input[index4] >> 16) & 0xff;
tg4 = (input[index4] >> 8) & 0xff;
tb4 = input[index4] & 0xff;
int tr = (int)(tr1 * coffiecent1 + tr2 * coffiecent4 + tr3 * coffiecent2 + tr4 * coffiecent3);
int tg = (int)(tg1 * coffiecent1 + tg2 * coffiecent4 + tg3 * coffiecent2 + tg4 * coffiecent3);
int tb = (int)(tb1 * coffiecent1 + tb2 * coffiecent4 + tb3 * coffiecent2 + tb4 * coffiecent3);
return new int[]{tr, tg, tb};
}
}
本文原文来自CSDN
热门推荐
高压液压齿轮泵:结构组成与工作原理
高压磁力旋涡泵:无泄漏设计保障工业安全,五大领域应用前景广阔
白居易:用简洁诗句连接世界的诗人
“野火烧不尽”:三部热播剧里的坚韧精神
“诗魔”白居易:用通俗语言书写社会百态与人间真情
直击时弊,巧用虚构:白居易《新乐府》的叙事艺术
护士职称制度改革:机遇与挑战并存
痛经救星:五款舒缓运动教程
最新研究:抑郁症或增加痛经风险,60万例数据揭示心理健康与生理健康的关联
幼儿园期末总结发言技巧:结构、要点与案例解析
幼儿园健康教育,让孩子赢在起点
家园共育:从幼儿园总结到家庭教育
南通调查:单身老人“搭伙养老”现象日益普遍
研究发现:老年人对爱情的追求超越年轻人
如何践行教师职业道德规范?
协同发展,医保互认手持一张医保卡,京津冀看病更方便
制药工程专业就业全景:5000家药企助力毕业生多渠道发展
小红书热议:台州必打卡特色景点攻略
天台山深度游:国清寺、石梁飞瀑、华顶森林公园全攻略
流感疫苗接种四大误区,专家权威解读来了!
文王一支笔:神农架特有珍稀药材的传奇与科学
流感高发季,职场人士如何正确接种流感疫苗?
京城冬季流感疫苗接种正当时
中国流感疫苗研发取得重大突破:首个细胞基质疫苗进入Ⅲ期临床
工程师职称评定条件全解析:从初级到高级的晋升路径
ZGQ手动西林瓶拧盖机使用技巧大揭秘
全自动拧盖机:提升生产线效率的关键装备
个体工商户变更经营者,这些细节要注意!
从零到一打造商业经营方案:理论框架与实战指南
方案重策略,计划重执行:企业经营中的双轮驱动