我在云服务器上用Flask和tornado部署了一个用于图像识别的服务,如果直接在浏览器输入ip地址+端口号的话,可以访问指定的网页,并且上传图片点击计算,服务器端都有响应,返回该图片的分类以及相似概率等数据。但是在安卓端上即使ip地址设置正确,点击上传图片也会显示“未连接服务器”。希望有大佬可以帮忙解决下这个问题,代码放在下面了。
服务器端代码:
(老师给的陈年老代码了……python2环境下的……)
import os import json import time import cPickle import random import datetime import logging import flask import werkzeug import optparse import tornado.wsgi import tornado.httpserver import numpy as np import pandas as pd from PIL import Image import cStringIO as StringIO import urllib import glob import cv2 #import lmdb import caffe from caffe.proto import caffe_pb2 from urllib import urlencode from urllib import quote from class_labels import labels import pymysql from werkzeug.utils import secure_filename DATA_FOLDER = '/input/cat-flask/ResNet-50' UPLOAD_FOLDER = '/input/cat-flask/uploads' THUMBNAIL_FOLDER = '/input/cat-flask/thumbnails' ALLOWED_IMAGE_EXTENSIONS = set(['png', 'bmp', 'jpg', 'jpe', 'jpeg', 'gif']) IMAGE_WIDTH = 224 IMAGE_HEIGHT = 224 baseurl = 'http://bob.geeekvr.com' # Obtain the flask app object app = flask.Flask(__name__) @app.route('/') def index(): return flask.render_template('index.html', has_result=False) @app.route('/images/<path:path>') def send_images(path): return flask.send_from_directory('images', path) @app.route('/uploads/<path:path>') def send_uploads(path): return flask.send_from_directory('uploads', path) @app.route('/thumbnails/<path:path>') def send_thumbnails(path): return flask.send_from_directory('thumbnails', path) @app.route('/classify_upload', methods=['POST']) def classify_upload(): try: sdkurl = flask.request.form.get('sdkurl', type=str, default=baseurl) imagefile = flask.request.files['imagefile'] filename_ = (datetime.datetime.now()).strftime("%Y%m%d%H%M%S") + '_' + \ secure_filename(imagefile.filename) filename = os.path.join(UPLOAD_FOLDER, filename_) imagefile.save(filename) logging.info('Saving to %s.', filename) image = cv2.imread(filename, cv2.IMREAD_COLOR) res = cv2.resize(image, (64,64), interpolation=cv2.INTER_CUBIC) thumbnail = os.path.join(THUMBNAIL_FOLDER, filename_) cv2.imwrite(thumbnail, res) except Exception as err: logging.info('Uploaded image open error: %s', err) result = {"errcode":1, "errmsg":"Cannot open uploaded image."} return flask.jsonify(result) result = app.clf.classify_image(image, sdkurl) if result["errcode"] == 0: try: result["imgurl"] = sdkurl+"/uploads/"+urllib.quote(filename_) source = flask.request.form.get('source', type=str, default='-1') usertoken = flask.request.form.get('usertoken', type=str, default='') catsid0 = -1; catsid1 = -1; catsid2 = -1; if result["count"] > 0: catsid0 = result["result"][0]["id"] if result["count"] > 1: catsid1 = result["result"][1]["id"] if result["count"] > 2: catsid2 = result["result"][2]["id"] db = pymysql.connect( \ "localhost",\ "kando",\ "1997416",\ "CAT777") cursor = db.cursor() sql = "insert into cats_image( \ uid, filename, thumbnail, source,\ catsid0, catsid1, catsid2,\ createtime) values(%s, '%s', '%s', %s, %s, %s, %s, now())" % \ (0, "uploads/"+filename_, "thumbnails/"+filename_, source, \ catsid0, catsid1, catsid2) print(sql) try: cursor.execute(sql) db.commit() except Exception as err: print(err) db.rollback() db.close() except Exception as err: print(err) else: print("errcode="+str(errcode)) return flask.jsonify(result) def embed_image_html(image): """Creates an image embedded in HTML base64 format.""" image_pil = Image.fromarray((255 * image).astype('uint8')) image_pil = image_pil.resize((256, 256)) string_buf = StringIO.StringIO() image_pil.save(string_buf, format='png') data = string_buf.getvalue().encode('base64').replace('\n', '') return 'data:image/png;base64,' + data def allowed_file(filename): return ( '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_IMAGE_EXTENSIONS ) class ImagenetClassifier(object): default_args = { 'deploy_file': ( '{}/deploy.prototxt'.format(DATA_FOLDER)), 'model_file': ( '{}/resnet-50-cervix_iter_50000.caffemodel'.format(DATA_FOLDER)), 'mean_file': ( '{}/imagenet_mean.binaryproto'.format(DATA_FOLDER)), 'labels_file': ( '{}/synset_words.txt'.format(DATA_FOLDER)), } for key, val in default_args.iteritems(): if not os.path.exists(val): raise Exception( "File for {} is missing. Should be at: {}".format(key, val)) default_args['image_dim'] = 256 default_args['raw_scale'] = 255. def __init__(self, deploy_file, model_file, mean_file, raw_scale, labels_file, image_dim, gpu_mode): logging.info('Loading net and associated files...') if gpu_mode: caffe.set_mode_gpu() else: caffe.set_mode_cpu() mean_blob = caffe_pb2.BlobProto() with open(mean_file) as f: mean_blob.ParseFromString(f.read()) mean_array = np.asarray(mean_blob.data, dtype=np.float32).reshape( (mean_blob.channels, mean_blob.height, mean_blob.width)) # load the class labels from disk rows = open(labels_file).read().strip().split("\n") #self.classes = [r[r.find("|") + 1:].split(",")[0] for r in rows] self.classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows] #Read model architecture and trained model's weights self.net = caffe.Net(deploy_file, model_file, caffe.TEST) #Define image transformers self.transformer = caffe.io.Transformer({'data': self.net.blobs['data'].data.shape}) self.transformer.set_mean('data', mean_array) self.transformer.set_transpose('data', (2,0,1)) def transform_img(img, img_width=IMAGE_WIDTH, img_height=IMAGE_HEIGHT): #Histogram Equalization img[:, :, 0] = cv2.equalizeHist(img[:, :, 0]) img[:, :, 1] = cv2.equalizeHist(img[:, :, 1]) img[:, :, 2] = cv2.equalizeHist(img[:, :, 2]) #Image Resizing img = cv2.resize(img, (img_width, img_height), interpolation = cv2.INTER_CUBIC) return img def classify_image(self, image, sdkurl): try: logging.info('classify_image') #image = self.transform_img(image) #, img_width=IMAGE_WIDTH, img_height=IMAGE_HEIGHT) self.net.blobs['data'].data[...] = self.transformer.preprocess('data', image) starttime = time.time() out = self.net.forward() endtime = time.time() logging.info('classification took {:.5} seconds'.format(endtime - starttime)) preds = out['prob'] # sort the indexes of the probabilities in descending order (higher # probabilitiy first) and grab the top-5 predictions idxs = np.argsort(preds[0])[::-1][:5] # loop over the top-5 predictions and display them results = [] for (i, idx) in enumerate(idxs): prob = preds[0][idx] if prob < 0.10: break classes = self.classes[idx].replace(" ", ","); logging.info("{} {} {:.5}".format(idx, labels[idx], prob)) result = {} result["id"] = idx result["name"] = labels[idx] result["imgurl"] = sdkurl+"/images/"+urllib.quote(labels[idx])+".jpg" result["percent"] = "{:.3}".format(prob*100) results.append(result) data = {"errcode":0, "count":len(results), "result":results} logging.info("Result:\n%s", json.dumps(data)) return data except Exception as err: logging.info('Classification error: %s', err) result = {"errcode":1, "errmsg":err} return data def start_tornado(app, port=28070): http_server = tornado.httpserver.HTTPServer( tornado.wsgi.WSGIContainer(app)) baseurl = "http://bob.geeekvr.com:"+str(port) http_server.listen(port) print("Tornado server starting on port {}".format(port)) tornado.ioloop.IOLoop.instance().start() def start_from_terminal(app): """ Parse command line options and start the server. """ parser = optparse.OptionParser() parser.add_option( '-d', '--debug', help="enable debug mode", action="store_true", default=False) parser.add_option( '-p', '--port', help="which port to serve content on", type='int', default=28070) parser.add_option( '-g', '--gpu', help="use gpu mode", action='store_true', default=False) opts, args = parser.parse_args() ImagenetClassifier.default_args.update({'gpu_mode': opts.gpu}) # Initialize classifier + warm start by forward for allocation app.clf = ImagenetClassifier(**ImagenetClassifier.default_args) app.clf.net.forward() if opts.debug: app.run(debug=True, host='0.0.0.0', port=opts.port) else: start_tornado(app, opts.port) if __name__ == '__main__': logging.getLogger().setLevel(logging.INFO) if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) if not os.path.exists(THUMBNAIL_FOLDER): os.makedirs(THUMBNAIL_FOLDER) start_from_terminal(app)
安卓端部分代码:
设置端口号:
package com.example.birdrecognition.app; import android.app.Application; import android.content.res.Configuration; public class MyApplication extends Application { private String sdkUrl = "http://bob.geeekvr.com:28008"; @Override public void onCreate() { super.onCreate(); } @Override public void onTerminate() { super.onTerminate(); } @Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } public void setSdkUrl(String sdkUrl) { this.sdkUrl = sdkUrl; } public String getSdkUrl() { return sdkUrl; } }
package com.example.birdrecognition.sdk; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.Map; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; public class HttpConnect { public static String connect(String httpUrl, String message) throws IOException { if (message==null) { return ""; } Log.i("HttpConnect", "#Request:: "+message); String CONTENT_TYPE = "application/x-www-form-urlencoded"; URL url = null; url = new URL(httpUrl); HttpURLConnection conn = null; conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(30000); conn.setConnectTimeout(30000); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Charset", "utf-8"); conn.setRequestProperty("connection", "keep-alive"); conn.setRequestProperty("Content-Type", CONTENT_TYPE); conn.setRequestProperty ("Content-Length",""+message.length()); conn.connect(); DataOutputStream dos = new DataOutputStream (conn.getOutputStream()); dos.writeBytes(message); dos.flush (); dos.close (); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")); String s = ""; StringBuffer sb = new StringBuffer(""); while ((s = br.readLine()) != null ) { sb.append(s); } String ret = sb.toString(); int res = conn.getResponseCode(); conn.disconnect(); dos = null; conn = null; url = null; Log.i("HttpConnect", "#Response:: "+ret); return ret; } @SuppressWarnings("rawtypes") public static String formUpload(String urlStr, Map<String, String> textMap, Map<String, String> fileMap, String contentType) { String res = ""; HttpURLConnection conn = null; String BOUNDARY = "---------------------------123821742118716"; try { URL url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(30000); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)"); conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY); OutputStream out = new DataOutputStream(conn.getOutputStream()); if (textMap != null) { StringBuffer strBuf = new StringBuffer(); Iterator iter = textMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); String inputName = (String) entry.getKey(); String inputValue = (String) entry.getValue(); if (inputValue == null) { continue; } strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n"); strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"\r\n\r\n"); strBuf.append(inputValue); } out.write(strBuf.toString().getBytes()); } if (fileMap != null) { Iterator iter = fileMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); String inputName = (String) entry.getKey(); String inputValue = (String) entry.getValue(); if (inputValue == null) { continue; } File file = new File(inputValue); String filename = file.getName(); if (contentType == null || "".equals(contentType)) { contentType = "application/octet-stream"; } StringBuffer strBuf = new StringBuffer(); strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n"); strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"; filename=\"" + filename+ "\"\r\n"); strBuf.append("Content-Type:" + contentType + "\r\n\r\n"); out.write(strBuf.toString().getBytes()); DataInputStream in = new DataInputStream(new FileInputStream(file)); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = in.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } in.close(); } } byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes(); out.write(endData); out.flush(); out.close(); StringBuffer strBuf = new StringBuffer(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")); String line = null; while ((line = reader.readLine()) != null) { strBuf.append(line).append("\n"); } res = strBuf.toString(); reader.close(); reader = null; } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); conn = null; } } return res; } @SuppressWarnings("rawtypes") public static String formUploadCompressed(String urlStr, Map<String, String> textMap, Map<String, String> fileMap, String contentType) { String res = ""; HttpURLConnection conn = null; String BOUNDARY = "---------------------------123821742118716"; try { Log.i("HttpConnect", urlStr); URL url = new URL(urlStr); conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setReadTimeout(30000); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)"); conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY); OutputStream out = new DataOutputStream(conn.getOutputStream()); if (textMap != null) { StringBuffer strBuf = new StringBuffer(); Iterator iter = textMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); String inputName = (String) entry.getKey(); String inputValue = (String) entry.getValue(); if (inputValue == null) { continue; } strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n"); strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"\r\n\r\n"); strBuf.append(inputValue); } out.write(strBuf.toString().getBytes()); } if (fileMap != null) { Iterator iter = fileMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); String inputName = (String) entry.getKey(); String inputValue = (String) entry.getValue(); if (inputValue == null) { continue; } File file = new File(inputValue); String filename = file.getName(); long filelength = file.length()/1024; if (contentType == null || "".equals(contentType)) { contentType = "application/octet-stream"; } StringBuffer strBuf = new StringBuffer(); strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n"); strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"; filename=\"" + filename+ "\"\r\n"); strBuf.append("Content-Type:" + contentType + "\r\n\r\n"); out.write(strBuf.toString().getBytes()); BitmapFactory.Options bo=new BitmapFactory.Options(); if (filelength > 2000) { bo.inSampleSize = 4; } else if (filelength > 1000) { bo.inSampleSize = 2; } else { bo.inSampleSize = 1; } Bitmap image = BitmapFactory.decodeFile(inputValue,bo); ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 90; while (baos.toByteArray().length / 1024 > 200 && options > 10 ) { Log.i("HttpConnect", "options: "+options); baos.reset(); image.compress(Bitmap.CompressFormat.JPEG, options, baos); options -= 10; } InputStream is = new ByteArrayInputStream(baos.toByteArray()); byte[] bufferOut = new byte[1024]; int len = 0; while ((len=is.read(bufferOut)) != -1){ out.write(bufferOut, 0, len); } is.close(); } } byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes(); out.write(endData); out.flush(); out.close(); StringBuffer strBuf = new StringBuffer(); BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")); String line = null; while ((line = reader.readLine()) != null) { strBuf.append(line).append("\n"); } res = strBuf.toString(); reader.close(); reader = null; } catch (Exception e) { e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); conn = null; } } return res; } public static String get(String httpUrl) throws IOException { String CONTENT_TYPE = "application/x-www-form-urlencoded"; URL url = null; url = new URL(httpUrl); HttpURLConnection conn = null; conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(30000); conn.setConnectTimeout(30000); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("GET"); conn.setRequestProperty("Charset", "utf-8"); conn.setRequestProperty("connection", "keep-alive"); conn.setRequestProperty("Content-Type", CONTENT_TYPE); conn.connect(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(),"utf-8")); String s = ""; StringBuffer sb = new StringBuffer(""); while ((s = br.readLine()) != null ) { sb.append(s); } String ret = sb.toString(); conn.disconnect(); conn = null; url = null; return ret; } }
package com.example.birdrecognition.sdk; public class ImageUploadRequest extends PublicRequest implements IRequest { private String file; private int source; private String userToken; @Override public String getUrl() { return sdkUrl+"/classify_upload?access_token="+accessToken; } @Override public String getRequestJson() { return null; } public String getFile() { return file; } public void setFile(String file) { this.file = file; } public String getUserToken() { return userToken; } public void setUserToken(String user_token) { this.userToken = userToken; } public void setSource(int source) { this.source = source; } public int getSource() { return source; } }
package com.example.birdrecognition.sdk; import java.util.ArrayList; import org.json.JSONArray; import org.json.JSONObject; public class ImageUploadResponse extends PublicResponse implements IResponse { private int count; private ArrayList<String> id; private ArrayList<String> name; private ArrayList<String> percent; @Override public void parseJSON(String jsonStr) { jsonResponse = jsonStr; if (jsonStr == null || jsonStr.length() == 0) { errcode = 998; errmsg = "返回JSON为空"; } try { JSONObject obj = new JSONObject(jsonStr); errcode = getInt(obj,"errcode",997); errmsg = get(obj,"errmsg","JSON解析错误"); id = new ArrayList<String>(); name = new ArrayList<String>(); percent = new ArrayList<String>(); if (errcode == 0) { count = getInt(obj,"count",0); if (count > 0) { JSONArray resultArray = obj.getJSONArray("result"); int len = resultArray.length(); for(int i=0;i<len;i++) { id.add(get(resultArray.getJSONObject(i),"id","")); name.add(get(resultArray.getJSONObject(i),"name","")); percent.add(get(resultArray.getJSONObject(i),"percent","")); } } } } catch(Exception e) { errcode = 997; errmsg = "未连接服务器"; } } public int getCount() { return count; } public ArrayList<String> getId() { return id; } public ArrayList<String> getName() { return name; } public ArrayList<String> getPercent() { return percent; } }
上面的代码中,服务端和安卓端的ip地址+端口号,其实每次调试时设置的都是相同的,上面忘记改了
安卓连接自己服务器,要开一下自己电脑的防火墙...不然不能访问..
你这云服务器加了https没?新的安全策略不允许http访问