I've followed this tutoria to create a custom object YoloV3 Keras model:
https://momoky.space/pythonlessons/YOLOv3-object-detection-tutorial/tree/master/YOLOv3-custom-training
Model works perfectly fine, my next goal is to create a Python Flask API witch is capable to process Image after upload it.
I've started modify the Code here for image detection
That's my added code:
#app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\\yolo\\temp.jpg', image)
image = 'c:\\yolo\\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
app.run(host="localhost", port=5000)
So my problem is that it works only on first iteration, when I upload a new image I receive following error:
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 929, in run
run_metadata_ptr)
File "C:\Users\xxx\Anaconda3\envs\yolo\lib\site-packages\tensorflow\python\client\session.py", line 1095, in _run
'Cannot interpret feed_dict key as Tensor: ' + e.args[0])
TypeError: Cannot interpret feed_dict key as Tensor: Tensor Tensor("Placeholder:0", shape=(3, 3, 3, 32), dtype=float32) is not an element of this graph.
This is the original static part of the code:
if __name__=="__main__":
yolo = YOLO()
image = 'test.png'
r_image, ObjectsList = yolo.detect_img(image)
print(ObjectsList)
#cv2.imshow(image, r_image)
cv2.imwrite('detect.png', r_image)
yolo.close_session()
Things that really confuse me is how to load the model when the application start, and execute detection every time a new image is posted.
Thank you
UPDATE
in the construtor part there's a referenced Keras backend session:
**def __init__(self, **kwargs):
self.__dict__.update(self._defaults) # set up default values
self.__dict__.update(kwargs) # and update with user overrides
self.class_names = self._get_class()
self.anchors = self._get_anchors()
self.sess = K.get_session()
self.boxes, self.scores, self.classes = self.generate()**
After addinga K.clear_session it works for multiple series request:
#app.route('/api/test', methods=['POST'])
def main():
img = request.files["image"].read()
img = Image.open(io.BytesIO(img))
npimg=np.array(img)
image=npimg.copy()
image=cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
#cv2.imshow("Image", image)
#cv2.waitKey()
cv2.imwrite('c:\\yolo\\temp.jpg', image)
image = 'c:\\yolo\\temp.jpg'
yolo = YOLO()
r_image, ObjectsList = yolo.detect_img(image)
#response = {ObjectsList}
response_pikled = jsonpickle.encode(ObjectsList)
#yolo.close_session()
K.clear_session()
return Response(response=response_pikled, status=200, mimetype="application/json")
Will be possible to avoid model, anchors and classes needs to be loaded at every computation avoiding this:
ogs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:58:49] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:08] "?[37mPOST /api/test HTTP/1.1?[0m" 200 -
logs/000/trained_weights_final.h5 model, anchors, and classes loaded.
127.0.0.1 - - [27/Dec/2019 22:59:33] "?[37mPOST /api/test HTTP/1.1?[0m" 200
-
I've managed to get this up and running as a prototype. I've uploaded a repo: vulcan25/image_processor which implements this all.
The first thing I investigated was the functionality of the method YOLO.detect_img in that code from the tutorial. This method takes a filename, which is immediately handled by cv2.imread in the original code: #L152-L153. The returned data from this is then processed internally by self.detect_image (note the difference in spelling) and the result displayed with cv2.show.
This behaviour isn't good for a webapp and I wanted to keep everything in memory, so figured the best way to change that functionality was to subclass YOLO and overide the detect_img method, making it behave differently. So in processor/my_yolo.py I do something like:
from image_detect import YOLO as stock_yolo
class custom_yolo(stock_yolo):
def detect_img(self, input_stream):
image = cv2.imdecode(numpy.fromstring(input_stream, numpy.uint8), 1)
original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
original_image_color = cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB)
r_image, ObjectsList = self.detect_image(original_image_color)
is_success, output_stream = cv2.imencode(".jpg", r_image)
return is_success, output_stream
Note: In a later decision, I pulled image_detect.py into my repo to append K.clear_session(). It would have been possible to put the above mod in that file also, but I've stuck with subclassing for that part.
This accepts a stream, then uses cv2.imencode (source) and cv2.imdecode (source) in place of imshow and imread respectively.
We can now define a single a single function which will in turn run all the image processing stuff. This separates that part of the code (and dependencies) from your flask app which is good:
yolo = custom_yolo() # create an object from the custom class defined above.
def process(intput_stream):
start_time = time.time()
is_success, output_stream = yolo.detect_img(input_stream)
io_buf = io.BytesIO(output_stream)
print("--- %s seconds ---" % (time.time() - start_time))
return is_success, io_buf.read()
From Flask we can call this in the same way, where we already have the stream of the uploaded file available to us as: request.files['file'].read() (which is actually a method of the werkzeug.datastructures.FileStorage object, as I've documented elsewhere).
As a side-note, this function could be run from the terminal. If you're launching my repo you'd need to do this in the processor container (see the docker exec syntax at the end of my README)...
from my_yolo import process
with f.open('image.jpg', 'rb') as f:
is_sucess, processed_data = process(f.read())
Then the result written to a file:
with f.open('processed_image.jpg', 'wb' as f):
f.write(processed_data)
Note that my repo actually has two separate flask apps (based on another upload script I put together which implements dropzone.js on the frontend).
I can run in two modes:
processor/src/app.py: Accessible on port 5001, this runs process directly (incoming requests block until the processed data is returned).
flask/src/app.py: Accessible on port 5000, this creates an rq job for each incoming request, the processor container then runs as a worker to process these requests from the queue.
Each app has its own index.html which does its own unique implementation on the frontend. Mode (1) writes images straight to the page, mode (2) adds a link to the page, which when clicked leads to a separate endpoint that serves the image (when processed).
The major difference is how process is invoked. With mode (1) processor/src/app.py:
from my_yolo import process
if file and allowed_file(file.filename):
# process the upload immediately
input_data = file.read()
complete, data = process(input_data)
As mentioned in a comment, I was seeing pretty fast conversions with this mode: ~1.6s per image on CPU. This script also uses a redis set to maintain a list of uploaded files, which can be used for validation on the view endpoint further down.
In mode (2) flask/src/app.py:
from qu import image_enqueue
if file and allowed_file(file.filename):
input_data = file.read()
job = img_enqueue(input_data)
return jsonify({'url': url_for('view', job_id=job.id)})
I've implemented a separate file flask/src/qu.py which implements this img_enqueue function, which ultimately loads the process function from flask/src/my_yolo.py where it is defined as:
def process(data): pass
This is an important destinction. Normally with rq the contents of this function would be defined in the same codebase as the flask service. In fact, I've actually put the business logic in processor/src/my_yolo.py which allows us to detach the container with the image processing dependencies, and ultiately host this somewhere else, as long is it shares a redis connection with the flask service.
Please have a look at the code in the repo for further info, and feel free to log an issue against that repo with any further queries (or if you get stuck). Be aware I may introduce breaking changes, so you may wish to fork.
I've tried to keep this pretty simple. In theory this could be edited slightly to support a different processor/Dockerfile which handles any processing workload, but the same frontend allowing you to submit any type of data from a stream: images, CSV, other text, etc.
Things that really confuse me is how to load the model when the application start, and execute detection every time a new image is posted. Thank you
You'll notice when you run this mode (1) it is perfect, in that the dependencies load when the flask server boots (~17.s) and individual image processing takes ~1s. This is ideal, although probably leads to higher overall memory usage on the server, as each WSGI worker requires all the dependencies loaded.
When run in mode (2) - where processing is passed to rq workers, the libraries are loaded each time an image is processed, so it's much slower. I will try to fix that, I just need to investigate how to pre-load the libraries in the rq deployment; I was close to this before but that was around the time I stumbled with the K.clear_session() problem, so haven't had time to retest a fix for this (yet).
Inside YOLO constructor try adding this:
from keras import backend as K
K.clear_session()
Related
I'm using boto3 to connect to s3, download objects and do some processing. I'm using a multiprocessing pool to do the above.
Following is a synopsis of the code I'm using:
session = None
def set_global_session():
global session
if not session:
session = boto3.Session(region_name='us-east-1')
def function_to_be_sent_to_mp_pool():
s3 = session.client('s3', region_name='us-east-1')
list_of_b_n_o = list_of_buckets_and_objects
for bucket, object in list_of_b_n_o:
content = s3.get_object(Bucket=bucket, Key=key)
data = json.loads(content['Body'].read().decode('utf-8'))
write_processed_data_to_a_location()
def main():
pool = mp.Pool(initializer=set_global_session, processes=40)
pool.starmap(function_to_be_sent_to_mp_pool, list_of_b_n_o_i)
Now, when processes=40, everything works good. When processes = 64, still good.
However, when I increases to processes=128, I get the following error:
botocore.exceptions.NoCredentialsError: Unable to locate credentials
Our machine has the required IAM roles for accessing S3. Moreover, the weird thing that happens is that for some processes, it works fine, whereas for some others, it throws the credentials error. Why is this happening, and how to resolve this?
Another weird thing that happens is that I'm able to trigger two jobs in 2 separate terminal tabs (each of which has a separate ssh login shell to the machine). Each job spawns 64 processes, and that works fine as well, which means there are 128 processes running simultaneously. But 80 processes in one login shell fails.
Follow up:
I tried creating separate sessions for separate processes in one approach. In the other, I directly created s3-client using boto3.client. However, both of them throw the same error with 80 processes.
I also created separate clients with the following extra config:
Config(retries=dict(max_attempts=40), max_pool_connections=800)
This allowed me to use 80 processes at once, but anything > 80 fails with the same error.
Post follow up:
Can someone confirm if they've been able to use boto3 in multiprocessing with 128 processes?
This is actually a race condition on fetching the credentials. I'm not sure how fetching credentials under the hood works, but the I saw this question in Stack Overflow and this ticket in github.
I was able to resolve this by keeping a random wait time for each of the processes. The following is the updated code which works for me:
client_config = Config(retries=dict(max_attempts=400), max_pool_connections=800)
time.sleep(random.randint(0, num_processes*10)/1000) # random sleep time in milliseconds
s3 = boto3.client('s3', region_name='us-east-1', config=client_config)
I tried keeping the range for sleep time lesser than num_processes*10, but that failed again with the same issue.
#DenisDmitriev, since you are getting the credentials and storing them explicitly, I think that resolves the race condition and hence the issue is resolved.
PS: values for max_attempts and max_pool_connections don't have a logic. I was plugging several values until the race condition was figured out.
I suspect that AWS recently reduced throttling limits for metadata requests because I suddenly started running into the same issue. The solution that appears to work is to query credentials once before creating the pool and have the processes in the pool use them explicitly instead of making them query credentials again.
I am using fsspec with s3fs, and here's what my code for this looks like:
def get_aws_credentials():
'''
Retrieve current AWS credentials.
'''
import asyncio, s3fs
fs = s3fs.S3FileSystem()
# Try getting credentials
num_attempts = 5
for attempt in range(num_attempts):
credentials = asyncio.run(fs.session.get_credentials())
if credentials is not None:
if attempt > 0:
log.info('received credentials on attempt %s', 1 + attempt)
return asyncio.run(credentials.get_frozen_credentials())
time.sleep(15 * (random.random() + 0.5))
raise RuntimeError('failed to request AWS credentials '
'after %d attempts' % num_attempts)
def process_parallel(fn_d, max_processes):
# [...]
c = get_aws_credentials()
# Cache credentials
import fsspec.config
prev_s3_cfg = fsspec.config.conf.get('s3', {})
try:
fsspec.config.conf['s3'] = dict(prev_s3_cfg,
key=c.access_key,
secret=c.secret_key)
num_processes = min(len(fn_d), max_processes)
from concurrent.futures import ProcessPoolExecutor
with ProcessPoolExecutor(max_workers=num_processes) as pool:
for data in pool.map(process_file, fn_d, chunksize=10):
yield data
finally:
fsspec.config.conf['s3'] = prev_s3_cfg
Raw boto3 code will look essentially the same, except instead of the whole fs.session and asyncio.run() song and dance, you'll work with boto3.Session itself and call its get_credentials() and get_frozen_credentials() methods directly.
I get the same problem with multi process situation. I guess there is a client init problem when you use multi process. So I suggest that you can use get function to get s3 client. It works for me.
g_s3_cli = None
def get_s3_client(refresh=False):
global g_s3_cli
if not g_s3_cli or refresh:
g_s3_cli = boto3.client('s3')
return g_s3_cli
I have Angular 8 web app. It needs to send some data for analysis to python flask App. This flask App will send it to 3rd party service and get response through webhook.
My need is to provide a clean interface to the client so that there is no need to provide webhook from client.
Hence I am trying to initiate a request from Flask app, wait until I get data from webhook and then return.
Here is the code.
#In autoscaled micro-service this will not work. Right now, the scaling is manual and set to 1 instance
#Hence keeping this sliding window list in RAM is okay.
reqSlidingWindow =[]
#app.route('/handlerequest',methods = ['POST'])
def Process_client_request_and_respond(param1,param2,param3):
#payload code removed
#CORS header part removed
querystring={APIKey, 'https://mysvc.mycloud.com/postthirdpartyresponsehook'}
response = requests.post(thirdpartyurl, json=payload, headers=headers, params=querystring)
if(response.status_code == SUCCESS):
respdata = response.json()
requestId = respdata["request_id"]
requestobject = {}
requestobject['requestId'] = requestId
reqSlidingWindow.append(requestobject)
#Now wait for the response to arrive through webhook
#Keep checking the list if reqRecord["AllAnalysisDoneFlag"] is set to True.
#If set, read reqRecord["AnalysisResult"] = jsondata
jsondata = None
while jsondata is None:
time.sleep(2)
for reqRecord in reqSlidingWindow:
if(reqRecord["requestId"] == da_analysis_req_Id ):
print("Found matching req id in req list. Checking if analysis is done.")
print(reqRecord)
if(reqRecord["AllAnalysisDoneFlag"] == True):
jsondata = reqRecord["AnalysisResult"]
return jsonify({"AnalysisResult": "Success", "AnalysisData":jsondata})
#app.route('/webhookforresponse',methods = ['POST'])
def process_thirdparty_svc_response(reqIdinResp):
print(request.data)
print("response receieved at")
data = request.data.decode('utf-8')
jsondata = json.loads(data)
#
for reqRecord in reqSlidingWindow:
#print("In loop. current analysis type" + reqRecord["AnalysisType"] )
if(reqRecord["requestId"] == reqIdinResp ):
reqRecord["AllAnalysisDoneFlag"] = True
reqRecord["AnalysisResult"] = jsondata
return
I'm trying to maintain sliding window of requests in list. Upon the
Observations so far:
First, this does not work. The function of 'webhookforresponse' does not seem to run until my request function comes out. i.e. my while() loop appears to block everything though I have a time.sleep(). My assumption is that flask framework would ensure that callback is called since sleep in another 'route' gives it adequate time and internally flask would be multithreaded?
I tried running python threads for the sliding window data structure and also used RLocks. This does not alter behavior. I have not tried flask specific threading.
Questions:
What is the right architecture of the above need with flask? I need clean REST interface without callback for angular client. Everything else can change.
If the above code to be used, what changes should I make? Is threading required at all?
Though I can use database and then pick from there, it still requires polling the sliding window.
Should I use multithreading specific to flask? Is there any specific example with skeletal design, threading library choices?
Please suggest the right way to proceed to achieve abstract REST API for angular client which hides the back end callbacks and other complexities.
I have a flask app that returns a JSON response. However, I want it to call that function every 30 seconds without clicking the refresh button on the browser. Here is what I did
Using apscheduler
. This code in application.py
from apscheduler.schedulers.background import BachgroundScheduler
def create_app(config_filname):
con = redis.StrictRedis(host= "localhost", port=6379, charset ="utf-8", decode_responses=True, db=0)
application = Flask(__name__)
CORS(application)
sched = BackgroundScheduler()
#application.route('/users')
#cross_origin()
#sched.scheduled_job('interval', seconds = 20)
def get_users():
//Some code...
return jsonify(users)
sched.start()
return application
Then in my wsgi.py
from application import create_app
application = create_app('application.cfg')
with application.app_context():
if __name__ == "__main__":
application.run()
When I run this appliaction, I get the json output but it does not refresh instead after 20 seconds it throws
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
What am I doing wrong? I would appreciate any advise.
Apologies if this in a way subverting the question, but if you want the users to be sent every 30 seconds, this probably shouldn't be done in the backend. The backend should only ever send out data when a request is made. In order for the data to be sent at regular intervals the frontend needs to be configured to make requests at regular intervals
Personally I'd recommend doing this with a combination of i-frames and javascript, as described in this stack overflow question:
Auto Refresh IFrame HTML
Lastly, when it comes to your actual code, it seems like there is an error here:
if __name__ == "__main__":
application.run()
The "application.run()" line should be indented as it is inside the if statement
Background: Apache server using mod_wsgi to serve a Flask app using Flask_Sqlalchemy connecting to MySQL. This is a full stack application so it is nearly impossible to create a minimal example but I have tried.
My problem is that when I make some change that should modify the database subsequent requests don't always seem to reflect that change. For example if I create an object, then try to edit that same object, the edit will sometimes fail.
Most of the time if I create an object then go to the page listing all the objects, it will not show up on the list. Sometimes it will show up until I refresh, when it will disappear, and with another refresh it shows up again.
The same happens with edits. Example code:
bp = Blueprint('api_region', __name__, url_prefix='/app/region')
#bp.route('/rename/<int:region_id>/<string:name>', methods=['POST'])
def change_name(region_id, name):
region = Region.query.get(region_id)
try:
region.name = name
except AttributeError:
abort(404)
db.session.add(region)
db.session.commit()
return "Success"
#bp.route('/name/<int:region_id>/', methods=['GET'])
def get_name(region_id):
region = Region.query.get(region_id)
try:
name = region.name
except AttributeError:
abort(404)
return name
After object is created send a POST
curl -X POST https://example.com/app/region/rename/5/Europe
Then several GETs
curl -X GET https://example.com/app/region/name/5/
Sometimes, the GET will return the correct info, but every now and then it will return whatever it was before. Further example output https://pastebin.com/s8mqRHSR it happens at varying frequency but about one in 25 will fail, and it isn't always the "last" value either, when testing it seems to get 'stuck' at a certain value no matter how many times I change it up.
I am using the "dynamically bound" example of Flask_Sqlalchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
db.init_app(app)
... snip ...
return app
Which creates a scoped_session accessible in db.session.
Apache config is long and complicated but includes the line
WSGIDaemonProcess pixel processes=5 threads=5 display-name='%{GROUP}'
I can post more information if required.
For reference if anyone finds this thread with the same issue, I fixed my problem.
My Flask App factory function had the line app.app_context().push() leftover from the early days when it was based off a Flask tutorial. Unfortunately snipped out of the example code otherwise it might have been spotted by someone. During a restructuring of the project this line was left out and the problem fixed itself. Not sure why or how this line would cause this issue, and only for some but not all requests.
I have a python program which is doing millions of comparisons across records. Occasionally a comparison fails and I need to have a user (me) step and update a record. Today I do this by calling a function which:
creates a flask 'app'
creates and populates a wtform form to collect the necessary information
instantiates the flask app (e.g. app.run() and webbrowser.open() call to pull up the form)
I update the data in the form, when the form is submitted, the handler puts the updated data into a variable and then shuts down the flask app returning the
data to the caller
This seems kludgy. Is there a cleaner way of doing this recognizing that this is not a typical client-driven web application?
The minimal problem is how best to programmatically launch an 'as needed' web-based UI which presents a form, and then return back the submitted data to the caller.
My method works, but seems a poor design to meet the goal.
As I have not yet found a better way - in case someone else has a similar need, here is how I'm meeting the goal:
...
if somedata_needs_review:
review_status = user_review(somedata)
update_data(review_status)
...
def user_review(data_to_review):
""" Present web form to user and receive their input """
returnstruct = {}
app = Flask(__name__)
#app.route('/', methods=['GET'])
def show_review_form():
form = create_review_form(data_to_review)
return render_template('reviewtemplate.tpl', form=form)
# TODO - I currently split the handling into a different route because
# when using the same route previously Safari would warn of resubmitting data.
# This has the slightly unfortunate effect of creating multiple tabs.
#app.route('/process_compare', methods=['POST'])
def process_review_form():
# this form object will be populated with the submitted information
form = create_review_form(request.form, matchrecord=matchrecord)
# handle submitted updates however necessary
returnstruct['matched'] = form.process_changes.data
shutdown_flask_server()
return "Changes submitted, you can close this tab"
webbrowser.open('http://localhost:5000/', autoraise=True)
app.run(debug=True, use_reloader=False)
# The following will execute after the app is shutdown.
print('Finished manual review, returning {}'.format(returnstruct))
return(returnstruct)
def shutdown_flask_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the Werkzeug Server')
func()