概述

今天进行一个很有意思的实践,利用Java编程实现照片拼图,通过程序操作图片文件,结合photoshop软件,实现下面的效果:

该图片由很多小图片拼接而成,放大以后是这样的效果:

下文中将介绍实现步骤,主要用到了Java程序和photoshop,利用Java编程完成图片的拼接和原图像的采样,然后利用photoshop完成采样图片和拼图图片的叠加,达到更好的效果。

原理

构造这样一幅拼图的原理很简单,主要分为3个步骤:
        ① 将作为子元素的图片缩小,拼接成一幅图片,这里称为:拼图图片。
        ② 对原图片中的颜色进行采样,并实现图片的像素化,生成半透明的采样图片。
        ③ 将采样图片覆盖在拼图图片之上,操作完成。
合成步骤可以用下图来描述:

利用Java中的BufferedImage类可以很轻松地对图片文件进行处理,在这里,我们主要的操作是:读取和写入图片中某个位置的像素值。主要用到的方法如下:

  BufferedImage read(File file)

    ImageIO类中的静态方法。根据指定的File对象构建一个BufferedImage对象,支持pngjpg格式的图片文件,如果file对象指向的不是一个图片文件,则会出现异常。

    BufferedImage(int width, int height, int imageType)

    构建一个新的BufferedImage对象,参数指定了图片的尺寸以及图片的类型。imageType参数可以使用BufferedImage类中提供的静态常量,例如BufferedImage.TYPE_3BYTE_BGR,表明图片为RGB三色模式,每个像素的信息用3个字节来表示。初始情况下,图片中所有位置的像素值均为0,即为黑色。

    int getRgb(int x, int y)

    获取图片中指定位置的像素值,返回值为一个int结果,从低位到高位的3个字节分别表示蓝、绿、红三种基本颜色的饱和度。

    void setRgb(int x, int y, int rgb):

    设定图片中指定位置的像素值。

    boolean write(BufferImage buffer, String formatName, File output):

    ImageIO类中的静态方法,将图片信息写入到文件中,其中formatName为一个表示图片文件格式的字符串,合法的值有"jpg""png"等,output参数则表示目标输出文件。    

这里可以通过简单的计算来找到这些参     利用以上的API变可以完成图片的拼接操作。当然,这些API只是程序中的核心步骤,要编程生成拼图文件,还有很多问题需要解决,详见下文。

实现

接下来介绍具体的程序实现方式。

·参数设定

首先介绍程序中用到的几个参数:

  ·original:作为模板的图片文件。
    ·srcPath:作为拼图元素的图片集合所在的目录(这里为了便于操作,我们需将所有要作为拼图元素的图片放在同一个目录下)。
    ·ratioPath:作为拼图元素的图片尺寸大小应该是完全相同的,程序会首先调整图片的长宽比例,然后再调整图片大小,该参数表示调整长宽比例后的图片的存储路径。
    ·sizePath:调整长宽比例后,程序会调整元素图片的大小,调整完毕后,所有图片具有相同的大小尺寸,该参数表示调整后的图片的存储位置,最终的拼图图片会由该路径下的图片进行合成。
    ·orgWidth、orgHeight:模板图片的宽度和高度。
    ·photoWidth、photoHeight:拼图元素图片的尺寸(所有作为拼图元素的图片大小是相同的,下文中程序会对图片的尺寸进行处理)。
    ·L:这个参数表示放大比率,由于生成的结果文件是由多个字图片拼接而成的一个大的图片,因此,相比原始的模板图片,结果图片的尺寸一定不会小于原始的模板图片。该参数表示放大的倍数,即原始模板图片中的1个像素长度对应与结果图片中的L像素长度。
    ·row、column:该参数表示结果图片中包含元素图片的行数和列数。  

这里可以通过简单的计算来找到这些参数之间的关系,如果设定了模板图片的尺寸(orgWidth、orgHeight)和元素图片(photoWidth、photoHeight)的尺寸以及放大倍率(L),此时,构成结果图片中的元素图片的行数和列数分别是:

            row = floor[orgWidth / (photoWidth / L)]

column = floor[orgHeight / (photoHeight / L)]
    这里的floor表示向下取整,即如果计算得到的行数和列数不是整数,此时,模板图片中的边缘部分可能无法呈现在结果图片中。同时,我们还可以计算出结果图片的大小尺寸:
            resultWidth = orgWidth * L = row * photoWidth
            resultHeight = orgHeight * L = column * photoHeight
该数据同时还是采样图片和拼图图片的大小尺寸数据。

以上为程序中用到的参数。

·调整长宽比例

调整长宽比例的目的是使得所有的元素图片具有相同的长宽比例,便于之后的程序调整图片的大小。在程序的输入中我们设定了元素图片的尺寸photoWidth、photoHeight,因此,元素图片的长宽比例为:photoWidth / photoHeight,将这个结果携程比值的形式为:
            宽 :高 = w : h (其中:w = photoWidth / gcd, h = photoHeight / gcd)
其中:gcd为photoWidth和photoHeight的最大公约数。
    在保证图片长宽比例的同时,我们还要使得修改后的图片的面积尽可能的大,这样就使得原有图片的内容能尽可能多地保留。

    

这里需要计算两个参数:
            qw = width / w; 
            qh = height / h;
    其中:width和height为图片调整前的原始尺寸。 选择qw和qh的较小者q,然后就可以确定调整比例后的图片的尺寸(用width'和height'来表示):
            width' = q * w
            height' = q * h
    此时,可以得到下面的结论:
            qw > qh时,q = qh,此时:width' = qh * w, height' = height。
            qw < qh时,q = qw,此时:width' = width, height' = qw * h。

·调整大小

调整比例后,所有的元素图片都有相同的长宽比例,接下来调整图片的大小。 调整图片大小的逻辑很简单,取出x坐标是width'/photoWidth的倍数,同时y坐标是height'/photoHeight的倍数位置的像素点即可。

·生成拼图图像

调整至所有元素图片大小相同后,我们就可以利用这些元素图片生成拼图图片了,拼图图片的大小为:resultWidth × resultHeight,元素图片的大小为photoWidth × photoHeight,拼图图片中具有row行,column列的元素图片。在这里,我们需要进行坐标映射,对于位于拼图图片的第r行第c列的元素图片,其中的任意一个像素点,如果该点在元素图片中的坐标是px,py,那么该点在拼图图片中的坐标dx,dy可以表示为:
                    dx = photoWidth * r + px
                    dy = photoHeight * c + py
其中:0 ≤ px < photoWidth, 0 ≤ py < photoHeight。
    确定了元素图片和拼图图片的坐标映射关系后,我们还需要决定具体每个位置要选择哪个图片进行覆盖,我们需要找到色调和模板图片对应位置最相似的图片进行覆盖,例如,如果模板图像为一张人像,那么图片中人面部颜色较浅,我们需要选择颜色较浅的元素图片进行拼接;图片中头发的部分颜色较深,我们需要选择色调较暗的元素图片进行覆盖。
    这里为找到每个位置的最合适的图片,我们定义一个判定的标准,在选择拼图图片中的第r行,第c列的位置的图片时,对于某一个候选图片中的每一个像素点(px,py),首先将其转化为拼图图片中的坐标(dx, dy),然后,在将其颜色和模板图片中对应为位置的像素点的颜色进行比较,计算其相似度s,对于相似度s,我们定义为两像素点红、绿、蓝三个分量的绝对差值之和,即:
                    r1 = rgb1 & 0x000000ff;

                    g1 = (rgb1 & 0x0000ff00) >> 8;
                    b1 = (rgb1 & 0x00ff0000) >> 16;
                    r2 = rgb2 & 0x000000ff;
                    g2 = (rgb2 & 0x0000ff00) >> 8;
                    b2 = (rgb2 & 0x00ff0000) >> 16;
                    s = |r2 - r1| + |b2 - b1| + |g2 - g1|
    然后将s值和约定的阈值s0进行比较,统计候选图片中s值小于阈值s0的像素点的个数count,找到候选图片中count值最大的图片,就是当前位置需要选择的图片。
    这里还有一个问题:由于模板图像和拼图图像的大小是不相同的,因此,在比较像素点的相似度时,还需要对拼图图片的坐标和模板图片的像素点坐标进行坐标映射。通过计算,我们可以得到:对于拼图图像中的位置(dx,dy)原始图像中的对应位置(rx, ry)可以表示为:
                    rx = dx / L
                    ry = dy / L
进而,根据上文中的计算结果,我们可以得到元素图片中的像素点坐标(px,py)和模板图片中的坐标(rx, ry)之间的直接映射关系:
                    rx = orgWidth / row * r + px / L
                    ry = orgHeight / column * c + py / L
这样,在选择图片时,将位于元素图片中(px, py)位置的像素点和模板图片中位于(rx, ry)位置的像素点进行比较,计算相似度s,选择最优的图片拼接即可。
    下图是笔者采用的模板图片和生成的拼图图片,可以看到,设定的放大倍率越大,包含的元素图片越多,生成的拼图图像和模板图像的相似度也就越高。

模板图片
拼图图片(L=2)
    
拼图图片(L=5)
拼图图片(L=10)

·生成采样图片

生成拼图图片过程中,虽然程序会通过计算找到合适元素图片对模板图片的每个位置进行拼接,但是,如果选择的元素图片在色彩上和模板图片差别很大,生成的拼图图像效果依旧不好,解决的方案是在拼图图像上覆盖一层半透明的基于模板图像的采样图像使得拼图图像的色调跟符合原图。本步骤的目的就是生成这个采样图片,显然采样图片的尺寸和拼图图片是完全一致的。
    对于由row行column列元素图片构成的拼图图片,其对应的采样图片也由row行column列构成,不同的是,构成采样图片的每个元素图片是只有一种颜色的纯色色块,这里由于笔者对于计算机图形学的相关知识了解甚少,因此采用一种最简单的策略,选择模板图片对应位置的中心位置的像素的颜色作为采样色。如下图所示:

    
    同样,这里需要对模板图片和采样图片之间进行坐标映射,这个映射关系和上文中选择元素图片时用到的映射关系很相似,对于采样图片中第r行第c列的元素图片,其颜色应该采样于模板图片中的sx,xy位置,则:
    sx = orgWidth / row * (r + 1/2)
    sy = orgHeight / column * (c + 1/2)

    下图为笔者生成的采样图片,同样可以看出,放大倍率越大,包含的元素图片越多,采样图片就越清晰,就能保留模板图片中更多的信息。
    
模板图片
    
采样图片(L=2)
    
采样图片(L=5)
    
采样图片(L=10)

·代码整合

以上就是Java程序完成的全部工作,为了操作方便,笔者利用Swing绘制了GUI界面,将以上各个步骤的代码整合起来:

 


PS叠加

最后的步骤是叠加,利用photoShop将采样图片叠加在拼图图片之上,然后将采样图片设置为半透明即可:

以上便是生成照片拼图方法。

程序代码

程序代码见:https://github.com/octopusfly/photo_puzzle,有兴趣朋友可以体验一下。

利用Java和photoShop实现照片拼图相关推荐

  1. java读取网络图片数据_如何利用java读取网络照片

    如何利用java读取网络照片 Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的`编程.以下是小编为大家搜索整理的如何利用java读取网络照片, ...

  2. 用Java实现的简易马赛克拼图

    用Java实现的简易马赛克拼图 什么是马赛克拼图 效果图 原理 所有代码 什么是马赛克拼图 马赛克拼图 简单来说就是远远看上去是一张大图,放大之后会发现其实是由许多张不同的小图组成 效果图 如果准备的 ...

  3. 利用Java进行身份证正反面信息识别

    利用Java进行身份证正反面信息识别 1.百度授权信息准备 首先你得在百度AI开放平台上面注册一个账号,或者已经有百度账号了,网址是:https://ai.baidu.com/,如下图所示: 然后点击 ...

  4. 照片拼图制作怎么弄?这几个方法或许能帮到你

    过年的时候,跟几个相识多年的朋友出门去玩.期间,我们拍摄了许多照片准备用来发朋友圈.回到家后,看到她们因为微信的朋友圈只能上传九张照片而在讨论要用哪张照片的时候,我说出了那句至理名言"小孩子 ...

  5. 利用RAW格式处理大光比照片

    从来只发处理过后的照片,对处理过程绝口不提,第一次尝试说说后期处理过程中的一些小技巧,就说说利用RAW格式处理大光比照片吧,这个技巧在这阵处理同学的婚纱照过程中就有用到. 背景知识 相机的宽容度就是相 ...

  6. 照片拼图软件哪个好?这几个软件好用且简单

    最近我总是收到大家要怎么制作照片拼图的私信,看来大家拍了很多好看的照片呀.为了解决大家的问题,我马上就开始搜集资料了,在经过我一番尝试后,我觉得这三款软件不错.小伙伴们可以先收藏这篇文章呀,这样以后就 ...

  7. 手把手教你利用Java获取图片GPS信息

    你知道的越多,不知道的就越多,业余的像一棵小草! 成功路上并不拥挤,因为坚持的人不多. 编辑:业余草 推荐:https://www.xttblog.com/?p=5251 一张图片能包含很多敏感信息, ...

  8. java 根据ip获取mac地址_利用java如何根据IP获取mac地址

    利用java如何根据IP获取mac地址 发布时间:2020-11-25 15:49:42 来源:亿速云 阅读:74 作者:Leah 利用java如何根据IP获取mac地址?针对这个问题,这篇文章详细介 ...

  9. Oracle字符串转BooIean,利用Java的多线程技术实现数据库的访问.pdf

    利用Java的多线程技术实现数据库的访问.pdf 第 卷第 期 计算机应用 22 12 Voi .22 , No . 12 年 月 2002 12 Computer Appiications Dec ...

  10. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

最新文章

  1. java泛型中?和T有什么区别?
  2. 报销流程不简单,OA平台的私人订制
  3. CVTE(WEB后台开发)
  4. C++ 基础概念、语法和易错点整理
  5. 【跃迁之路】【428天】程序员高效学习方法论探索系列(实验阶段185-2018.04.09)...
  6. 计算机技术题目,计算机技术题目.doc
  7. 转:springboot servlet使用配置
  8. JS_15作用域与作用域链
  9. 说说Javascript
  10. Visual Studio 2005 重置设置
  11. Excel/WPS之粘贴可见内容
  12. Python-从txt中获取所有带有书名号的内容,并去除重复内容
  13. 关于linux校准时间
  14. python 简单操作dbpedia
  15. 如何登录无线web认证服务器,路由器如何设置web认证的方式连接免费WIFI
  16. Python 多态,概念与示例,精简篇
  17. Arduino智能小车设计(二)
  18. 微信小程序 SOTER 生物认证DEMO,指纹识别
  19. Model 3 FBCM
  20. Linux环境编程05

热门文章

  1. 设定session.timeout=30后,可是不到5分钟session变量就消失了.为什么?
  2. 阅读笔记:XModal-ID: Through-Wall Person Identification from Candidate Video Footage Using WiFi
  3. ps裁剪和裁切的区别_PS剪切、裁剪、裁切的区别
  4. java实现角色+武器攻击小游戏
  5. 完爆面试官!2021Java高频精选面试题讲解
  6. Qt警告:Missing reference in range-for with non trivial type (QString) [clazy-range-loop]
  7. Unity中的存档与读档
  8. 笔记本电脑显示dns服务器出错,电脑出现dns错误无法上网的解决方法详解
  9. 【Excel】某列数据有效性根据另外一列数据有效性进行动态更改
  10. diskpart建立新卷