• openseadragon.js与deep zoom java实现艺术品图片展示


         openseadragon.js 是一款用来做图像缩放的插件,它可以用来做图片展示,做展示的插件很多,也很优秀,但大多数都解决不了图片尺寸过大的问题。

         艺术品图像展示就是最简单的例子,展示此类图片一般要求比较精细,所以图片尺寸很大,如果按照普通的方式直接将整个图片加载,要耗费巨大的带宽。

         openseadragon.js 即为此而生,它展示的图像,必须经过切割处理,当放大图像时,才去加载更大的尺寸,真正做到了按需加载。

         值得一提的是,openseadragon.js是微软公司的一款开源产品,非常难得。

         openseadragon.js用法很简单。

    定义html容器

    1 <div id="container" style=" 100%; height: 600px;"></div>

    初始化openseadragon.js

     1 (function(win){
     2     var viewer = OpenSeadragon({
     3         // debugMode: true,
     4         id: "container",  //容器id
     5         prefixUrl: "./lib/openseadragon/images/", //openseadragon插件资源路径
     6         tileSources: "./image/earth.xml",  //openseadragon 图片资源xml
     7         showNavigator:true  //是否显示控制按钮
     8     });
     9     console.log(viewer);
    10 })(window);

    效果图

     openseadragon

          对,你没有看错,使用openseadragon.js,只需要copy一段初始化代码,初始化代码格式基本固定,只有tileSources是需要变化的,想显示哪个图,就写哪个图的xml链接。

          那么这个xml是个什么东西呢?这是微软定义的一套Deep Zoom技术,或者叫规范,通过图像分割,然后配上一个xml文件,即可按照用户缩放的尺寸,按需加载图像,直到最清晰为止。这本来是微软Silverlight里的技术,它的JavaScript实现就是openseadragon.js。

         笔者从google code上找到了一份java程序,专门用来生成Deep Zoom。笔者在原作基础上做了一些小小的改进,原作有些错误。。。

    Deep Zoom For Java

      1 import java.awt.Graphics2D;
      2 import java.awt.RenderingHints;
      3 import java.awt.image.BufferedImage;
      4 import java.io.File;
      5 import java.io.FileOutputStream;
      6 import java.io.IOException;
      7 import java.nio.ByteBuffer;
      8 import java.nio.channels.FileChannel;
      9 import javax.imageio.ImageIO;
     10 
     11 /**
     12  * Deep Zoom Converter
     13  * 
     14  * @author 杨元
     15  *
     16  */
     17 public class DeepZoomUtil {
     18     static final String xmlHeader = "<?xml version="1.0" encoding="utf-8"?>";
     19     static final String schemaName = "http://schemas.microsoft.com/deepzoom/2009";
     20 
     21     static Boolean deleteExisting = true;
     22     static String tileFormat = "jpg";
     23 
     24     // settings
     25     static int tileSize = 256;
     26     static int tileOverlap = 1;
     27     static Boolean verboseMode = false;
     28     static Boolean debugMode = false;
     29 
     30     /**
     31      * @param args the command line arguments
     32      */
     33     public static void main(String[] args) {
     34       
     35         try {
     36             processImageFile(new File("d:/earth.jpg"), new File("d:/earth"));
     37         } catch (Exception e) {
     38             e.printStackTrace();
     39         }
     40     }
     41 
     42     /**
     43      * Process the given image file, producing its Deep Zoom output files
     44      * in a subdirectory of the given output directory.
     45      * @param inFile the file containing the image
     46      * @param outputDir the output directory
     47      */
     48     private static void processImageFile(File inFile, File outputDir) throws IOException {
     49         if (verboseMode)
     50              System.out.printf("Processing image file: %s
    ", inFile);
     51 
     52         String fileName = inFile.getName();
     53         String nameWithoutExtension = fileName.substring(0, fileName.lastIndexOf('.'));
     54         String pathWithoutExtension = outputDir + File.separator + nameWithoutExtension;
     55 
     56         BufferedImage image = loadImage(inFile);
     57 
     58         int originalWidth = image.getWidth();
     59         int originalHeight = image.getHeight();
     60 
     61         double maxDim = Math.max(originalWidth, originalHeight);
     62 
     63         int nLevels = (int)Math.ceil(Math.log(maxDim) / Math.log(2));
     64 
     65         if (debugMode)
     66             System.out.printf("nLevels=%d
    ", nLevels);
     67 
     68         // Delete any existing output files and folders for this image
     69 
     70         File descriptor = new File(pathWithoutExtension + ".xml");
     71         if (descriptor.exists()) {
     72             if (deleteExisting)
     73                 deleteFile(descriptor);
     74             else
     75                 throw new IOException("File already exists in output dir: " + descriptor);
     76         }
     77 
     78         File imgDir = new File(pathWithoutExtension);
     79         if (imgDir.exists()) {
     80             if (deleteExisting) {
     81                 if (debugMode)
     82                     System.out.printf("Deleting directory: %s
    ", imgDir);
     83                 deleteDir(imgDir);
     84             } else
     85                 throw new IOException("Image directory already exists in output dir: " + imgDir);
     86         }
     87 
     88         imgDir = createDir(outputDir, nameWithoutExtension.concat("_files"));
     89 
     90         double width = originalWidth;
     91         double height = originalHeight;
     92 
     93         for (int level = nLevels; level >= 0; level--) {
     94             int nCols = (int)Math.ceil(width / tileSize);
     95             int nRows = (int)Math.ceil(height / tileSize);
     96             if (debugMode)
     97                 System.out.printf("level=%d w/h=%f/%f cols/rows=%d/%d
    ",
     98                                    level, width, height, nCols, nRows);
     99             
    100             File dir = createDir(imgDir, Integer.toString(level));
    101             for (int col = 0; col < nCols; col++) {
    102                 for (int row = 0; row < nRows; row++) {
    103                     BufferedImage tile = getTile(image, row, col);
    104                     saveImage(tile, dir + File.separator + col + '_' + row);
    105                 }
    106             }
    107 
    108             // Scale down image for next level
    109             width = Math.ceil(width / 2);
    110             height = Math.ceil(height / 2);
    111             if (width > 10 && height > 10) {
    112                 // resize in stages to improve quality
    113                 image = resizeImage(image, width * 1.66, height * 1.66);
    114                 image = resizeImage(image, width * 1.33, height * 1.33);
    115             }
    116             image = resizeImage(image, width, height);
    117         }
    118 
    119         saveImageDescriptor(originalWidth, originalHeight, descriptor);
    120     }
    121 
    122 
    123     /**
    124      * Delete a file
    125      * @param path the path of the directory to be deleted
    126      */
    127     private static void deleteFile(File file) throws IOException {
    128          if (!file.delete())
    129              throw new IOException("Failed to delete file: " + file);
    130     }
    131 
    132     /**
    133      * Recursively deletes a directory
    134      * @param path the path of the directory to be deleted
    135      */
    136     private static void deleteDir(File dir) throws IOException {
    137         if (!dir.isDirectory())
    138             deleteFile(dir);
    139         else {
    140             for (File file : dir.listFiles()) {
    141                if (file.isDirectory())
    142                    deleteDir(file);
    143                else
    144                    deleteFile(file);
    145             }
    146             if (!dir.delete())
    147                 throw new IOException("Failed to delete directory: " + dir);
    148         }
    149     }
    150 
    151     /**
    152      * Creates a directory
    153      * @param parent the parent directory for the new directory
    154      * @param name the new directory name
    155      */
    156     private static File createDir(File parent, String name) throws IOException {
    157         assert(parent.isDirectory());
    158         File result = new File(parent + File.separator + name);
    159         if (!result.mkdir())
    160            throw new IOException("Unable to create directory: " + result);
    161         return result;
    162     }
    163 
    164     /**
    165      * Loads image from file
    166      * @param file the file containing the image
    167      */
    168     private static BufferedImage loadImage(File file) throws IOException {
    169         BufferedImage result = null;
    170         try {
    171             result = ImageIO.read(file);
    172         } catch (Exception e) {
    173             throw new IOException("Cannot read image file: " + file);
    174         }
    175         return result;
    176     }
    177 
    178     /**
    179      * Gets an image containing the tile at the given row and column
    180      * for the given image.
    181      * @param img - the input image from whihc the tile is taken
    182      * @param row - the tile's row (i.e. y) index
    183      * @param col - the tile's column (i.e. x) index
    184      */
    185     private static BufferedImage getTile(BufferedImage img, int row, int col) {
    186         int x = col * tileSize - (col == 0 ? 0 : tileOverlap);
    187         int y = row * tileSize - (row == 0 ? 0 : tileOverlap);
    188         int w = tileSize + (col == 0 ? 1 : 2) * tileOverlap;
    189         int h = tileSize + (row == 0 ? 1 : 2) * tileOverlap;
    190 
    191         if (x + w > img.getWidth())
    192             w = img.getWidth() - x;
    193         if (y + h > img.getHeight())
    194             h = img.getHeight() - y;
    195 
    196         if (debugMode)
    197             System.out.printf("getTile: row=%d, col=%d, x=%d, y=%d, w=%d, h=%d
    ",
    198                               row, col, x, y, w, h);
    199         
    200         assert(w > 0);
    201         assert(h > 0);
    202 
    203         BufferedImage result = new BufferedImage(w, h, img.getType());
    204         Graphics2D g = result.createGraphics();
    205         g.drawImage(img, 0, 0, w, h, x, y, x+w, y+h, null);
    206 
    207         return result;
    208     }
    209 
    210     /**
    211      * Returns resized image
    212      * NB - useful reference on high quality image resizing can be found here:
    213      *   http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
    214      * @param width the required width
    215      * @param height the frequired height
    216      * @param img the image to be resized
    217      */
    218     private static BufferedImage resizeImage(BufferedImage img, double width, double height) {
    219         int w = (int)width;
    220         int h = (int)height;
    221         BufferedImage result = new BufferedImage(w, h, img.getType());
    222         Graphics2D g = result.createGraphics();
    223         g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
    224                            RenderingHints.VALUE_INTERPOLATION_BICUBIC);
    225         g.drawImage(img, 0, 0, w, h, 0, 0, img.getWidth(), img.getHeight(), null);
    226         return result;
    227     }
    228 
    229     /**
    230      * Saves image to the given file
    231      * @param img the image to be saved
    232      * @param path the path of the file to which it is saved (less the extension)
    233      */
    234     private static void saveImage(BufferedImage img, String path) throws IOException {
    235         File outputFile = new File(path + "." + tileFormat);
    236         try {
    237             ImageIO.write(img, tileFormat, outputFile);
    238         } catch (IOException e) {
    239             throw new IOException("Unable to save image file: " + outputFile);
    240         }
    241     }
    242 
    243     /**
    244      * Write image descriptor XML file
    245      * @param width image width
    246      * @param height image height
    247      * @param file the file to which it is saved
    248      */
    249     private static void saveImageDescriptor(int width, int height, File file) throws IOException {
    250         StringBuilder sb = new StringBuilder(256);
    251         sb.append(xmlHeader);
    252         sb.append("<Image TileSize="");
    253         sb.append(tileSize);
    254         sb.append("" Overlap="");
    255         sb.append(tileOverlap);
    256         sb.append("" Format="");
    257         sb.append(tileFormat);
    258         sb.append("" ServerFormat="Default" xmlns="");
    259         sb.append(schemaName);
    260         sb.append("">");
    261         sb.append("<Size Width="");
    262         sb.append(width);
    263         sb.append("" Height="");
    264         sb.append(height);
    265         sb.append("" />");
    266         sb.append("</Image>");
    267         saveText(sb.toString().getBytes("UTF-8"), file);
    268     }
    269 
    270     /**
    271      * Saves strings as text to the given file
    272      * @param bytes the image to be saved
    273      * @param file the file to which it is saved
    274      */
    275     private static void saveText(byte[] bytes, File file) throws IOException {
    276         try {
    277             //输出流
    278             FileOutputStream fos = new FileOutputStream(file);
    279             //从输出流中创建写通道
    280             FileChannel writeChannel = fos.getChannel();
    281             //将既有数组作为buffer内存空间
    282             ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
    283             //将buffer写到磁盘
    284             writeChannel.write(byteBuffer);
    285             
    286             writeChannel.close();
    287         } catch (IOException e) {
    288             throw new IOException("Unable to write to text file: " + file);
    289         }
    290     }
    291 }
    View Code

         调用非常简单,只有一句话:processImageFile(new File("d:/earth.jpg"), new File("d:/earth"));,第一个参数是图片路径,第二个参数是生成的Deep Zoom保存路径。本例将会在d:/earth目录下生成一个earth.xml文件和一个earth_files文件夹,xml的文件名默认和图片的文件名一致,然后直接把earth.xml的url返回给前端的openseadragon.js,就可以实现图像缩放。

         需要注意的是,*_files文件夹必须和xml文件在同一目录,并且*要和xml文件名保持一致。

         想要预览openseadragon.js效果,必须在真实的http容器中,不可以直接在文件中打开。

         打包的openseadragon.js笔者做了一些UI上的美化,个人觉得漂亮些,如果读者不喜欢,可以用包里的原版。

    openseadragon.js 世界地图案例

    openseadragon.js+java源码打包下载

  • 相关阅读:
    在ConcurrentModificationException异常上的联想
    记录一下自己爬虎牙LOL主播的爬虫思路
    ajax解决跨域问题
    解决多线程下数据库操作问题
    浅谈时间复杂度
    想一下,最大公约数怎么求
    IO流与IO缓冲
    循环移位
    3Sum探讨(Java)
    Two Sum(两个数的相加)
  • 原文地址:https://www.cnblogs.com/iyangyuan/p/4527509.html
Copyright © 2020-2023  润新知