-
Notifications
You must be signed in to change notification settings - Fork 0
/
textract.py
276 lines (243 loc) · 11.1 KB
/
textract.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""
Purpose
Shows how to use the AWS SDK for Python (Boto3) with Amazon Textract to
detect text, form, and table elements in document images.
"""
import json
import logging
from botocore.exceptions import ClientError
logger = logging.getLogger(__name__)
# snippet-start:[python.example_code.textract.TextractWrapper]
class TextractWrapper:
"""Encapsulates Textract functions."""
def __init__(self, textract_client, s3_resource, sqs_resource):
"""
:param textract_client: A Boto3 Textract client.
:param s3_resource: A Boto3 Amazon S3 resource.
:param sqs_resource: A Boto3 Amazon SQS resource.
"""
self.textract_client = textract_client
self.s3_resource = s3_resource
self.sqs_resource = sqs_resource
# snippet-end:[python.example_code.textract.TextractWrapper]
# snippet-start:[python.example_code.textract.DetectDocumentText]
def detect_file_text(self, document_file_name=None, document_bytes=None):
"""
Detects text elements in a local image file or from in-memory byte data.
The image must be in PNG or JPG format.
:param document_file_name: The name of a document image file.
:param document_bytes: In-memory byte data of a document image.
:return: The response from Amazon Textract, including a list of blocks
that describe elements detected in the image.
"""
if document_file_name is not None:
with open(document_file_name, "rb") as document_file:
document_bytes = document_file.read()
try:
response = self.textract_client.detect_document_text(
Document={"Bytes": document_bytes}
)
logger.info("Detected %s blocks.", len(response["Blocks"]))
except ClientError:
logger.exception("Couldn't detect text.")
raise
else:
return response
# snippet-end:[python.example_code.textract.DetectDocumentText]
# snippet-start:[python.example_code.textract.AnalyzeDocument]
def analyze_file(
self, feature_types, document_file_name=None, document_bytes=None
):
"""
Detects text and additional elements, such as forms or tables, in a local image
file or from in-memory byte data.
The image must be in PNG or JPG format.
:param feature_types: The types of additional document features to detect.
:param document_file_name: The name of a document image file.
:param document_bytes: In-memory byte data of a document image.
:return: The response from Amazon Textract, including a list of blocks
that describe elements detected in the image.
"""
if document_file_name is not None:
with open(document_file_name, "rb") as document_file:
document_bytes = document_file.read()
try:
response = self.textract_client.analyze_document(
Document={"Bytes": document_bytes}, FeatureTypes=feature_types
)
logger.info("Detected %s blocks.", len(response["Blocks"]))
except ClientError:
logger.exception("Couldn't detect text.")
raise
else:
return response
# snippet-end:[python.example_code.textract.AnalyzeDocument]
# snippet-start:[python.example_code.textract.helper.prepare_job]
def prepare_job(self, bucket_name, document_name, document_bytes):
"""
Prepares a document image for an asynchronous detection job by uploading
the image bytes to an Amazon S3 bucket. Amazon Textract must have permission
to read from the bucket to process the image.
:param bucket_name: The name of the Amazon S3 bucket.
:param document_name: The name of the image stored in Amazon S3.
:param document_bytes: The image as byte data.
"""
try:
bucket = self.s3_resource.Bucket(bucket_name)
bucket.upload_fileobj(document_bytes, document_name)
logger.info("Uploaded %s to %s.", document_name, bucket_name)
except ClientError:
logger.exception("Couldn't upload %s to %s.", document_name, bucket_name)
raise
# snippet-end:[python.example_code.textract.helper.prepare_job]
# snippet-start:[python.example_code.textract.helper.check_job_queue]
def check_job_queue(self, queue_url, job_id):
"""
Polls an Amazon SQS queue for messages that indicate a specified Textract
job has completed.
:param queue_url: The URL of the Amazon SQS queue to poll.
:param job_id: The ID of the Textract job.
:return: The status of the job.
"""
status = None
try:
queue = self.sqs_resource.Queue(queue_url)
messages = queue.receive_messages()
if messages:
msg_body = json.loads(messages[0].body)
msg = json.loads(msg_body["Message"])
if msg.get("JobId") == job_id:
messages[0].delete()
status = msg.get("Status")
logger.info(
"Got message %s with status %s.", messages[0].message_id, status
)
else:
logger.info("No messages in queue %s.", queue_url)
except ClientError:
logger.exception("Couldn't get messages from queue %s.", queue_url)
else:
return status
# snippet-end:[python.example_code.textract.helper.check_job_queue]
# snippet-start:[python.example_code.textract.StartDocumentTextDetection]
def start_detection_job(
self, bucket_name, document_file_name, sns_topic_arn, sns_role_arn
):
"""
Starts an asynchronous job to detect text elements in an image stored in an
Amazon S3 bucket. Textract publishes a notification to the specified Amazon SNS
topic when the job completes.
The image must be in PNG, JPG, or PDF format.
:param bucket_name: The name of the Amazon S3 bucket that contains the image.
:param document_file_name: The name of the document image stored in Amazon S3.
:param sns_topic_arn: The Amazon Resource Name (ARN) of an Amazon SNS topic
where the job completion notification is published.
:param sns_role_arn: The ARN of an AWS Identity and Access Management (IAM)
role that can be assumed by Textract and grants permission
to publish to the Amazon SNS topic.
:return: The ID of the job.
"""
try:
response = self.textract_client.start_document_text_detection(
DocumentLocation={
"S3Object": {"Bucket": bucket_name, "Name": document_file_name}
},
NotificationChannel={
"SNSTopicArn": sns_topic_arn,
"RoleArn": sns_role_arn,
},
)
job_id = response["JobId"]
logger.info(
"Started text detection job %s on %s.", job_id, document_file_name
)
except ClientError:
logger.exception("Couldn't detect text in %s.", document_file_name)
raise
else:
return job_id
# snippet-end:[python.example_code.textract.StartDocumentTextDetection]
# snippet-start:[python.example_code.textract.GetDocumentTextDetection]
def get_detection_job(self, job_id):
"""
Gets data for a previously started text detection job.
:param job_id: The ID of the job to retrieve.
:return: The job data, including a list of blocks that describe elements
detected in the image.
"""
try:
response = self.textract_client.get_document_text_detection(JobId=job_id)
job_status = response["JobStatus"]
logger.info("Job %s status is %s.", job_id, job_status)
except ClientError:
logger.exception("Couldn't get data for job %s.", job_id)
raise
else:
return response
# snippet-end:[python.example_code.textract.GetDocumentTextDetection]
# snippet-start:[python.example_code.textract.StartDocumentAnalysis]
def start_analysis_job(
self,
bucket_name,
document_file_name,
feature_types,
sns_topic_arn,
sns_role_arn,
):
"""
Starts an asynchronous job to detect text and additional elements, such as
forms or tables, in an image stored in an Amazon S3 bucket. Textract publishes
a notification to the specified Amazon SNS topic when the job completes.
The image must be in PNG, JPG, or PDF format.
:param bucket_name: The name of the Amazon S3 bucket that contains the image.
:param document_file_name: The name of the document image stored in Amazon S3.
:param feature_types: The types of additional document features to detect.
:param sns_topic_arn: The Amazon Resource Name (ARN) of an Amazon SNS topic
where job completion notification is published.
:param sns_role_arn: The ARN of an AWS Identity and Access Management (IAM)
role that can be assumed by Textract and grants permission
to publish to the Amazon SNS topic.
:return: The ID of the job.
"""
try:
response = self.textract_client.start_document_analysis(
DocumentLocation={
"S3Object": {"Bucket": bucket_name, "Name": document_file_name}
},
NotificationChannel={
"SNSTopicArn": sns_topic_arn,
"RoleArn": sns_role_arn,
},
FeatureTypes=feature_types,
)
job_id = response["JobId"]
logger.info(
"Started text analysis job %s on %s.", job_id, document_file_name
)
except ClientError:
logger.exception("Couldn't analyze text in %s.", document_file_name)
raise
else:
return job_id
# snippet-end:[python.example_code.textract.StartDocumentAnalysis]
# snippet-start:[python.example_code.textract.GetDocumentAnalysis]
def get_analysis_job(self, job_id):
"""
Gets data for a previously started detection job that includes additional
elements.
:param job_id: The ID of the job to retrieve.
:return: The job data, including a list of blocks that describe elements
detected in the image.
"""
try:
response = self.textract_client.get_document_analysis(JobId=job_id)
job_status = response["JobStatus"]
logger.info("Job %s status is %s.", job_id, job_status)
except ClientError:
logger.exception("Couldn't get data for job %s.", job_id)
raise
else:
return response
# snippet-end:[python.example_code.textract.GetDocumentAnalysis]