├── README.md ├── image ├── dill.fun.png └── 在线验证图.png ├── pom.xml └── src └── main ├── java ├── com │ └── cooooode │ │ └── java12306 │ │ ├── service │ │ └── VerifyService.java │ │ └── util │ │ ├── ImageProcessor.java │ │ └── LabelImage.java └── org │ └── tensorflow │ └── NativeLibrary.java └── resources └── model ├── label.txt ├── lenet-top.pb └── mobilenet-pic.pb /README.md: -------------------------------------------------------------------------------- 1 | # Java12306 2 | Java调用tensorflow pb模型进行图像分类做验证码识别,其它基本图像处理由java完成 3 | 4 | 模型.pb文件 5 | = 6 | 模型文件已上传可以直接clone 7 | 8 | - 字符分类使用lenet(src/main/resource/model/lennt-top.pb) 9 | 10 | - 图片分类使用mobilenet(src/main/resource/model/mobilenet-pic.pb) 11 | 12 | maven依赖 13 | = 14 | ``` 15 | 16 | 17 | org.tensorflow 18 | tensorflow 19 | 1.14.0 20 | 21 | ``` 22 | 使用方法 23 | = 24 | - 执行 com.cooooode.java12306.service.VerifyService 的mian方法 25 | 26 | ``` 27 | Usage: java VerifyService image image_path 28 | or java VerifyService base64 base64_code 29 | ``` 30 | Online Demo 31 | = 32 | [demo演示地址](http://www.dill.fun/) (服务已关闭) 33 | 34 | [demo源码地址](https://github.com/vua/Java12306/tree/web) 35 |

36 | Sample 37 |

38 | 39 | 抢票自动化测试 40 | = 41 | [demo源码地址](https://github.com/vua/Java12306/tree/grab) 42 | -------------------------------------------------------------------------------- /image/dill.fun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vua/Java12306/2f8cd9fc8fbc0d8174e01d54c7bb51b18e6d597f/image/dill.fun.png -------------------------------------------------------------------------------- /image/在线验证图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vua/Java12306/2f8cd9fc8fbc0d8174e01d54c7bb51b18e6d597f/image/在线验证图.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.cooooode 8 | java12306 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | org.tensorflow 14 | tensorflow 15 | 1.14.0 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/com/cooooode/java12306/service/VerifyService.java: -------------------------------------------------------------------------------- 1 | package com.cooooode.java12306.service; 2 | 3 | import com.cooooode.java12306.util.ImageProcessor; 4 | import com.cooooode.java12306.util.LabelImage; 5 | import org.tensorflow.Tensor; 6 | import org.tensorflow.TensorFlow; 7 | 8 | import javax.imageio.ImageIO; 9 | import java.awt.image.BufferedImage; 10 | import java.io.*; 11 | import java.util.Base64; 12 | import java.util.List; 13 | import java.util.regex.Pattern; 14 | 15 | /** 16 | * @program: verify 17 | * @description: 18 | * @author: vua 19 | * @create: 2020-02-07 14:03 20 | */ 21 | 22 | public class VerifyService { 23 | private static final String modelDir = VerifyService.class.getClassLoader().getResource("model").toString(); 24 | 25 | private static LabelImage labelImage = new LabelImage(); 26 | private static List labels; 27 | private static byte[] graphMobileNet; 28 | private static byte[] graphLenet; 29 | 30 | static { 31 | // graphMobileNet = labelImage.readAllBytesOrExit(Paths.get(modelDir, "train-mobilenet-pic.pb")); 32 | // graphLenet = labelImage.readAllBytesOrExit(Paths.get(modelDir, "train-lenet-top.pb")); 33 | // labels = LabelImage.readAllLinesOrExit(Paths.get(modelDir, "label.txt")); 34 | 35 | try ( 36 | InputStream mobilenet = VerifyService.class.getClassLoader().getResourceAsStream("model/mobilenet-pic.pb"); 37 | InputStream lenet = VerifyService.class.getClassLoader().getResourceAsStream("model/lenet-top.pb"); 38 | InputStream label = VerifyService.class.getClassLoader().getResourceAsStream("model/label.txt"); 39 | ) { 40 | graphMobileNet = labelImage.readAllBytesOrExit(mobilenet); 41 | graphLenet = labelImage.readAllBytesOrExit(lenet); 42 | labels = LabelImage.readAllLinesOrExit(label); 43 | } catch (IOException e) { 44 | 45 | e.printStackTrace(); 46 | } 47 | 48 | 49 | } 50 | 51 | private static String predictPicClasses(byte[] imageBytes) { 52 | //byte[] imageBytes = LabelImage.readAllBytesOrExit(Paths.get("")); 53 | 54 | try (Tensor image = LabelImage.constructAndExecuteGraphToNormalizeImage(imageBytes, 67, 68)) { 55 | 56 | float[] labelProbabilities = LabelImage.executeInceptionGraph(graphMobileNet, image, "input_1", "act_softmax/Softmax"); 57 | int bestLabelIdx = LabelImage.maxIndex(labelProbabilities); 58 | return labels.get(bestLabelIdx); 59 | /*System.out.println( 60 | String.format("BEST MATCH: %s (%.2f%% likely)", 61 | labels.get(bestLabelIdx), 62 | labelProbabilities[bestLabelIdx] * 100f));*/ 63 | } 64 | } 65 | 66 | private static String predictTopClasses(byte[] imageBytes) { 67 | try (Tensor image = LabelImage.constructAndExecuteGraphToNormalizeImage(imageBytes, 30, 60)) { 68 | 69 | float[] labelProbabilities = LabelImage.executeInceptionGraph(graphLenet, image, "conv2d_1_input", "dense_2/Softmax"); 70 | int bestLabelIdx = LabelImage.maxIndex(labelProbabilities); 71 | return labels.get(bestLabelIdx); 72 | /*System.out.println( 73 | String.format("BEST MATCH: %s (%.2f%% likely)", 74 | labels.get(bestLabelIdx), 75 | labelProbabilities[bestLabelIdx] * 100f));*/ 76 | } 77 | } 78 | 79 | private static boolean isBase64(String str) { 80 | String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$"; 81 | return Pattern.matches(base64Pattern, str); 82 | } 83 | 84 | private static class PicThread extends Thread { 85 | private BufferedImage[] imgs; 86 | private StringBuilder response; 87 | 88 | PicThread(BufferedImage[] imgs, StringBuilder response) { 89 | this.imgs = imgs; 90 | this.response = response; 91 | } 92 | 93 | @Override 94 | public void run() { 95 | response.append("图片从左到右从上到下依次是:\n"); 96 | if (imgs != null && imgs.length > 0) { 97 | for (int i = 1; i < imgs.length; i++) { 98 | 99 | response.append(predictPicClasses(ImageProcessor.imageToBytes(imgs[i])) + "\t"); 100 | if (i == 4) 101 | response.append("\n"); 102 | } 103 | } else { 104 | response.append("image handling exception"); 105 | } 106 | response.append("\n"); 107 | } 108 | } 109 | 110 | private static class TopThread extends Thread { 111 | private BufferedImage top; 112 | private StringBuilder response; 113 | //private String name; 114 | 115 | TopThread(BufferedImage top, StringBuilder response) { 116 | this.top = top; 117 | this.response = response; 118 | //this.name = name; 119 | } 120 | 121 | @Override 122 | public void run() { 123 | /*System.out.println("top thread"); 124 | String[] arguments = new String[] {"python", "/usr/java/project/verify/img.py",name}; 125 | Process process = null; 126 | try { 127 | process = Runtime.getRuntime().exec(arguments); 128 | } catch (IOException e) { 129 | e.printStackTrace(); 130 | } 131 | System.out.println("top stop")*/ 132 | ; 133 | response.append("标签从左到右依次是:\n"); 134 | BufferedImage[] imgs = ImageProcessor.cutAgain(top); 135 | if (imgs != null && imgs.length > 0) { 136 | for (int i = 0; i < imgs.length; i++) { 137 | if (imgs[i] != null) { 138 | response.append(predictTopClasses(ImageProcessor.imageToBytes(imgs[i])) + "\t"); 139 | } 140 | } 141 | } else { 142 | response.append("image handling exception"); 143 | } 144 | response.append("\n"); 145 | 146 | } 147 | } 148 | private static String same(BufferedImage[] imgs){ 149 | StringBuilder response1 = new StringBuilder(); 150 | StringBuilder response2 = new StringBuilder(); 151 | PicThread picThread = new PicThread(imgs, response1); 152 | TopThread topThread = new TopThread(imgs[0], response2); 153 | picThread.start(); 154 | topThread.start(); 155 | try { 156 | picThread.join(); 157 | topThread.join(); 158 | } catch (InterruptedException e) { 159 | e.printStackTrace(); 160 | } 161 | return response2.toString() + "\n" + response1.toString(); 162 | } 163 | private static String process(BufferedImage image){ 164 | BufferedImage[] imgs = ImageProcessor.cutImage(image); 165 | return same(imgs); 166 | } 167 | private static String process(byte[] img) { 168 | BufferedImage[] imgs = ImageProcessor.cutImage(img); 169 | /*String uuid = UUID.randomUUID().toString().replaceAll("-", ""); 170 | try { 171 | ImageIO.write(imgs[0], "jpg", new File(uuid + ".jpg")); 172 | } catch (IOException e) { 173 | e.printStackTrace(); 174 | }*/ 175 | 176 | return same(imgs); 177 | } 178 | private static void printUsage(PrintStream s) { 179 | s.println("Usage: java -jar java12306.jar image image_path"); 180 | s.println(" java -jar java12306.jar base64 base64_code"); 181 | } 182 | public static void main(String[] args) { 183 | printUsage(System.err); 184 | 185 | if(args.length!=2){ 186 | System.err.println("参数数目错误,请查看Usage"); 187 | System.exit(0); 188 | } 189 | String type=args[0]; 190 | String value=args[1]; 191 | switch (type){ 192 | case "image":{ 193 | File file=new File(value); 194 | if(!file.exists()){ 195 | System.err.println("文件不存在"); 196 | System.exit(0); 197 | } 198 | BufferedImage image=null; 199 | try { 200 | image=ImageIO.read(file); 201 | } catch (IOException e) { 202 | System.err.println("文件读取失败"); 203 | System.exit(0); 204 | } 205 | if(image!=null) 206 | System.out.println(process(image)); 207 | 208 | }break; 209 | case "base64":{ 210 | if(!VerifyService.isBase64(value)){ 211 | System.err.println("base64 code错误"); 212 | System.exit(0); 213 | } 214 | System.out.println(process(Base64.getDecoder().decode(value))); 215 | }break; 216 | default:{ 217 | System.err.println("参数值错误,请查看Usage"); 218 | System.exit(0); 219 | } 220 | } 221 | 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/cooooode/java12306/util/ImageProcessor.java: -------------------------------------------------------------------------------- 1 | package com.cooooode.java12306.util; 2 | 3 | //import org.opencv.core.*; 4 | 5 | //import org.opencv.imgcodecs.Imgcodecs; 6 | 7 | import javax.imageio.ImageIO; 8 | import java.awt.*; 9 | import java.awt.image.BufferedImage; 10 | import java.io.ByteArrayInputStream; 11 | import java.io.ByteArrayOutputStream; 12 | import java.io.IOException; 13 | import java.util.ArrayList; 14 | 15 | //import java.awt.image.DataBufferByte; 16 | //import java.io.File; 17 | //import java.util.UUID; 18 | /* 19 | 20 | import static org.opencv.core.Core.REDUCE_AVG; 21 | import static org.opencv.imgproc.Imgproc.COLOR_RGB2GRAY; 22 | */ 23 | 24 | /** 25 | * @program: verify 26 | * @description: 27 | * @author: vua 28 | * @create: 2020-02-07 15:28 29 | */ 30 | public class ImageProcessor { 31 | static { 32 | //System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 33 | } 34 | /*切图坐标*/ 35 | 36 | static final ArrayList position = new ArrayList() {{ 37 | add(new int[]{0, 30, 120, 290}); 38 | add(new int[]{41, 108, 5, 73}); 39 | add(new int[]{41, 108, 77, 145}); 40 | add(new int[]{41, 108, 149, 217}); 41 | add(new int[]{41, 108, 221, 289}); 42 | add(new int[]{113, 180, 5, 73}); 43 | add(new int[]{113, 180, 77, 145}); 44 | add(new int[]{113, 180, 149, 217}); 45 | add(new int[]{113, 180, 221, 289}); 46 | }}; 47 | /*切图*/ 48 | public static BufferedImage[] cutImage(BufferedImage image){ 49 | BufferedImage imgs[]=null; 50 | int chunks = 9; 51 | imgs = new BufferedImage[chunks]; 52 | Image o = image.getScaledInstance(image.getWidth(), 53 | image.getHeight(), Image.SCALE_SMOOTH); 54 | 55 | int[] temp; 56 | for (int i = 0; i < position.size(); i++) { 57 | temp = position.get(i); 58 | int h = temp[1] - temp[0];//x 59 | int w = temp[3] - temp[2];//y 60 | 61 | imgs[i] = new BufferedImage(w, h, image.getType()); 62 | Graphics2D gr = imgs[i].createGraphics(); 63 | gr.drawImage(o, 0, 0, w, h, 64 | temp[2], temp[0], temp[3], temp[1], null); 65 | gr.dispose(); 66 | } 67 | return imgs; 68 | } 69 | public static BufferedImage[] cutImage(byte[] byteImg) { 70 | BufferedImage imgs[]=null; 71 | try ( 72 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteImg)) { 73 | BufferedImage image = ImageIO.read(byteArrayInputStream); 74 | imgs=cutImage(image); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | return imgs; 79 | } 80 | /** 81 | * BufferedImage转byte[] 82 | * 83 | * @param bImage BufferedImage对象 84 | * @return byte[] 85 | * @auth zhy 86 | */ 87 | public static byte[] imageToBytes(BufferedImage bImage) { 88 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 89 | try { 90 | ImageIO.write(bImage, "png", out); 91 | } catch (IOException e) { 92 | //log.error(e.getMessage()); 93 | } 94 | return out.toByteArray(); 95 | } 96 | /*public static Mat BufImg2Mat (BufferedImage original, int imgType, int matType) { 97 | if (original == null) { 98 | throw new IllegalArgumentException("original == null"); 99 | } 100 | 101 | // Don't convert if it already has correct type 102 | if (original.getType() != imgType) { 103 | 104 | // Create a buffered image 105 | BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), imgType); 106 | 107 | // Draw the image onto the new buffer 108 | Graphics2D g = image.createGraphics(); 109 | try { 110 | g.setComposite(AlphaComposite.Src); 111 | g.drawImage(original, 0, 0, null); 112 | } finally { 113 | g.dispose(); 114 | } 115 | } 116 | 117 | byte[] pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData(); 118 | Mat mat = new Mat(original.getHeight(), original.getWidth(), matType); 119 | mat.put(0, 0, pixels); 120 | return mat; 121 | } 122 | public static byte[] mat2Byte(Mat matrix) { 123 | MatOfByte mob = new MatOfByte(); 124 | Imgcodecs.imencode(".jpg", matrix, mob); 125 | byte[] byteArray = mob.toArray(); 126 | return byteArray; 127 | } 128 | 129 | public static void cutAgain(BufferedImage top){ 130 | Mat mat=BufImg2Mat(top,BufferedImage.TYPE_3BYTE_BGR, CvType.CV_8UC3); 131 | Mat avg=new Mat(1,mat.cols(),CvType.CV_64F,new Scalar(0)); 132 | Core.reduce(mat,avg,0,REDUCE_AVG); 133 | int maxIndex=0; 134 | double maxVal=0; 135 | for(int i=30;i<70;i++){ 136 | if(avg.get(0,i)[0]>maxVal){ 137 | maxVal=avg.get(0,i)[0]; 138 | maxIndex=i; 139 | } 140 | } 141 | ArrayList imgs=new ArrayList<>(); 142 | if(maxIndex<60){ 143 | Mat out=new Mat(30,60,CvType.CV_8UC3,new Scalar(255)); 144 | 145 | for(int i=0;i<30;i++){ 146 | for(int j=0;j60) index=60; 174 | BufferedImage[] images=new BufferedImage[2]; 175 | BufferedImage img = new BufferedImage(60,30 , top.getType()); 176 | Graphics2D gr = img.createGraphics(); 177 | gr.setBackground(new Color(255,255,255)); 178 | gr.fillRect(index, 0, 60-index, 30); 179 | 180 | gr.drawImage(o, 0, 0, index, 30, 181 | 0,0,index,30, null); 182 | gr.dispose(); 183 | images[0]=img; 184 | int idx=0; 185 | for(int i=index+30;ival){ 229 | // System.out.println(image.getRGB(i,j)&0xff); 230 | image.setRGB(i,j,0x00ffffff); 231 | }else{ 232 | 233 | image.setRGB(i,j,0x0); 234 | } 235 | } 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/main/java/com/cooooode/java12306/util/LabelImage.java: -------------------------------------------------------------------------------- 1 | package com.cooooode.java12306.util; 2 | 3 | /* Copyright 2016 The TensorFlow Authors. All Rights Reserved. 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | ==============================================================================*/ 14 | 15 | import java.io.*; 16 | import java.nio.charset.Charset; 17 | import java.nio.file.Files; 18 | import java.nio.file.Path; 19 | import java.nio.file.Paths; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | 24 | import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils; 25 | import org.tensorflow.DataType; 26 | import org.tensorflow.Graph; 27 | import org.tensorflow.Output; 28 | import org.tensorflow.Session; 29 | import org.tensorflow.Tensor; 30 | import org.tensorflow.TensorFlow; 31 | import org.tensorflow.types.UInt8; 32 | 33 | /** 34 | * Sample use of the TensorFlow Java API to label images using a pre-trained model. 35 | */ 36 | public class LabelImage { 37 | private static void printUsage(PrintStream s) { 38 | final String url = 39 | "https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip"; 40 | s.println( 41 | "Java program that uses a pre-trained Inception model (http://arxiv.org/abs/1512.00567)"); 42 | s.println("to label JPEG images."); 43 | s.println("TensorFlow version: " + TensorFlow.version()); 44 | s.println(); 45 | s.println("Usage: label_image "); 46 | s.println(); 47 | s.println("Where:"); 48 | s.println(" is a directory containing the unzipped contents of the inception model"); 49 | s.println(" (from " + url + ")"); 50 | s.println(" is the path to a JPEG image file"); 51 | } 52 | 53 | public static void main(String[] args) { 54 | if (args.length != 2) { 55 | printUsage(System.err); 56 | System.exit(1); 57 | } 58 | // src/main/resources/model/ 59 | // src/main/resources/珊瑚_2e9dc6067795507dad46c039a1c8ebe8.jpg 60 | String modelDir = args[0]; 61 | String imageFile = args[1]; 62 | 63 | byte[] graphDef = readAllBytesOrExit(Paths.get(modelDir, "train-mobilenet-pic.pb")); 64 | List labels = 65 | readAllLinesOrExit(Paths.get(modelDir, "label.txt")); 66 | byte[] imageBytes = readAllBytesOrExit(Paths.get(imageFile)); 67 | 68 | try (Tensor image = constructAndExecuteGraphToNormalizeImage(imageBytes, 67, 68)) { 69 | long[] size = image.shape(); 70 | for (int i = 0; i < size.length; i++) { 71 | System.out.print(size[i] + "\t"); 72 | } 73 | float[] labelProbabilities = executeInceptionGraph(graphDef, image, "", ""); 74 | int bestLabelIdx = maxIndex(labelProbabilities); 75 | System.out.println( 76 | String.format("BEST MATCH: %s (%.2f%% likely)", 77 | labels.get(bestLabelIdx), 78 | labelProbabilities[bestLabelIdx] * 100f)); 79 | } 80 | } 81 | 82 | public static Tensor constructAndExecuteGraphToNormalizeImage(byte[] imageBytes, int h, int w) { 83 | try (Graph g = new Graph()) { 84 | GraphBuilder b = new GraphBuilder(g); 85 | // Some constants specific to the pre-trained model at: 86 | // https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip 87 | // 88 | // - The model was trained with images scaled to 224x224 pixels. 89 | // - The colors, represented as R, G, B in 1-byte each were converted to 90 | // float using (value - Mean)/Scale. 91 | final int H = h; 92 | final int W = w; 93 | final float mean = 0f; 94 | final float scale = 255f; 95 | 96 | // Since the graph is being constructed once per execution here, we can use a constant for the 97 | // input image. If the graph were to be re-used for multiple input images, a placeholder would 98 | // have been more appropriate. 99 | final Output input = b.constant("input", imageBytes); 100 | final Output output = 101 | b.div( 102 | b.sub( 103 | b.resizeBilinear( 104 | b.expandDims( 105 | b.cast(b.decodeJpeg(input, 1), Float.class), 106 | b.constant("make_batch", 0)), 107 | b.constant("size", new int[]{H, W})), 108 | b.constant("mean", mean)), 109 | b.constant("scale", scale)); 110 | try (Session s = new Session(g)) { 111 | // Generally, there may be multiple output tensors, all of them must be closed to prevent resource leaks. 112 | return s.runner().fetch(output.op().name()).run().get(0).expect(Float.class); 113 | } 114 | } 115 | } 116 | 117 | public static float[] executeInceptionGraph(byte[] graphDef, Tensor image, 118 | String inputOperation, String outputOperation) { 119 | try (Graph g = new Graph()) { 120 | g.importGraphDef(graphDef); 121 | /*g.operations().forEachRemaining(o->{ 122 | System.out.println(o.name()); 123 | });*/ 124 | 125 | try (Session s = new Session(g); 126 | // Generally, there may be multiple output tensors, all of them must be closed to prevent resource leaks. 127 | Tensor result = 128 | s.runner().feed(inputOperation, image).fetch(outputOperation).run().get(0).expect(Float.class)) { 129 | final long[] rshape = result.shape(); 130 | if (result.numDimensions() != 2 || rshape[0] != 1) { 131 | throw new RuntimeException( 132 | String.format( 133 | "Expected model to produce a [1 N] shaped tensor where N is the number of labels, instead it produced one with shape %s", 134 | Arrays.toString(rshape))); 135 | } 136 | int nlabels = (int) rshape[1]; 137 | return result.copyTo(new float[1][nlabels])[0]; 138 | } 139 | } 140 | } 141 | 142 | public static int maxIndex(float[] probabilities) { 143 | int best = 0; 144 | for (int i = 1; i < probabilities.length; ++i) { 145 | if (probabilities[i] > probabilities[best]) { 146 | best = i; 147 | } 148 | } 149 | return best; 150 | } 151 | 152 | public static byte[] readAllBytesOrExit(Path path) { 153 | try { 154 | return Files.readAllBytes(path); 155 | } catch (IOException e) { 156 | System.err.println("Failed to read [" + path + "]: " + e.getMessage()); 157 | System.exit(1); 158 | } 159 | return null; 160 | } 161 | 162 | public static byte[] readAllBytesOrExit(InputStream in) { 163 | try { 164 | return inputStream2byte(in); 165 | } catch (IOException e) { 166 | System.exit(1); 167 | } 168 | return null; 169 | } 170 | 171 | public static byte[] inputStream2byte(InputStream inputStream) throws IOException { 172 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 173 | byte[] buff = new byte[1024]; 174 | int rc = 0; 175 | while ((rc = inputStream.read(buff, 0, 1024)) > 0) { 176 | byteArrayOutputStream.write(buff, 0, rc); 177 | } 178 | return byteArrayOutputStream.toByteArray(); 179 | } 180 | 181 | public static List readAllLinesOrExit(InputStream in) { 182 | BufferedReader reader = new BufferedReader(new InputStreamReader(new BufferedInputStream(in))); 183 | List res = new ArrayList<>(); 184 | String temp; 185 | try { 186 | while ((temp = reader.readLine()) != null) 187 | res.add(temp); 188 | } catch (IOException E) { 189 | System.exit(0); 190 | } 191 | return res; 192 | } 193 | 194 | 195 | public static List readAllLinesOrExit(Path path) { 196 | try { 197 | return Files.readAllLines(path, Charset.forName("UTF-8")); 198 | } catch (IOException e) { 199 | System.err.println("Failed to read [" + path + "]: " + e.getMessage()); 200 | System.exit(0); 201 | } 202 | return null; 203 | } 204 | 205 | // In the fullness of time, equivalents of the methods of this class should be auto-generated from 206 | // the OpDefs linked into libtensorflow_jni.so. That would match what is done in other languages 207 | // like Python, C++ and Go. 208 | static class GraphBuilder { 209 | GraphBuilder(Graph g) { 210 | this.g = g; 211 | } 212 | 213 | Output div(Output x, Output y) { 214 | return binaryOp("Div", x, y); 215 | } 216 | 217 | Output sub(Output x, Output y) { 218 | return binaryOp("Sub", x, y); 219 | } 220 | 221 | Output resizeBilinear(Output images, Output size) { 222 | return binaryOp3("ResizeBilinear", images, size); 223 | } 224 | 225 | Output expandDims(Output input, Output dim) { 226 | return binaryOp3("ExpandDims", input, dim); 227 | } 228 | 229 | Output cast(Output value, Class type) { 230 | DataType dtype = DataType.fromClass(type); 231 | return g.opBuilder("Cast", "Cast") 232 | .addInput(value) 233 | .setAttr("DstT", dtype) 234 | .build() 235 | .output(0); 236 | } 237 | 238 | Output decodeJpeg(Output contents, long channels) { 239 | return g.opBuilder("DecodeJpeg", "DecodeJpeg") 240 | .addInput(contents) 241 | .setAttr("channels", channels) 242 | .build() 243 | .output(0); 244 | } 245 | 246 | Output constant(String name, Object value, Class type) { 247 | try (Tensor t = Tensor.create(value, type)) { 248 | return g.opBuilder("Const", name) 249 | .setAttr("dtype", DataType.fromClass(type)) 250 | .setAttr("value", t) 251 | .build() 252 | .output(0); 253 | } 254 | } 255 | 256 | Output constant(String name, byte[] value) { 257 | return this.constant(name, value, String.class); 258 | } 259 | 260 | Output constant(String name, int value) { 261 | return this.constant(name, value, Integer.class); 262 | } 263 | 264 | Output constant(String name, int[] value) { 265 | return this.constant(name, value, Integer.class); 266 | } 267 | 268 | Output constant(String name, float value) { 269 | return this.constant(name, value, Float.class); 270 | } 271 | 272 | private Output binaryOp(String type, Output in1, Output in2) { 273 | return g.opBuilder(type, type).addInput(in1).addInput(in2).build().output(0); 274 | } 275 | 276 | private Output binaryOp3(String type, Output in1, Output in2) { 277 | return g.opBuilder(type, type).addInput(in1).addInput(in2).build().output(0); 278 | } 279 | 280 | private Graph g; 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/main/java/org/tensorflow/NativeLibrary.java: -------------------------------------------------------------------------------- 1 | /* Copyright 2017 The TensorFlow Authors. All Rights Reserved. 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | Unless required by applicable law or agreed to in writing, software 7 | distributed under the License is distributed on an "AS IS" BASIS, 8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | See the License for the specific language governing permissions and 10 | limitations under the License. 11 | ==============================================================================*/ 12 | 13 | package org.tensorflow; 14 | 15 | import java.io.File; 16 | import java.io.FileOutputStream; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | 20 | /** 21 | * Helper class for loading the TensorFlow Java native library. 22 | * 23 | *

The Java TensorFlow bindings require a native (JNI) library. This library 24 | * (libtensorflow_jni.so on Linux, libtensorflow_jni.dylib on OS X, tensorflow_jni.dll on Windows) 25 | * can be made available to the JVM using the java.library.path System property (e.g., using 26 | * -Djava.library.path command-line argument). However, doing so requires an additional step of 27 | * configuration. 28 | * 29 | *

Alternatively, the native libraries can be packaed in a .jar, making them easily usable from 30 | * build systems like Maven. However, in such cases, the native library has to be extracted from the 31 | * .jar archive. 32 | * 33 | *

NativeLibrary.load() takes care of this. First looking for the library in java.library.path 34 | * and failing that, it tries to find the OS and architecture specific version of the library in the 35 | * set of ClassLoader resources (under org/tensorflow/native/OS-ARCH). The resources paths used for 36 | * lookup must be consistent with any packaging (such as on Maven Central) of the TensorFlow Java 37 | * native libraries. 38 | */ 39 | final class NativeLibrary { 40 | private static final boolean DEBUG = 41 | System.getProperty("org.tensorflow.NativeLibrary.DEBUG") != null; 42 | private static final String JNI_LIBNAME = "tensorflow_jni"; 43 | 44 | public static void load() { 45 | if (isLoaded() || tryLoadLibrary()) { 46 | // Either: 47 | // (1) The native library has already been statically loaded, OR 48 | // (2) The required native code has been statically linked (through a custom launcher), OR 49 | // (3) The native code is part of another library (such as an application-level library) 50 | // that has already been loaded. For example, tensorflow/examples/android and 51 | // tensorflow/tools/android/inference_interface include the required native code in 52 | // differently named libraries. 53 | // 54 | // Doesn't matter how, but it seems the native code is loaded, so nothing else to do. 55 | return; 56 | } 57 | // Native code is not present, perhaps it has been packaged into the .jar file containing this. 58 | // Extract the JNI library itself 59 | final String jniLibName = System.mapLibraryName(JNI_LIBNAME); 60 | final String jniResourceName = makeResourceName(jniLibName); 61 | log("jniResourceName: " + jniResourceName); 62 | final InputStream jniResource = 63 | NativeLibrary.class.getClassLoader().getResourceAsStream(jniResourceName); 64 | // Extract the JNI's dependency 65 | final String frameworkLibName = 66 | getVersionedLibraryName(System.mapLibraryName("tensorflow_framework")); 67 | final String frameworkResourceName = makeResourceName(frameworkLibName); 68 | log("frameworkResourceName: " + frameworkResourceName); 69 | final InputStream frameworkResource = 70 | NativeLibrary.class.getClassLoader().getResourceAsStream(frameworkResourceName); 71 | // Do not complain if the framework resource wasn't found. This may just mean that we're 72 | // building with --config=monolithic (in which case it's not needed and not included). 73 | if (jniResource == null) { 74 | throw new UnsatisfiedLinkError( 75 | String.format( 76 | "Cannot find TensorFlow native library for OS: %s, architecture: %s. See " 77 | + "https://github.com/tensorflow/tensorflow/tree/master/tensorflow/java/README.md" 78 | + " for possible solutions (such as building the library from source). Additional" 79 | + " information on attempts to find the native library can be obtained by adding" 80 | + " org.tensorflow.NativeLibrary.DEBUG=1 to the system properties of the JVM.", 81 | os(), architecture())); 82 | } 83 | try { 84 | // Create a temporary directory for the extracted resource and its dependencies. 85 | final File tempPath = createTemporaryDirectory(); 86 | // Deletions are in the reverse order of requests, so we need to request that the directory be 87 | // deleted first, so that it is empty when the request is fulfilled. 88 | tempPath.deleteOnExit(); 89 | final String tempDirectory = tempPath.getCanonicalPath(); 90 | if (frameworkResource != null) { 91 | extractResource(frameworkResource, frameworkLibName, tempDirectory); 92 | } else { 93 | log( 94 | frameworkResourceName 95 | + " not found. This is fine assuming " 96 | + jniResourceName 97 | + " is not built to depend on it."); 98 | } 99 | System.load(extractResource(jniResource, jniLibName, tempDirectory)); 100 | } catch (IOException e) { 101 | throw new UnsatisfiedLinkError( 102 | String.format( 103 | "Unable to extract native library into a temporary file (%s)", e.toString())); 104 | } 105 | } 106 | 107 | private static boolean tryLoadLibrary() { 108 | try { 109 | System.loadLibrary(JNI_LIBNAME); 110 | return true; 111 | } catch (UnsatisfiedLinkError e) { 112 | log("tryLoadLibraryFailed: " + e.getMessage()); 113 | return false; 114 | } 115 | } 116 | 117 | private static boolean isLoaded() { 118 | try { 119 | TensorFlow.version(); 120 | log("isLoaded: true"); 121 | return true; 122 | } catch (UnsatisfiedLinkError e) { 123 | return false; 124 | } 125 | } 126 | 127 | private static boolean resourceExists(String baseName) { 128 | return NativeLibrary.class.getClassLoader().getResource(makeResourceName(baseName)) != null; 129 | } 130 | 131 | private static String getVersionedLibraryName(String libFilename) { 132 | final String versionName = getMajorVersionNumber(); 133 | 134 | // If we're on darwin, the versioned libraries look like blah.1.dylib. 135 | final String darwinSuffix = ".dylib"; 136 | if (libFilename.endsWith(darwinSuffix)) { 137 | final String prefix = libFilename.substring(0, libFilename.length() - darwinSuffix.length()); 138 | if (versionName != null) { 139 | final String darwinVersionedLibrary = prefix + "." + versionName + darwinSuffix; 140 | if (resourceExists(darwinVersionedLibrary)) { 141 | return darwinVersionedLibrary; 142 | } 143 | } else { 144 | // If we're here, we're on darwin, but we couldn't figure out the major version number. We 145 | // already tried the library name without any changes, but let's do one final try for the 146 | // library with a .so suffix. 147 | final String darwinSoName = prefix + ".so"; 148 | if (resourceExists(darwinSoName)) { 149 | return darwinSoName; 150 | } 151 | } 152 | } else if (libFilename.endsWith(".so")) { 153 | // Libraries ending in ".so" are versioned like "libfoo.so.1", so try that. 154 | final String versionedSoName = libFilename + "." + versionName; 155 | if (versionName != null && resourceExists(versionedSoName)) { 156 | return versionedSoName; 157 | } 158 | } 159 | 160 | // Otherwise, we've got no idea. 161 | return libFilename; 162 | } 163 | 164 | /** 165 | * Returns the major version number of this TensorFlow Java API, or {@code null} if it cannot be 166 | * determined. 167 | */ 168 | private static String getMajorVersionNumber() { 169 | String version = NativeLibrary.class.getPackage().getImplementationVersion(); 170 | // expecting a string like 1.14.0, we want to get the first '1'. 171 | int dotIndex; 172 | if (version == null || (dotIndex = version.indexOf('.')) == -1) { 173 | return "1"; 174 | } 175 | String majorVersion = version.substring(0, dotIndex); 176 | try { 177 | Integer.parseInt(majorVersion); 178 | return majorVersion; 179 | } catch (NumberFormatException unused) { 180 | return null; 181 | } 182 | } 183 | 184 | private static String extractResource( 185 | InputStream resource, String resourceName, String extractToDirectory) throws IOException { 186 | final File dst = new File(extractToDirectory, resourceName); 187 | dst.deleteOnExit(); 188 | final String dstPath = dst.toString(); 189 | log("extracting native library to: " + dstPath); 190 | final long nbytes = copy(resource, dst); 191 | log(String.format("copied %d bytes to %s", nbytes, dstPath)); 192 | return dstPath; 193 | } 194 | 195 | private static String os() { 196 | final String p = System.getProperty("os.name").toLowerCase(); 197 | if (p.contains("linux")) { 198 | return "linux"; 199 | } else if (p.contains("os x") || p.contains("darwin")) { 200 | return "darwin"; 201 | } else if (p.contains("windows")) { 202 | return "windows"; 203 | } else { 204 | return p.replaceAll("\\s", ""); 205 | } 206 | } 207 | 208 | private static String architecture() { 209 | final String arch = System.getProperty("os.arch").toLowerCase(); 210 | return (arch.equals("amd64")) ? "x86_64" : arch; 211 | } 212 | 213 | private static void log(String msg) { 214 | if (DEBUG) { 215 | System.err.println("org.tensorflow.NativeLibrary: " + msg); 216 | } 217 | } 218 | 219 | private static String makeResourceName(String baseName) { 220 | return "org/tensorflow/native/" + String.format("%s-%s/", os(), architecture()) + baseName; 221 | } 222 | 223 | private static long copy(InputStream src, File dstFile) throws IOException { 224 | FileOutputStream dst = new FileOutputStream(dstFile); 225 | try { 226 | byte[] buffer = new byte[1 << 20]; // 1MB 227 | long ret = 0; 228 | int n = 0; 229 | while ((n = src.read(buffer)) >= 0) { 230 | dst.write(buffer, 0, n); 231 | ret += n; 232 | } 233 | return ret; 234 | } finally { 235 | dst.close(); 236 | src.close(); 237 | } 238 | } 239 | 240 | // Shamelessly adapted from Guava to avoid using java.nio, for Android API 241 | // compatibility. 242 | private static File createTemporaryDirectory() { 243 | File baseDirectory = new File(System.getProperty("java.io.tmpdir")); 244 | String directoryName = "tensorflow_native_libraries-" + System.currentTimeMillis() + "-"; 245 | for (int attempt = 0; attempt < 1000; attempt++) { 246 | File temporaryDirectory = new File(baseDirectory, directoryName + attempt); 247 | if (temporaryDirectory.mkdir()) { 248 | return temporaryDirectory; 249 | } 250 | } 251 | throw new IllegalStateException( 252 | "Could not create a temporary directory (tried to make " 253 | + directoryName 254 | + "*) to extract TensorFlow native libraries."); 255 | } 256 | 257 | private NativeLibrary() {} 258 | } 259 | -------------------------------------------------------------------------------- /src/main/resources/model/label.txt: -------------------------------------------------------------------------------- 1 | 双面胶 2 | 红酒 3 | 珊瑚 4 | 鞭炮 5 | 安全帽 6 | 拖把 7 | 挂钟 8 | 沙拉 9 | 红豆 10 | 龙舟 11 | 电线 12 | 蚂蚁 13 | 红枣 14 | 海苔 15 | 文具盒 16 | 啤酒 17 | 苍蝇拍 18 | 耳塞 19 | 档案袋 20 | 雨靴 21 | 手掌印 22 | 菠萝 23 | 烛台 24 | 高压锅 25 | 樱桃 26 | 蜡烛 27 | 漏 斗 28 | 本子 29 | 蜥蜴 30 | 排风机 31 | 订书机 32 | 调色板 33 | 茶几 34 | 薯条 35 | 钟表 36 | 锣 37 | 盘子 38 | 海报 39 | 锅铲 40 | 冰箱 41 | 电子秤 42 | 毛线 43 | 辣椒酱 44 | 刺绣 45 | 创可贴 46 | 药片 47 | 老虎 48 | 茶盅 49 | 路灯 50 | 卷尺 51 | 剪纸 52 | 中国结 53 | 口哨 54 | 热水袋 55 | 话梅 56 | 牌坊 57 | 风铃 58 | 篮球 59 | 绿豆 60 | 护腕 61 | 开瓶器 62 | 铃铛 63 | 打字机 64 | 网球拍 65 | 日历 66 | 公交卡 67 | 海鸥 68 | 沙包 69 | 印章 70 | 电饭煲 71 | 狮子 72 | 蜜蜂 73 | 黑板 74 | 跑步机 75 | 金字塔 76 | 仪表盘 77 | 蒸笼 78 | 棉棒 79 | 航母 80 | 锦旗 -------------------------------------------------------------------------------- /src/main/resources/model/lenet-top.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vua/Java12306/2f8cd9fc8fbc0d8174e01d54c7bb51b18e6d597f/src/main/resources/model/lenet-top.pb -------------------------------------------------------------------------------- /src/main/resources/model/mobilenet-pic.pb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vua/Java12306/2f8cd9fc8fbc0d8174e01d54c7bb51b18e6d597f/src/main/resources/model/mobilenet-pic.pb --------------------------------------------------------------------------------