-
-
Notifications
You must be signed in to change notification settings - Fork 17
Open
Description
Hello,
I hope you are doing well.
As part of our analysis, we detected several ML service misuses in your repository. The refactored code snippets provided below were generated with the assistance of an LLM after we identified the misuse and manually validated the refactoring to ensure correctness.
For further details on each misuse definition, please consult our ML Service Misuse Catalog: here
📝 Notes
We did not submit a Pull Request, as we did not want to risk breaking or affecting your project’s workflow or CI/CD pipeline. Instead, we are providing the information below so you can review and integrate the changes at your convenience.
🔍 Detected ML Service Misuses
Below is the list of misuses detected, each with:
- A short explanation
- Refactored code examples
1️⃣ Ignoring Monitoring Data Drift
- Description: No mechanism to detect input/output drift across time.
- Refactored version:
# ===== File: ImageToText/img2txt.py =====
import os, io
import argparse
from collections import deque
from google.cloud import vision
from google.oauth2 import service_account
def set_args():
global args
parser = argparse.ArgumentParser()
parser.add_argument("path", help="path to image")
parser.add_argument("--url", action="store_true", help="specify the path for an external image located on the Web (http:// or https://) or in Google Cloud Storage (gs://)")
parser.add_argument("--document", action="store_true", help="optimized for dense images")
parser.add_argument("--languages", "--language", help="specify language hints from https://cloud.google.com/vision/docs/languages (comma separated)", default='')
parser.add_argument("--full", "--verbose", action="store_true", help="show full description (paragraphs, per-word confidence, boundaries...)")
parser.add_argument("--confidence", type=float, default=0.6, help="display possible mistakes for symbols with low confidence. Default: 0.6")
parser.add_argument("--key", help="explicitly define the path to your service account JSON credentials")
args = parser.parse_args()
def f(n, decimals=3):
return "{:.{}f}".format(n, decimals)
def set_credentials():
global credentials
credentials_file = args.key or os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
if not credentials_file:
script_dir = os.path.dirname(os.path.realpath(__file__))
service_account_json = os.path.join(script_dir, '..', 'service_account.json')
if os.path.isfile(service_account_json):
credentials_file = service_account_json
if credentials_file:
credentials = service_account.Credentials.from_service_account_file(credentials_file)
else:
raise Exception("Missing service_account.json, GOOGLE_APPLICATION_CREDENTIALS or --key\nhttps://github.com/Carleslc/ImageToText
def detect_text(vision_image, language_hints=[], full=False):
client = vision.ImageAnnotatorClient(credentials=credentials)
image_context = vision.ImageContext(language_hints=language_hints)
response = client.text_detection(image=vision_image, image_context=image_context)
texts = response.text_annotations
if texts:
print(f"Language: {texts[0].locale}")
if full:
for text in texts:
print('\n' + text.description)
vertices = ([f'({vertex.x},{vertex.y})' for vertex in text.bounding_poly.vertices])
boundaries = ','.join(vertices)
print(f'bounds: {boundaries}')
elif texts:
print()
print(texts[0].description.strip())
def detect_document_text(vision_image, language_hints=[], full=False):
client = vision.ImageAnnotatorClient(credentials=credentials)
image_context = vision.ImageContext(language_hints=language_hints)
response = client.document_text_detection(image=vision_image, image_context=image_context)
text = response.text_annotations[0]
print(f"Language: {text.locale}\n")
print(text.description.strip())
if full:
paragraphs, lines = extract_paragraphs(response.full_text_annotation)
print('\nSINGLE LINE\n')
print(' '.join(map(str.strip, lines)))
print('\nPARAGRAPHS\n\n--')
print('\n\n'.join(paragraphs) + '\n--')
for page in response.full_text_annotation.pages:
has_mistakes = False
for block in page.blocks:
if full:
print(f'\nBlock confidence: {f(block.confidence)}')
for paragraph in block.paragraphs:
if full:
print('\n' + paragraphs.popleft())
print(f'\nParagraph confidence: {f(paragraph.confidence)}\n')
for word in paragraph.words:
word_text = ''.join([symbol.text for symbol in word.symbols])
if full:
print(f'({f(word.confidence)}) {word_text}')
for symbol in word.symbols:
if symbol.confidence < args.confidence and not has_mistakes:
print(f"Possible mistake: symbol '{symbol.text}' in word '{word_text}' (confidence: {f(symbol.confidence)})")
has_mistakes = True
def extract_paragraphs(full_text_annotation):
breaks = vision.TextAnnotation.DetectedBreak.BreakType
paragraphs = []
lines = []
for page in full_text_annotation.pages:
for block in page.blocks:
for paragraph in block.paragraphs:
para = ""
line = ""
for word in paragraph.words:
for symbol in word.symbols:
line += symbol.text
if symbol.property.detected_break.type == breaks.SPACE:
line += ' '
if symbol.property.detected_break.type == breaks.EOL_SURE_SPACE:
line += ' '
lines.append(line)
para += line
line = ''
if symbol.property.detected_break.type == breaks.LINE_BREAK:
lines.append(line)
para += line
line = ''
paragraphs.append(para)
return deque(paragraphs), lines
def get_image_file(path):
with io.open(path, 'rb') as image_file:
content = image_file.read()
return vision.Image(content=content)
def get_image_uri(uri):
image = vision.Image()
image.source.image_uri = uri
return image
def main():
set_args()
convert = detect_document_text if args.document else detect_text
get_image = get_image_uri if args.url else get_image_file
language_hints = args.languages.split(',')
set_credentials()
convert(get_image(args.path), language_hints, args.full)
if __name__ == "__main__":
main()
# ===== File: setup.py =====
from setuptools import setup, find_packages
with open('README.md', 'r') as readme_f:
readme = readme_f.read()
setup(
name='ImageToText',
version='1.1',
author='Carlos Lázaro Costa',
author_email='lazaro.costa.carles@gmail.com',
url='https://github.com/Carleslc/ImageToText',
description="OCR with Google's AI technology (Cloud Vision API)",
long_description=readme,
long_description_content_type='text/markdown',
python_requires='>=3.6',
packages=find_packages(),
install_requires=['google-cloud-vision'],
entry_points={
'console_scripts': [
'img2txt = ImageToText.img2txt:main',
]
},
)2️⃣ Improper Handling of ML API Limits
- Description: API rate limits or quota usage are not checked or handled.
- Refactored version:
import os, io
import argparse
from collections import deque
from google.cloud import vision
from google.oauth2 import service_account
def set_args():
global args
parser = argparse.ArgumentParser()
parser.add_argument("path", help="path to image")
parser.add_argument("--url", action="store_true", help="specify the path for an external image located on the Web (http:// or https://) or in Google Cloud Storage (gs://)")
parser.add_argument("--document", action="store_true", help="optimized for dense images")
parser.add_argument("--languages", "--language", help="specify language hints from https://cloud.google.com/vision/docs/languages (comma separated)", default='')
parser.add_argument("--full", "--verbose", action="store_true", help="show full description (paragraphs, per-word confidence, boundaries...)")
parser.add_argument("--confidence", type=float, default=0.6, help="display possible mistakes for symbols with low confidence. Default: 0.6")
parser.add_argument("--key", help="explicitly define the path to your service account JSON credentials")
args = parser.parse_args()
def f(n, decimals=3):
return "{:.{}f}".format(n, decimals)
def set_credentials():
global credentials
credentials_file = args.key or os.environ.get('GOOGLE_APPLICATION_CREDENTIALS')
if not credentials_file:
script_dir = os.path.dirname(os.path.realpath(__file__))
service_account_json = os.path.join(script_dir, '..', 'service_account.json')
if os.path.isfile(service_account_json):
credentials_file = service_account_json
if credentials_file:
credentials = service_account.Credentials.from_service_account_file(credentials_file)
else:
raise Exception("Missing service_account.json, GOOGLE_APPLICATION_CREDENTIALS or --key\nhttps://github.com/Carleslc/ImageToText
def detect_text(vision_image, language_hints=[], full=False):
client = vision.ImageAnnotatorClient(credentials=credentials)
image_context = vision.ImageContext(language_hints=language_hints)
response = client.text_detection(image=vision_image, image_context=image_context, max_results_per_request=1000) # Add max_results_per_request parameter to control the number of API requests
texts = response.text_annotations
if texts:
print(f"Language: {texts[0].locale}")
if full:
for text in texts:
print('\n' + text.description)
vertices = ([f'({vertex.x},{vertex.y})' for vertex in text.bounding_poly.vertices])
boundaries = ','.join(vertices)
print(f'bounds: {boundaries}')
elif texts:
print()
print(texts[0].description.strip())
def detect_document_text(vision_image, language_hints=[], full=False):
client = vision.ImageAnnotatorClient(credentials=credentials)
image_context = vision.ImageContext(language_hints=language_hints)
response = client.document_text_detection(image=vision_image, image_context=image_context, max_results_per_request=1000) # Add max_results_per_request parameter to control the number of API requests
text = response.text_annotations[0]
print(f"Language: {text.locale}\n")
print(text.description.strip())
if full:
paragraphs, lines = extract_paragraphs(response.full_text_annotation)
print('\nSINGLE LINE\n')
print(' '.join(map(str.strip, lines)))
print('\nPARAGRAPHS\n\n--')
print('\n\n'.join(paragraphs) + '\n--')
for page in response.full_text_annotation.pages:
has_mistakes = False
for block in page.blocks:
if full:
print(f'\nBlock confidence: {f(block.confidence)}')
for paragraph in block.paragraphs:
if full:
print('\n' + paragraphs.popleft())
print(f'\nParagraph confidence: {f(paragraph.confidence)}\n')
for word in paragraph.words:
word_text = ''.join([symbol.text for symbol in word.symbols])
if full:
print(f'({f(word.confidence)}) {word_text}')
for symbol in word.symbols:
if symbol.confidence < args.confidence:
if not has_mistakes:
has_mistakes = True
if not full:
print()
print(f"Possible mistake: symbol '{symbol.text}' in word '{word_text}' (confidence: {f(symbol.confidence)})")
def extract_paragraphs(full_text_annotation):
breaks = vision.TextAnnotation.DetectedBreak.BreakType
paragraphs = []
lines = []
for page in full_text_annotation.pages:
for block in page.blocks:
for paragraph in block.paragraphs:
para = ""
line = ""
for word in paragraph.words:
for symbol in word.symbols:
line += symbol.text
if symbol.property.detected_break.type == breaks.SPACE:
line += ' '
if symbol.property.detected_break.type == breaks.EOL_SURE_SPACE:
line += ' '
lines.append(line)
para += line
line = ''
if symbol.property.detected_break.type == breaks.LINE_BREAK:
lines.append(line)
para += line
line = ''
paragraphs.append(para)
return deque(paragraphs), lines
def get_image_file(path):
with io.open(path, 'rb') as image_file:
content = image_file.read()
return vision.Image(content=content)
def get_image_uri(uri):
image = vision.Image()
image.source.image_uri = uri
return image
def main():
set_args()
convert = detect_document_text if args.document else detect_text
get_image = get_image_uri if args.url else get_image_file
language_hints = args.languages.split(',')
set_credentials()
convert(get_image(args.path), language_hints, args.full)
if __name__ == "__main__":
main()Metadata
Metadata
Assignees
Labels
No labels