图像处理之给定任意四点不规则放缩

简介: 图像处理之给定任意四点不规则放缩

基本原理:

计算四个点的增长斜率,使用双线性插值实现像素填充。

废话也懒得说啦,自己看代码吧,我从一个地方抄袭+修改了一下

源来的代码,原因是原来的代码太乱了,也太让人费解了。

运行效果:

1366901518_1725.jpg

滤镜源代码:

package com.gloomyfish.filter.study;
 
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
 
/**
 * 
 * @author gloomy-fish 2013-04-25
 *
 */
public class PerspectiveFilter extends AbstractBufferedImageOp {
  private float x0, y0, x1, y1, x2, y2, x3, y3;
  private float dx1, dy1, dx2, dy2, dx3, dy3;
  private float A, B, C, D, E, F, G, H, I;
  private float a11, a12, a13, a21, a22, a23, a31, a32, a33;
    private boolean scaled;
    private int width;
    private int height;
    
  @Override
  public BufferedImage filter(BufferedImage src, BufferedImage dest) {
        width = src.getWidth();
        height = src.getHeight();
        if ( dest == null )
            dest = createCompatibleDestImage( src, null );
      A = a22*a33 - a32*a23;
      B = a31*a23 - a21*a33;
      C = a21*a32 - a31*a22;
      D = a32*a13 - a12*a33;
      E = a11*a33 - a31*a13;
      F = a31*a12 - a11*a32;
      G = a12*a23 - a22*a13;
      H = a21*a13 - a11*a23;
      I = a11*a22 - a21*a12;
        if ( !scaled ) {
            float invWidth = 1.0f/width;
            float invHeight = 1.0f/height;
 
            A *= invWidth;
            D *= invWidth;
            G *= invWidth;
            B *= invHeight;
            E *= invHeight;
            H *= invHeight;
        }
        
    int[] inPixels = getRGB( src, 0, 0, width, height, null );
 
    int srcWidth = width;
    int srcHeight = height;
    int srcWidth1 = width-1;
    int srcHeight1 = height-1;
    int outX=0, outY=0;
    Rectangle transformedSpace = new Rectangle(0, 0, width, height);
    transformSpace(transformedSpace);
    outX = transformedSpace.x;
    outY = transformedSpace.y;
    int outWidth = transformedSpace.width;
    int outHeight = transformedSpace.height;
    // int index = 0;
    int[] outPixels = new int[transformedSpace.width];
    float[] out = new float[2];
 
    for (int y = 0; y < outHeight; y++) {
      for (int x = 0; x < outWidth; x++) {
        transformInverse(outX+x, outY+y, out);
        int srcX = (int)Math.floor( out[0] );
        int srcY = (int)Math.floor( out[1] );
        float xWeight = out[0]-srcX;
        float yWeight = out[1]-srcY;
        int nw, ne, sw, se;
 
        if ( srcX >= 0 && srcX < srcWidth1 && srcY >= 0 && srcY < srcHeight1) {
          // Easy case, all corners are in the image
          int i = srcWidth*srcY + srcX;
          nw = inPixels[i];
          ne = inPixels[i+1];
          sw = inPixels[i+srcWidth];
          se = inPixels[i+srcWidth+1];
        } else {
          // Some of the corners are off the image
          nw = getPixel( inPixels, srcX, srcY, srcWidth, srcHeight );
          ne = getPixel( inPixels, srcX+1, srcY, srcWidth, srcHeight );
          sw = getPixel( inPixels, srcX, srcY+1, srcWidth, srcHeight );
          se = getPixel( inPixels, srcX+1, srcY+1, srcWidth, srcHeight );
        }
        outPixels[x] = ImageMath.bilinearInterpolate(xWeight, yWeight, nw, ne, sw, se);
      }
      setRGB( dest, 0, y, transformedSpace.width, 1, outPixels );
    }
    
    return dest;
  }
  
  protected void transformSpace( Rectangle rect ) {
    if ( scaled ) {
            rect.x = (int)Math.min( Math.min( x0, x1 ), Math.min( x2, x3 ) );
            rect.y = (int)Math.min( Math.min( y0, y1 ), Math.min( y2, y3 ) );
            rect.width = (int)Math.max( Math.max( x0, x1 ), Math.max( x2, x3 ) ) - rect.x;
            rect.height = (int)Math.max( Math.max( y0, y1 ), Math.max( y2, y3 ) ) - rect.y;
            return;
        }
  }
  final private int getPixel( int[] pixels, int x, int y, int width, int height ) {
    if (x < 0 || x >= width || y < 0 || y >= height) {
      return pixels[(ImageMath.clamp(y, 0, height-1) * width) + ImageMath.clamp(x, 0, width-1)] & 0x00ffffff;
    }
    return pixels[ y*width+x ];
  }
  
  public PerspectiveFilter() {
    this( 0, 0, 1, 0, 1, 1, 0, 1);
  }
    
  public PerspectiveFilter(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
    unitSquareToQuad(x0, y0, x1, y1, x2, y2, x3, y3);
  }
 
  protected void transformInverse( int x, int y, float[] out ) {
    out[0] = width * (A*x+B*y+C)/(G*x+H*y+I);
    out[1] = height * (D*x+E*y+F)/(G*x+H*y+I);
  }
  
  public void unitSquareToQuad( float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3 ) {
    this.x0 = x0;
    this.y0 = y0;
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.x3 = x3;
    this.y3 = y3;
    
    dx1 = x1-x2;
    dy1 = y1-y2;
    dx2 = x3-x2;
    dy2 = y3-y2;
    dx3 = x0-x1+x2-x3;
    dy3 = y0-y1+y2-y3;
    
    if (dx3 == 0 && dy3 == 0) {
      a11 = x1-x0;
      a21 = x2-x1;
      a31 = x0;
      a12 = y1-y0;
      a22 = y2-y1;
      a32 = y0;
      a13 = a23 = 0;
    } else {
      a13 = (dx3*dy2-dx2*dy3)/(dx1*dy2-dy1*dx2);
      a23 = (dx1*dy3-dy1*dx3)/(dx1*dy2-dy1*dx2);
      a11 = x1-x0+a13*x1;
      a21 = x3-x0+a23*x3;
      a31 = x0;
      a12 = y1-y0+a13*y1;
      a22 = y3-y0+a23*y3;
      a32 = y0;
    }
        a33 = 1;
        scaled = false;
  }
  
  public void setCorners(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
    unitSquareToQuad( x0, y0, x1, y1, x2, y2, x3, y3 );
        scaled = true;
  }
    
}

转载请务必注明

相关文章
|
消息中间件 边缘计算 物联网
物联网络管理平台(LoRaWAN)介绍|学习笔记
快速学习物联网络管理平台(LoRaWAN)介绍
1072 5
物联网络管理平台(LoRaWAN)介绍|学习笔记
|
API PHP C++
Windows下用vs2017编译和配置libcurl库(手把手教,适合新人)
Windows下用vs2017编译和配置libcurl库(手把手教,适合新人)
2473 0
|
数据采集 存储 C#
C# 爬虫技术:京东视频内容抓取的实战案例分析
C# 爬虫技术:京东视频内容抓取的实战案例分析
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp小程序的甘肃旅游服务平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp小程序的甘肃旅游服务平台附带文章源码部署视频讲解等
93 0
|
测试技术 Python
【手机群控】 利用Python与uiautomator2实现
使用Python的uiautomator2库进行多设备自动化测试,涉及环境准备(Python、uiautomator2、adb连接设备)和代码实现。通过`adb devices`获取设备列表,使用多进程并行执行测试脚本,每个脚本通过uiautomator2连接设备并获取屏幕尺寸。注意设备需开启USB调试并授权adb。利用多进程而非多线程,因Python的GIL限制。文章提供了一种提高测试效率的方法,适用于大规模设备测试场景。
1118 2
【手机群控】 利用Python与uiautomator2实现
|
存储 SQL JSON
两万字详解MongoDB从入门到精通(一)
两万字详解MongoDB从入门到精通
1509 1
|
SQL 关系型数据库 数据库
实时计算 Flink版操作报错合集之遇到报错:"An OperatorEvent from an OperatorCoordinator to a task was lost. Triggering task failover to ensure consistency." ,该怎么办
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
429 0
|
监控 数据可视化 Unix
自用的Linux命令高效的4个shell替代工具
这篇文章介绍了几个用于提升Unix系统终端体验的替代工具。首先提到了`oh-my-zsh`,然后重点推荐了三个命令行工具:1) `htop`和`btop`作为`top`命令的增强版,提供更丰富的进程监控视图;2) `duf`作为`df`命令的替代,以更整洁的界面显示磁盘空间使用情况;3) `eza`(原`exa`)和`bat`,这两个工具为`ls`和`cat`命令提供了颜色高亮和更好的文件查看体验。这些工具旨在使终端操作更加直观和愉快。
331 0
|
JavaScript
vue3多条件搜索功能
搜索功能在后台管理页面中非常常见,本篇就着重讲一下vue3-admin-element框架中如何实现一个顶部多条件搜索功能
367 0
vue3多条件搜索功能
|
网络协议 安全 网络安全
openWRT SFTP 实现远程文件安全传输
openWRT SFTP 实现远程文件安全传输