├── .gitignore
├── app.py
├── darknet.py
├── exifutil.py
├── readme.md
├── requirements.txt
├── ssd_detect.py
├── static
└── js
│ └── template.js
└── templates
└── index.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | *.pyc
3 | __pycache__
4 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | #encoding=utf-8
2 | import os
3 | import time
4 | # import cPickle
5 | import datetime
6 | import logging
7 | import flask
8 | import werkzeug
9 | import optparse
10 | import tornado.wsgi
11 | import tornado.httpserver
12 | import numpy as np
13 | import pandas as pd
14 | from PIL import Image, ImageDraw
15 | # import cStringIO as StringIO
16 | try:
17 | import cStringIO as StringIO
18 | except ImportError:
19 | from io import StringIO
20 |
21 | import urllib
22 | import exifutil
23 | import sys
24 | reload(sys)
25 | sys.setdefaultencoding('utf-8') # add this to support Chinese in python2
26 |
27 | # import caffe
28 | import darknet
29 |
30 | REPO_DIRNAME = os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + '/../..')
31 | UPLOAD_FOLDER = '/tmp/objdet_demos_uploads'
32 | ALLOWED_IMAGE_EXTENSIONS = set(['png', 'bmp', 'jpg', 'jpe', 'jpeg', 'gif', 'tif', 'tiff'])
33 |
34 | # Obtain the flask app object
35 | app = flask.Flask(__name__)
36 |
37 |
38 | @app.route('/')
39 | def index():
40 | return flask.render_template('index.html', has_result=False)
41 |
42 | # fyk
43 | def load_img(img_buffer):
44 | # image = caffe.io.load_image(string_buffer)
45 | pass
46 | def disp_wait_msg(imagesrc):
47 | flask.render_template(
48 | 'index.html', has_result=True,
49 | result=(False, '处理图片中...'),
50 | imagesrc=imagesrc
51 | )
52 |
53 |
54 | def draw_rectangle(draw, coordinates, color, width=1, draw_ellipse=False):
55 | for i in range(width):
56 | rect_start = (coordinates[0] - i, coordinates[1] - i)
57 | rect_end = (coordinates[2] + i, coordinates[3] + i)
58 | if draw_ellipse:
59 | draw.ellipse((rect_start, rect_end), outline=color)
60 | else:
61 | draw.rectangle((rect_start, rect_end), outline=color)
62 |
63 |
64 | def draw_rectangles(image_pil,det_result):
65 | # draw rectangles
66 | draw = ImageDraw.Draw(image_pil)
67 | for idx, item in enumerate(det_result):
68 | x, y, w, h = item[2]
69 | half_w = w / 2
70 | half_h = h / 2
71 | box = (int(x - half_w+1), int(y - half_h+1), int(x + half_w+1), int(y + half_h+1))
72 | # draw.rectangle(box, outline=(0, 255, 0))
73 | draw_rectangle(draw,box,(0, 255, 0),width=2,draw_ellipse=True)
74 | # draw.ellipse(box, outline=(255, 0, 0))
75 | draw.text((x - half_w + 5, y - half_h + 5), str(idx + 1)+" : "+item[0], fill=(0, 0, 150))
76 | del draw
77 |
78 |
79 | @app.route('/classify_url', methods=['GET'])
80 | def classify_url():
81 | imageurl = flask.request.args.get('imageurl', '')
82 | try:
83 | # download
84 | raw_data = urllib.urlopen(imageurl).read()
85 | string_buffer = StringIO.StringIO(raw_data)
86 | # image = load_img(string_buffer)
87 | image_pil = Image.open(string_buffer)
88 | filename = os.path.join(UPLOAD_FOLDER, 'tmp.jpg')
89 | with open(filename,'wb') as f:
90 | f.write(raw_data)
91 |
92 | except Exception as err:
93 | # For any exception we encounter in reading the image, we will just
94 | # not continue.
95 | logging.info('URL Image open error: %s', err)
96 | return flask.render_template(
97 | 'index.html', has_result=True,
98 | result=(False, 'Cannot open image from URL.')
99 | )
100 |
101 | logging.info('Image: %s', imageurl)
102 | # img_base64 = embed_image_html(filename)
103 | # disp_wait_msg(img_base64)
104 | results = app.clf.classify_image(filename)
105 | draw_rectangles(image_pil, results[1])
106 | new_img_base64 = embed_image_html(image_pil)
107 | return flask.render_template(
108 | 'index.html', has_result=True, result=results, imagesrc=new_img_base64)
109 | # 'index.html', has_result=True, result=result, imagesrc=imageurl)
110 |
111 |
112 | @app.route('/classify_upload', methods=['POST'])
113 | def classify_upload():
114 | try:
115 | # We will save the file to disk for possible data collection.
116 | imagefile = flask.request.files['imagefile']
117 | filename_ = str(datetime.datetime.now()).replace(' ', '_') + \
118 | werkzeug.secure_filename(imagefile.filename)
119 | filename = os.path.join(UPLOAD_FOLDER, filename_)
120 | imagefile.save(filename)
121 | logging.info('Saving to %s.', filename)
122 | image_pil = exifutil.open_oriented_pil(filename)
123 |
124 | except Exception as err:
125 | logging.info('Uploaded image open error: %s', err)
126 | return flask.render_template(
127 | 'index.html', has_result=True,
128 | result=(False, 'Cannot open uploaded image.')
129 | )
130 | # img_base64 = embed_image_html(image_pil)
131 | # disp_wait_msg(img_base64)
132 | results = app.clf.classify_image(filename)
133 | # [('F22', 0.9006772637367249, (338.6946105957031, 431.28515625, 608.9721069335938, 220.40663146972656)),
134 | # ('F22', 0.890718400478363, (545.9476318359375, 294.4508361816406, 509.1690979003906, 177.72409057617188)),
135 | # ('F22', 0.8847938179969788, (642.2884521484375, 193.6743927001953, 401.5226745605469, 135.20948791503906))]
136 |
137 | draw_rectangles(image_pil, results[1])
138 | new_img_base64 = embed_image_html(image_pil)
139 | # import time
140 | # time.sleep(5) # test
141 | return flask.render_template(
142 | 'index.html', has_result=True, result=results,
143 | imagesrc=new_img_base64
144 | )
145 |
146 |
147 | def embed_image_html(image_pil):
148 | """Creates an image embedded in HTML base64 format."""
149 | # image_pil = Image.fromarray((255 * image).astype('uint8'))
150 | # image_pil = Image.open(image)
151 | size = (512, 512) # (256, 256)
152 | resized = image_pil.resize(size)
153 | string_buf = StringIO.StringIO()
154 | resized.save(string_buf, format='png')
155 | data = string_buf.getvalue().encode('base64').replace('\n', '')
156 | return 'data:image/png;base64,' + data
157 |
158 |
159 | def allowed_file(filename):
160 | return (
161 | '.' in filename and
162 | filename.rsplit('.', 1)[1] in ALLOWED_IMAGE_EXTENSIONS
163 | )
164 |
165 |
166 | class ImagenetClassifier(object):
167 | default_args = {
168 | 'model_def_file': (
169 | '{}/models/bvlc_reference_caffenet/deploy.prototxt'.format(REPO_DIRNAME)),
170 | 'pretrained_model_file': (
171 | '{}/models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel'.format(REPO_DIRNAME)),
172 | 'mean_file': (
173 | '{}/python/caffe/imagenet/ilsvrc_2012_mean.npy'.format(REPO_DIRNAME)),
174 | 'class_labels_file': (
175 | '{}/data/ilsvrc12/synset_words.txt'.format(REPO_DIRNAME)),
176 | 'bet_file': (
177 | '{}/data/ilsvrc12/imagenet.bet.pickle'.format(REPO_DIRNAME)),
178 | }
179 | # for key, val in default_args.iteritems():
180 | # if not os.path.exists(val):
181 | # raise Exception(
182 | # "File for {} is missing. Should be at: {}".format(key, val))
183 | default_args['image_dim'] = 256
184 | default_args['raw_scale'] = 255.
185 |
186 | # fyk 预先加载模型
187 | def __init__(self, model_def_file, pretrained_model_file, mean_file,
188 | raw_scale, class_labels_file, bet_file, image_dim, gpu_mode):
189 | logging.info('Loading net and associated files...')
190 | base_dir = "/home/s05/fyk/darknet-modify/"
191 | self.net = darknet.load_net(base_dir + "PLANE/yolo-voc.2.0.cfg", base_dir + "backup/yolo-voc_26000.weights", 0)
192 | self.meta = darknet.load_meta(base_dir + "PLANE/voc.data")
193 |
194 | # if gpu_mode:
195 | # caffe.set_mode_gpu()
196 | # else:
197 | # caffe.set_mode_cpu()
198 | # self.net = caffe.Classifier(
199 | # model_def_file, pretrained_model_file,
200 | # image_dims=(image_dim, image_dim), raw_scale=raw_scale,
201 | # mean=np.load(mean_file).mean(1).mean(1), channel_swap=(2, 1, 0)
202 | # )
203 | # with open(class_labels_file) as f:
204 | # labels_df = pd.DataFrame([
205 | # {
206 | # 'synset_id': l.strip().split(' ')[0],
207 | # 'name': ' '.join(l.strip().split(' ')[1:]).split(',')[0]
208 | # }
209 | # for l in f.readlines()
210 | # ])
211 | # self.labels = labels_df.sort('synset_id')['name'].values
212 | # self.bet = cPickle.load(open(bet_file))
213 | # A bias to prefer children nodes in single-chain paths
214 | # I am setting the value to 0.1 as a quick, simple model.
215 | # We could use better psychological models here...
216 | # self.bet['infogain'] -= np.array(self.bet['preferences']) * 0.1
217 |
218 | def classify_image(self, image_filename):
219 | try:
220 | starttime = time.time()
221 | # scores = self.net.predict([image], oversample=True).flatten()
222 | results = darknet.detect(self.net, self.meta, image_filename)
223 | # [('F22', 0.9006772637367249, (338.6946105957031, 431.28515625, 608.9721069335938, 220.40663146972656)),
224 | # ('F22', 0.890718400478363, (545.9476318359375, 294.4508361816406, 509.1690979003906, 177.72409057617188)),
225 | # ('F22', 0.8847938179969788, (642.2884521484375, 193.6743927001953, 401.5226745605469, 135.20948791503906))]
226 | endtime = time.time()
227 | bet_result = [(str(idx+1)+' : '+v[0], '%.5f' % v[1])
228 | for idx, v in enumerate(results)]
229 | # logging.info('bet result: %s', str(bet_result))
230 | rtn = (True, results, bet_result, '%.3f' % (endtime - starttime))
231 | return rtn
232 |
233 | # indices = (-scores).argsort()[:5]
234 | # predictions = self.labels[indices]
235 |
236 | # In addition to the prediction text, we will also produce
237 | # the length for the progress bar visualization.
238 | # meta = [
239 | # (p, '%.5f' % scores[i])
240 | # for i, p in zip(indices, predictions)
241 | # ]
242 | # logging.info('result: %s', str(meta))
243 |
244 | # Compute expected information gain
245 | # expected_infogain = np.dot(
246 | # self.bet['probmat'], scores[self.bet['idmapping']])
247 | # expected_infogain *= self.bet['infogain']
248 |
249 | # sort the scores
250 | # infogain_sort = expected_infogain.argsort()[::-1]
251 | # bet_result = [(self.bet['words'][v], '%.5f' % expected_infogain[v])
252 | # for v in infogain_sort[:5]]
253 | # logging.info('bet result: %s', str(bet_result))
254 |
255 | # return (True, meta, bet_result, '%.3f' % (endtime - starttime))
256 |
257 | except Exception as err:
258 | logging.info('Classification error: %s', err)
259 | return (False, 'Something went wrong when classifying the '
260 | 'image. Maybe try another one?')
261 |
262 |
263 | def start_tornado(app, port=5000):
264 | http_server = tornado.httpserver.HTTPServer(
265 | tornado.wsgi.WSGIContainer(app))
266 | http_server.listen(port)
267 | print("Tornado server starting on port {}".format(port))
268 | tornado.ioloop.IOLoop.instance().start()
269 |
270 |
271 | def start_from_terminal(app):
272 | """
273 | Parse command line options and start the server.
274 | """
275 | parser = optparse.OptionParser()
276 | parser.add_option(
277 | '-d', '--debug',
278 | help="enable debug mode",
279 | action="store_true", default=False)
280 | parser.add_option(
281 | '-p', '--port',
282 | help="which port to serve content on",
283 | type='int', default=5000)
284 | parser.add_option(
285 | '-g', '--gpu',
286 | help="use gpu mode",
287 | action='store_true', default=True)
288 |
289 | opts, args = parser.parse_args()
290 | ImagenetClassifier.default_args.update({'gpu_mode': opts.gpu})
291 |
292 | # Initialize classifier + warm start by forward for allocation
293 | app.clf = ImagenetClassifier(**ImagenetClassifier.default_args)
294 | #app.clf.net.forward()
295 |
296 | if opts.debug:
297 | app.run(debug=True, host='0.0.0.0', port=opts.port)
298 | else:
299 | start_tornado(app, opts.port)
300 |
301 |
302 | if __name__ == '__main__':
303 | logging.getLogger().setLevel(logging.INFO)
304 | if not os.path.exists(UPLOAD_FOLDER):
305 | os.makedirs(UPLOAD_FOLDER)
306 | start_from_terminal(app)
307 |
--------------------------------------------------------------------------------
/darknet.py:
--------------------------------------------------------------------------------
1 | from ctypes import *
2 | import math
3 | import random
4 |
5 |
6 | def sample(probs):
7 | s = sum(probs)
8 | probs = [a / s for a in probs]
9 | r = random.uniform(0, 1)
10 | for i in range(len(probs)):
11 | r = r - probs[i]
12 | if r <= 0:
13 | return i
14 | return len(probs) - 1
15 |
16 |
17 | def c_array(ctype, values):
18 | return (ctype * len(values))(*values)
19 |
20 |
21 | class BOX(Structure):
22 | _fields_ = [("x", c_float),
23 | ("y", c_float),
24 | ("w", c_float),
25 | ("h", c_float)]
26 |
27 |
28 | class IMAGE(Structure):
29 | _fields_ = [("w", c_int),
30 | ("h", c_int),
31 | ("c", c_int),
32 | ("data", POINTER(c_float))]
33 |
34 |
35 | class METADATA(Structure):
36 | _fields_ = [("classes", c_int),
37 | ("names", POINTER(c_char_p))]
38 |
39 |
40 | # lib = CDLL("/home/pjreddie/documents/darknet/libdarknet.so", RTLD_GLOBAL)
41 | lib = CDLL("/home/s05/fyk/darknet-modify/libdarknet-gpu.so", RTLD_GLOBAL)
42 | #lib = CDLL("/home/s05/fyk/darknet-modify/libdarknet-cpu.so", RTLD_GLOBAL)
43 | lib.network_width.argtypes = [c_void_p]
44 | lib.network_width.restype = c_int
45 | lib.network_height.argtypes = [c_void_p]
46 | lib.network_height.restype = c_int
47 |
48 | predict = lib.network_predict_p
49 | predict.argtypes = [c_void_p, POINTER(c_float)]
50 | predict.restype = POINTER(c_float)
51 |
52 | make_boxes = lib.make_boxes
53 | make_boxes.argtypes = [c_void_p]
54 | make_boxes.restype = POINTER(BOX)
55 |
56 | free_ptrs = lib.free_ptrs
57 | free_ptrs.argtypes = [POINTER(c_void_p), c_int]
58 |
59 | num_boxes = lib.num_boxes
60 | num_boxes.argtypes = [c_void_p]
61 | num_boxes.restype = c_int
62 |
63 | make_probs = lib.make_probs
64 | make_probs.argtypes = [c_void_p]
65 | make_probs.restype = POINTER(POINTER(c_float))
66 |
67 | detect = lib.network_predict_p
68 | detect.argtypes = [c_void_p, IMAGE, c_float, c_float, c_float, POINTER(BOX), POINTER(POINTER(c_float))]
69 |
70 | reset_rnn = lib.reset_rnn
71 | reset_rnn.argtypes = [c_void_p]
72 |
73 | load_net = lib.load_network_p
74 | load_net.argtypes = [c_char_p, c_char_p, c_int]
75 | load_net.restype = c_void_p
76 |
77 | free_image = lib.free_image
78 | free_image.argtypes = [IMAGE]
79 |
80 | letterbox_image = lib.letterbox_image
81 | letterbox_image.argtypes = [IMAGE, c_int, c_int]
82 | letterbox_image.restype = IMAGE
83 |
84 | load_meta = lib.get_metadata
85 | lib.get_metadata.argtypes = [c_char_p]
86 | lib.get_metadata.restype = METADATA
87 |
88 | load_image = lib.load_image_color
89 | load_image.argtypes = [c_char_p, c_int, c_int]
90 | load_image.restype = IMAGE
91 |
92 | predict_image = lib.network_predict_image
93 | predict_image.argtypes = [c_void_p, IMAGE]
94 | predict_image.restype = POINTER(c_float)
95 |
96 | network_detect = lib.network_detect
97 | network_detect.argtypes = [c_void_p, IMAGE, c_float, c_float, c_float, POINTER(BOX), POINTER(POINTER(c_float))]
98 |
99 |
100 | def classify(net, meta, im):
101 | out = predict_image(net, im)
102 | res = []
103 | for i in range(meta.classes):
104 | res.append((meta.names[i], out[i]))
105 | res = sorted(res, key=lambda x: -x[1])
106 | return res
107 |
108 |
109 | def detect(net, meta, image, thresh=.5, hier_thresh=.5, nms=.45):
110 | im = load_image(image, 0, 0)
111 | boxes = make_boxes(net)
112 | probs = make_probs(net)
113 | num = num_boxes(net)
114 | network_detect(net, im, thresh, hier_thresh, nms, boxes, probs)
115 | res = []
116 | for j in range(num):
117 | for i in range(meta.classes):
118 | if probs[j][i] > 0:
119 | res.append((meta.names[i], probs[j][i], (boxes[j].x, boxes[j].y, boxes[j].w, boxes[j].h)))
120 | res = sorted(res, key=lambda x: -x[1])
121 | free_image(im)
122 | free_ptrs(cast(probs, POINTER(c_void_p)), num)
123 | return res
124 |
125 |
126 | if __name__ == "__main__":
127 | # net = load_net("cfg/densenet201.cfg", "/home/pjreddie/trained/densenet201.weights", 0)
128 | # im = load_image("data/wolf.jpg", 0, 0)
129 | # meta = load_meta("cfg/imagenet1k.data")
130 | # r = classify(net, meta, im)
131 | # print r[:10]
132 | net = load_net("cfg/tiny-yolo.cfg", "tiny-yolo.weights", 0)
133 | meta = load_meta("cfg/coco.data")
134 | r = detect(net, meta, "data/dog.jpg")
135 | # print r
136 |
--------------------------------------------------------------------------------
/exifutil.py:
--------------------------------------------------------------------------------
1 | """
2 | This script handles the skimage exif problem.
3 | """
4 |
5 | from PIL import Image
6 | import numpy as np
7 |
8 | ORIENTATIONS = { # used in apply_orientation
9 | 2: (Image.FLIP_LEFT_RIGHT,),
10 | 3: (Image.ROTATE_180,),
11 | 4: (Image.FLIP_TOP_BOTTOM,),
12 | 5: (Image.FLIP_LEFT_RIGHT, Image.ROTATE_90),
13 | 6: (Image.ROTATE_270,),
14 | 7: (Image.FLIP_LEFT_RIGHT, Image.ROTATE_270),
15 | 8: (Image.ROTATE_90,)
16 | }
17 |
18 | def open_oriented_pil(im_path):
19 | im = Image.open(im_path)
20 | if hasattr(im, '_getexif'):
21 | exif = im._getexif()
22 | if exif is not None and 274 in exif:
23 | orientation = exif[274]
24 | im = apply_orientation(im, orientation)
25 |
26 | return im
27 |
28 | def open_oriented_im(im_path):
29 | im = open_oriented_pil(im_path)
30 | img = np.asarray(im)
31 | # img = np.asarray(im).astype(np.float32) / 255. # for caffe
32 | if img.ndim == 2:
33 | img = img[:, :, np.newaxis]
34 | img = np.tile(img, (1, 1, 3))
35 | elif img.shape[2] == 4:
36 | img = img[:, :, :3]
37 | return img
38 |
39 |
40 | def apply_orientation(im, orientation):
41 | if orientation in ORIENTATIONS:
42 | for method in ORIENTATIONS[orientation]:
43 | im = im.transpose(method)
44 | return im
45 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Object Detection Web Demo
3 |
4 |
5 |
6 |
7 |
8 | Image object detection demo(YOLO,SSD, Faster rcnn, etc.) running as a Flask web server.
9 |
10 | > ***Notice***
11 | This repo is not a turnkey project for object detection web system but an easy template for those who are not familiar with Web development just like me. You need to understand and modify the code little or much to use.
12 |
13 | ## Requirements
14 |
15 | The demo server requires Python with some dependencies.
16 | To make sure you have the dependencies, please run `pip install -r examples/web_demo/requirements.txt`.
17 |
18 | ## Run
19 |
20 | Running `python app.py` will bring up the demo server, accessible at `http://0.0.0.0:5000`.
21 | You can enable debug mode of the web server, or switch to a different port:
22 |
23 | % python app.py -h
24 | Usage: app.py [options]
25 |
26 | Options:
27 | -h, --help show this help message and exit
28 | -d, --debug enable debug mode
29 | -p PORT, --port=PORT which port to serve content on
30 |
31 | ## More
32 |
33 | The Javascript code `static/js/template.js` includes several part:
34 |
35 | - use of `Dropzone.js`(Drag and drop file plugin) to upload image and get result with AJAX.
36 | - parse JSON result and draw rectangles on image in browser canvas.
37 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | werkzeug
2 | flask
3 | tornado
4 | numpy
5 | pandas
6 | pillow
7 | pyyaml
8 |
--------------------------------------------------------------------------------
/ssd_detect.py:
--------------------------------------------------------------------------------
1 | #encoding=utf8
2 | '''
3 | Detection with SSD
4 | In this example, we will load a SSD model and use it to detect objects.
5 | '''
6 |
7 | import os
8 | import sys
9 | import argparse
10 | import numpy as np
11 | from PIL import Image, ImageDraw
12 | # Make sure that caffe is on the python path:
13 | caffe_root = '/home/s05/caffe_ssd/caffe/'
14 | os.chdir(caffe_root)
15 | sys.path.insert(0, os.path.join(caffe_root, 'python'))
16 | import caffe
17 |
18 | from google.protobuf import text_format
19 | from caffe.proto import caffe_pb2
20 |
21 |
22 | def get_labelname(labelmap, labels):
23 | num_labels = len(labelmap.item)
24 | labelnames = []
25 | if type(labels) is not list:
26 | labels = [labels]
27 | for label in labels:
28 | found = False
29 | for i in xrange(0, num_labels):
30 | if label == labelmap.item[i].label:
31 | found = True
32 | labelnames.append(labelmap.item[i].display_name)
33 | break
34 | assert found == True
35 | return labelnames
36 |
37 | class CaffeDetection:
38 | def __init__(self, gpu_id, model_def, model_weights, image_resize, labelmap_file):
39 | if gpu_id == -1:
40 | caffe.set_mode_cpu()
41 | else:
42 | caffe.set_device(gpu_id)
43 | caffe.set_mode_gpu()
44 |
45 | self.image_resize = image_resize
46 | # Load the net in the test phase for inference, and configure input preprocessing.
47 | self.net = caffe.Net(model_def, # defines the structure of the model
48 | model_weights, # contains the trained weights
49 | caffe.TEST) # use test mode (e.g., don't perform dropout)
50 | # input preprocessing: 'data' is the name of the input blob == net.inputs[0]
51 | self.transformer = caffe.io.Transformer({'data': self.net.blobs['data'].data.shape})
52 | self.transformer.set_transpose('data', (2, 0, 1))
53 | self.transformer.set_mean('data', np.array([104, 117, 123])) # mean pixel
54 | # the reference model operates on images in [0,255] range instead of [0,1]
55 | self.transformer.set_raw_scale('data', 255)
56 | # the reference model has channels in BGR order instead of RGB
57 | self.transformer.set_channel_swap('data', (2, 1, 0))
58 |
59 | # load PASCAL VOC labels
60 | file = open(labelmap_file, 'r')
61 | self.labelmap = caffe_pb2.LabelMap()
62 | text_format.Merge(str(file.read()), self.labelmap)
63 |
64 | def detect(self, image_file, conf_thresh=0.5, topn=5):
65 | '''
66 | SSD detection
67 | '''
68 | # set net to batch size of 1
69 | # image_resize = 300
70 | self.net.blobs['data'].reshape(1, 3, self.image_resize, self.image_resize)
71 | image = caffe.io.load_image(image_file)
72 |
73 | #Run the net and examine the top_k results
74 | transformed_image = self.transformer.preprocess('data', image)
75 | self.net.blobs['data'].data[...] = transformed_image
76 |
77 | # Forward pass.
78 | detections = self.net.forward()['detection_out']
79 |
80 | # Parse the outputs.
81 | det_label = detections[0,0,:,1]
82 | det_conf = detections[0,0,:,2]
83 | det_xmin = detections[0,0,:,3]
84 | det_ymin = detections[0,0,:,4]
85 | det_xmax = detections[0,0,:,5]
86 | det_ymax = detections[0,0,:,6]
87 | #conf_thresh=0.8
88 | # Get detections with confidence higher than 0.6.
89 | top_indices = [i for i, conf in enumerate(det_conf) if conf >= conf_thresh]
90 |
91 | top_conf = det_conf[top_indices]
92 | top_label_indices = det_label[top_indices].tolist()
93 | top_labels = get_labelname(self.labelmap, top_label_indices)
94 | top_xmin = det_xmin[top_indices]
95 | top_ymin = det_ymin[top_indices]
96 | top_xmax = det_xmax[top_indices]
97 | top_ymax = det_ymax[top_indices]
98 |
99 | result = []
100 | for i in xrange(min(topn, top_conf.shape[0])):
101 | xmin = top_xmin[i] # xmin = int(round(top_xmin[i] * image.shape[1]))
102 | ymin = top_ymin[i] # ymin = int(round(top_ymin[i] * image.shape[0]))
103 | xmax = top_xmax[i] # xmax = int(round(top_xmax[i] * image.shape[1]))
104 | ymax = top_ymax[i] # ymax = int(round(top_ymax[i] * image.shape[0]))
105 | score = top_conf[i]
106 | label = int(top_label_indices[i])
107 | label_name = top_labels[i]
108 | result.append([xmin, ymin, xmax, ymax, label, score, label_name])
109 | return result
110 |
111 | def main(args):
112 | '''main '''
113 | detection = CaffeDetection(args.gpu_id,
114 | args.model_def, args.model_weights,
115 | args.image_resize, args.labelmap_file)
116 | result = detection.detect(args.image_file)
117 | print result
118 |
119 | img = Image.open(args.image_file)
120 | draw = ImageDraw.Draw(img)
121 | width, height = img.size
122 | print width, height
123 | for item in result:
124 | xmin = int(round(item[0] * width))
125 | ymin = int(round(item[1] * height))
126 | xmax = int(round(item[2] * width))
127 | ymax = int(round(item[3] * height))
128 | draw.rectangle([xmin, ymin, xmax, ymax], outline=(255, 0, 0))
129 | draw.text([xmin, ymin], item[-1] + str(item[-2]), (0, 0, 255))
130 | print item
131 | print [xmin, ymin, xmax, ymax]
132 | print [xmin, ymin], item[-1]
133 | img.save('detect_result.jpg')
134 |
135 |
136 | def parse_args():
137 | '''parse args'''
138 | parser = argparse.ArgumentParser()
139 | parser.add_argument('--gpu_id', type=int, default=0, help='gpu id')
140 | parser.add_argument('--labelmap_file',
141 | default='/home/s05/caffe_ssd/caffe/data/VOC0712/labelmap_voc.prototxt')
142 | parser.add_argument('--model_def',
143 | default='/home/s05/caffe_ssd/caffe/models/VGGNet/VOC0712/SSD_300x300/deploy.prototxt')
144 | parser.add_argument('--image_resize', default=300, type=int)
145 | parser.add_argument('--model_weights',
146 | default='/home/s05/caffe_ssd/caffe/models/VGGNet/VOC0712/SSD_300x300/'
147 | 'VGG_VOC0712_SSD_300x300_iter_6000.caffemodel')
148 | # parser.add_argument('--image_file', default='/home/s05/caffe_ssd/caffe/examples/images/plane (25).jpg')
149 | return parser.parse_args()
150 | def load_net():
151 | args = parse_args()
152 | detection = CaffeDetection(args.gpu_id,
153 | args.model_def, args.model_weights,
154 | args.image_resize, args.labelmap_file)
155 | return detection
156 | def detect(net,image_file):
157 | result = net.detect(image_file)
158 | return result
159 | if __name__ == '__main__':
160 | main(parse_args())
--------------------------------------------------------------------------------
/static/js/template.js:
--------------------------------------------------------------------------------
1 | function require(script) {
2 | $.ajax({
3 | url: script,
4 | dataType: "script",
5 | async: false, // <-- This is the key, however this has been deprecated, for more solutions, see https://stackoverflow.com/questions/950087/how-do-i-include-a-javascript-file-in-another-javascript-file
6 | success: function () {
7 | // all good...
8 | },
9 | error: function () {
10 | throw new Error("Could not load script " + script);
11 | }
12 | });
13 | }
14 | // load the script of decompressing tiff image, for tiff/tif is not normal images supported by browser
15 | //require("/static/js/tiff.min.js");
16 | // load the script of dropzone(drag and drop file utils).
17 | //require("/static/js/dropzone.js");
18 |
19 | var currentFile = null;
20 | var resizeRatio = 1;
21 | var dx = 0;
22 | var dy = 0;
23 | Dropzone.autoDiscover = false;
24 | var myDropzone = new Dropzone("#dropz", { //url: "/upload-det-rsi"});
25 | url: "/upload-det-rsi",
26 | timeout: 600000, /*milliseconds, default is 30 sec*/
27 | maxFiles: 100,
28 | maxFilesize: 1024,
29 | // acceptedFiles: ".jpg,.jpeg,.doc,.docx,.ppt,.pptx,.txt,.avi,.pdf,.mp3,.zip",
30 | // autoProcessQueue: false,
31 | // paramName: "file",
32 | createImageThumbnails: false,//不显示缩略图
33 | previewsContainer: false,//不显示preview
34 | // dictDefaultMessage: "拖入需要上传的文件",
35 | init: function () {
36 | var myDropzone = this;//, submitButton = document.querySelector("#qr"),
37 | //cancelButton = document.querySelector("#cancel");
38 | var picshow = document.querySelector("#dropz");
39 | myDropzone.on('addedfile', function (file) {
40 | //添加上传文件的过程
41 | if (currentFile) {
42 | this.removeFile(currentFile);
43 | }
44 | currentFile = file;
45 | picshow.setAttribute('plus_sign', 'none');
46 | $(".dz-message").html(null);
47 | var subregions = document.getElementById('rightpic');
48 | $(subregions).empty();
49 | subregions.setAttribute("class", "rightpic_hide");
50 | var FR= new FileReader();
51 | // handle special type of images
52 | if (file.type != "image/tif" && file.type != "image/tiff") {
53 | FR.onload = function(e) {
54 | // console.log( e.target.result); //This is the base64 data of file(gif) dropped
55 | //if you want to display it somewhere in your previewTemplate
56 | // var temp = file.previewTemplate;
57 | // picshow.innerHTML = "
";
58 | // var imgTag = picshow.querySelector("img");
59 | // imgTag.setAttribute('src', e.target.result); //setting as src of some img tag with class 'my-preview'
60 |
61 | var img = new Image();
62 | img.onload = imageLoaded;
63 | img.src = e.target.result;
64 | };
65 | FR.readAsDataURL(file);
66 |
67 | } else {
68 | FR.onload = function (event) {
69 | var buffer = event.target.result;
70 | var tiff = new Tiff({ buffer: buffer });
71 | var tif_canvas = tiff.toCanvas();
72 | //var width = tiff.width();
73 | //var height = tiff.height();
74 | var dataURL = tif_canvas.toDataURL();
75 | var img = new Image();
76 | img.onload = imageLoaded;
77 | img.src = dataURL;
78 | };
79 | FR.onerror = function (event) {
80 | //console.error("File could not be read! Code " + event.target.error.code);
81 | $("#status").text("File could not be read! Code " + event.target.error.code);
82 | };
83 | FR.readAsArrayBuffer(file);
84 | }
85 | });
86 | myDropzone.on('sending', function (data, xhr, formData) {
87 | /*Called just before each file is sent*/
88 | xhr.ontimeout = (() => {
89 | /*Execute on case of timeout only*/
90 | $("#status").text('Server Timeout')
91 | });
92 | //向后台发送该文件前添加参数、表单
93 | var thresholdValue = $("#thresholdValue").text();
94 | formData.append('threshold', thresholdValue);
95 | // formData.append('watermark', jQuery('#info').val());
96 | });
97 | myDropzone.on("complete", function(file) {
98 | // console.log("结束");
99 | });
100 | myDropzone.on('success', function (files, response) {
101 | // 得到返回结果
102 | // console.log(response);
103 |
104 | showResult(response);
105 | });
106 | myDropzone.on('error', function (files, response) {
107 | //文件上传失败后的操作
108 | $("#status").text("上传失败");
109 | });
110 | myDropzone.on('totaluploadprogress', function (progress, byte, bytes) {
111 | //progress为进度百分比
112 | if (progress == 100){
113 | $("#status").text("上传成功,等待结果...");
114 | }else{
115 | $("#status").text("上传进度:" + parseInt(progress) + "%");
116 | }
117 | //计算上传速度和剩余时间
118 | /*var mm = 0;
119 | var byte = 0;
120 | var tt = setInterval(function () {
121 | mm++;
122 | var byte2 = bytes;
123 | var remain;
124 | var speed;
125 | var byteKb = byte/1024;
126 | var bytesKb = bytes/1024;
127 | var byteMb = byte/1024/1024;
128 | var bytesMb = bytes/1024/1024;
129 | if(byteKb <= 1024){
130 | speed = (parseFloat(byte2 - byte)/(1024)/mm).toFixed(2) + " KB/s";
131 | remain = (byteKb - bytesKb)/parseFloat(speed);
132 | }else{
133 | speed = (parseFloat(byte2 - byte)/(1024*1024)/mm).toFixed(2) + " MB/s";
134 | remain = (byteMb - bytesMb)/parseFloat(speed);
135 | }
136 | $("#dropz #speed").text("上传速率:" + speed);
137 | $("#dropz #time").text("剩余时间"+arrive_timer_format(parseInt(remain)));
138 | if(bytes >= byte){
139 | clearInterval(tt);
140 | if(byteKb <= 1024){
141 | $("#dropz #speed").text("上传速率:0 KB/s");
142 | }else{
143 | $("#dropz #speed").text("上传速率:0 MB/s");
144 | }
145 | $("#dropz #time").text("剩余时间:0:00:00");
146 | }
147 | },1000);*/
148 | });
149 | /*submitButton.addEventListener('click', function () {
150 | //点击上传文件
151 | myDropzone.processQueue();
152 | });
153 | cancelButton.addEventListener('click', function () {
154 | //取消上传
155 | myDropzone.removeAllFiles();
156 | });*/
157 | }
158 | });
159 | function imageLoaded() {
160 | var img = this;
161 | showImageOnCanvas(img);
162 | }
163 | function showImageOnCanvas(img) {
164 | var canvas = document.getElementById('canvas');
165 | canvas.style.display = 'block';
166 | canvas.width = 700;
167 | canvas.height = 700;
168 | var max_side = img.width > img.height?img.width:img.height;
169 | resizeRatio = 700.0 / max_side;
170 | var dispWidth = img.width * resizeRatio;
171 | var dispHeight = img.height * resizeRatio;
172 | dx = (canvas.width - dispWidth)/2;
173 | dy = (canvas.height - dispHeight)/2;
174 | var ctx = canvas.getContext("2d");
175 | ctx.drawImage(img,0,0,img.width,img.height,dx,dy,dispWidth,dispHeight);
176 | }
177 | // loaded from our server
178 | function urlImgLoaded() {
179 | var img = this;
180 | showImageOnCanvas(img);
181 | showResult(img.det_result);
182 | }
183 | function showResult(jsonObj) {
184 | var subregions = document.getElementById('rightpic');
185 | subregions.setAttribute("class", "rightpic");
186 | var html = "
54 | 55 | 56 | 康行天下 57 | 58 |
59 |耗时 {{ result[3] }} 秒.
126 |