Skip to content

Commit

Permalink
More descriptive errors
Browse files Browse the repository at this point in the history
  • Loading branch information
prouast committed Jun 13, 2024
1 parent db5840e commit da31294
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 11 deletions.
16 changes: 8 additions & 8 deletions examples/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,18 @@ def run(args=None):
video, _ = read_video_from_path(path=args.video_path, pix_fmt='rgb24')
print("Video shape: {}".format(video.shape))
# Estimate vitals and measure inference time
vl = VitalLens(
method=args.method,
api_key="YOUR_API_KEY")
vl = VitalLens(method=args.method, api_key=args.api_key)
start = timeit.default_timer()
result = vl(video=video, fps=fps)
stop = timeit.default_timer()
print("Inference time: {:.2f} ms".format((stop-start)*1000))
time_ms = (stop-start)*1000
print("Inference time: {:.2f} ms".format(time_ms))
# Plot the results
if 'resp' in result[0]:
fig, (ax1, ax2) = plt.subplots(2, sharex=True)
fig, (ax1, ax2) = plt.subplots(2, sharex=True, figsize=(12, 6))
else:
fig, ax1 = plt.subplots(1)
fig.suptitle('Vital signs estimated from {} using {}'.format(args.video_path, args.method.name))
fig, ax1 = plt.subplots(1, figsize=(12, 6))
fig.suptitle('Vital signs estimated from {} using {} in {:.2f} ms'.format(args.video_path, args.method.name, time_ms))
if "pulse" in result[0] and ppg_gt is not None:
hr_gt = estimate_freq(ppg_gt, f_s=fps, f_res=0.005, f_range=(40./60., 240./60.), method='periodogram') * 60.
ax1.plot(ppg_gt, color=COLOR_GT, label='Pulse Ground Truth -> HR: {:.1f} bpm'.format(hr_gt))
Expand All @@ -72,9 +71,10 @@ def method_type(name):

if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--api_key', type=str, default='', help='Your API key. Get one for free at https://www.rouast.com/api.')
parser.add_argument('--vitals_path', type=str, default='examples/sample_vitals_1.csv', help='Path to ground truth vitals')
parser.add_argument('--video_path', type=str, default='examples/sample_video_1.mp4', help='Path to video')
parser.add_argument('--method', type=method_type, default='POS', help='Choice of method', required=True)
parser.add_argument('--method', type=method_type, default='VITALLENS', help='Choice of method')
parser.add_argument('--input_str', type=str2bool, default=True, help='If true, pass filepath to VitalLens, otherwise read video into memory first')
args = parser.parse_args()
run(args)
5 changes: 3 additions & 2 deletions vitallens/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ def __init__(
elif self.config['model'] == 'pos':
self.rppg = POSRPPGMethod(self.config)
elif self.config['model'] == 'vitallens':
if self.api_key is None:
logging.warn("API key is required to use Method.VITALLENS. Get yours at www.rouast.com/api")
if self.api_key is None or self.api_key == '':
raise ValueError("An API key is required to use Method.VITALLENS, but was not provided. "
"Get one for free at https://www.rouast.com/api.")
self.rppg = VitalLensRPPGMethod(self.config, self.api_key)
else:
raise ValueError("Method {} not implemented!".format(self.config['model']))
Expand Down
37 changes: 37 additions & 0 deletions vitallens/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright (c) 2024 Rouast Labs
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

class VitalLensAPIKeyError(Exception):
"""Exception raised for errors related to the API key."""
def __init__(self, message="A valid API key is required to use Method.VITALLENS. Get one for free at https://www.rouast.com/api."):
self.message = message
super().__init__(self.message)

class VitalLensAPIQuotaExceededError(Exception):
"""Exception raised if quota exceeded."""
def __init__(self, message="The quota or rate limit associated with your API Key may have been exceeded. Check your account at https://www.rouast.com/api and consider changing to a different plan."):
self.message = message
super().__init__(self.message)

class VitalLensAPIError(Exception):
"""Exception raised for internal API errors."""
def __init__(self, message="Bad request or an error occured in the API."):
self.message = message
super().__init__(self.message)
10 changes: 9 additions & 1 deletion vitallens/methods/vitallens.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from typing import Union, Tuple

from vitallens.constants import API_MAX_FRAMES, API_URL, API_OVERLAP
from vitallens.errors import VitalLensAPIKeyError, VitalLensAPIQuotaExceededError, VitalLensAPIError
from vitallens.methods.rppg_method import RPPGMethod
from vitallens.signal import detrend_lambda_for_hr_response, detrend_lambda_for_rr_response
from vitallens.signal import moving_average_size_for_hr_response, moving_average_size_for_rr_response
Expand Down Expand Up @@ -133,7 +134,14 @@ def process_api(
# Check if call was successful
if response.status_code != 200:
logging.error("Error {}: {}".format(response.status_code, response_body['message']))
return [], [], []
if response.status_code == 403:
raise VitalLensAPIKeyError()
elif response.status_code == 429:
raise VitalLensAPIQuotaExceededError()
elif response.status_code == 400:
raise VitalLensAPIError("Error occurred in the API. Message: {}".format(response_body['message']))
else:
raise Exception("Error {}: {}".format(response.status_code, response_body['message']))
# Parse response
sig_ds = np.asarray(response_body["signal"])
conf_ds = np.asarray(response_body["conf"])
Expand Down

0 comments on commit da31294

Please sign in to comment.