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

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

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

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

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

     openseadragon.js用法很简单。

 

定义html容器

 

1 <div id="container" style="width: 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\n", 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\n", 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\n", 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\n",
 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\n",
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源码打包下载

 

posted @ 2015-05-25 11:53  杨元  阅读(3365)  评论(0编辑  收藏  举报