Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
84fc93f
Add OCR engine as an Enum
NegassaB Dec 29, 2021
404334c
correct typo
NegassaB Dec 29, 2021
6ade1cd
Update example.py to include usage of engine_two
NegassaB Dec 29, 2021
bf672d3
Update raised error types
NegassaB Dec 29, 2021
405f9da
Merge branch 'add_ocrengine'
NegassaB Dec 29, 2021
8398ee9
Update setup.py to include the needed package
NegassaB Dec 29, 2021
4a9fa29
update requirements.txt to list out all that are needed
NegassaB Dec 29, 2021
9743184
Timout exception, update README
NegassaB Dec 29, 2021
abdbfd6
Merge branch 'optimize'
NegassaB Dec 29, 2021
68fda59
update README.md
NegassaB Dec 29, 2021
77f17fb
Per request change
NegassaB Jan 5, 2022
7b1f2d3
Merge branch 'review'
NegassaB Jan 5, 2022
f093f67
Per review #2
NegassaB Jan 5, 2022
88b2a8a
Merge branch 'review'
NegassaB Jan 5, 2022
83799af
Merge branch 'review'
NegassaB Jan 5, 2022
8d58ee9
Per 2nd review
NegassaB Jan 5, 2022
665fccc
Merge branch 'master' of github.com:NegassaB/ocrspace
NegassaB Jan 5, 2022
96a7654
Per 2nd review
NegassaB Jan 5, 2022
c8ae64a
Merge branch 'master' of github.com:NegassaB/ocrspace
NegassaB Jan 5, 2022
593ed79
Add a headers parameter, move apikey to headers
NegassaB Jan 5, 2022
fadf892
Merge branch 'review'
NegassaB Jan 5, 2022
6392996
Per 3rd review
NegassaB Jan 5, 2022
38f7398
Merge branch 'use_headers' into copied
NegassaB Jan 5, 2022
ebb3b56
Merge branch 'use_headers'
NegassaB Jan 6, 2022
dbcf91a
Per the 4th review
NegassaB Jan 6, 2022
52c143b
add maintainer's name
NegassaB May 19, 2022
c9ffd78
Updated in accordance with the 6th review
NegassaB May 21, 2022
649ed1f
Merge branch 'release'
NegassaB May 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,6 @@ ENV/
# Rope project settings
.ropeproject
MANIFEST

# vscode
.vscode/
3 changes: 2 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
MIT License
MIT License

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for leading space either


Copyright (c) 2017 Ali Najafi
Copyright (c) 2019 Erik Bøsen
Copyright (c) 2022 Negassa Berhanu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
41 changes: 34 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,61 @@
> A Python wrapper for using the [ocr.space API](https://ocr.space/ocrapi).

## Installation

Simply install from `pip`:

```sh
pip install ocrspace
```

## Use

First, get an API key from [Free OCR API](https://ocr.space/OCRAPI), otherwise the default apikey `helloworld` will be used, which is [severely rate limited.](https://ocr.space/faq#span12)

First you'll need to import and instantiate the API wrapper:
```py

```python
import ocrspace

api_key = 'apikey retrieved from Free OCR API'

api = ocrspace.API(api_key=api_key)
# Or if you have a custom API host, API key or desired language, pass those:
api = ocrspace.API(endpoint='https://example.host', api_key=api_key, language=ocrspace.Language.Croatian)
```

If you wish to change the OCR engine used, use the enum class Engine from ocrspace and pass the value of ocrspace.Engine.ENGINE_2 to the api instantiation. By default it uses ocrspace.Engine.ENGINE_1:

```python
import ocrspace
api = ocrspace.API()

api_key = "apikey retrieved from Free OCR API"

api = ocrspace.API(api_key=api_key, engine=ocrspace.Engine.ENGINE_2)
# Or if you have a custom API host, API key or desired language, pass those:
api = ocrspace.API(endpoint='https://example.host', api_key='Insert key here', language=ocrspace.Language.Croatian)
api = ocrspace.API(endpoint='https://example.host', api_key=api_key, language=ocrspace.Language.Croatian, engine=ocrspace.Engine.ENGINE_2)
```

To perform recognition on an image hosted at some URL:
```py

```python
api.ocr_url('URL of image goes here')
```

Or, if you have an image locally upon which to perform recognition:
```py

```python
api.ocr_file('image.jpg')
# or:
api.ocr_file(open('image.jpg', 'rb')) # or any other file pointer
```
That's it! Look at [`example.py`](example.py) for a demonstration.

That's it! Look at [`example.py`](example.py) for a demonstration.

## Authorship
This package was created by [Ali Najafi](https://github.com/a4fr) and is maintained by [Erik Boesen](https://github.com/ErikBoesen).

This package was created by [Ali Najafi](https://github.com/a4fr) and is maintained by [Erik Boesen](https://github.com/ErikBoesen) and [Negassa Berhanu](https://github.com/NegassaB).

## License

[MIT](LICENSE)
16 changes: 14 additions & 2 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
import ocrspace
from ocrspace import Engine
Comment thread
NegassaB marked this conversation as resolved.
import requests


# api with the default ocrengine aka engine 1
api = ocrspace.API()

# api with engine 2
api_with_engine_two = ocrspace.API(engine=Engine.ENGINE_2)
TEST_IMAGE_URL = 'https://images-na.ssl-images-amazon.com/images/I/71ovNJN1URL._SL1244_.jpg'

print('Testing URL-based OCR:')
print(api.ocr_url(TEST_IMAGE_URL))
print('Testing URL-based OCR using engine_two:')
print(api_with_engine_two.ocr_url(TEST_IMAGE_URL))

print('Testing file-based OCR:')
# Download image for demo purposes
TEST_FILENAME = '/tmp/test_image.jpg'
with open(TEST_FILENAME, 'wb') as f:
r = requests.get(TEST_IMAGE_URL)
r.raw.decode_content = True
f.write(r.content)

print('Testing file-based OCR:')
# With file path
print(api.ocr_file(TEST_FILENAME))
# With file pointer
print(api.ocr_file(open(TEST_FILENAME, 'rb')))

print('Testing file-based OCR using engine_two:')
# With file path
print(api_with_engine_two.ocr_file(TEST_FILENAME))
# With file pointer
print(api_with_engine_two.ocr_file(open(TEST_FILENAME, 'rb')))
65 changes: 48 additions & 17 deletions ocrspace/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import requests
from enum import IntEnum


class Language:
Expand Down Expand Up @@ -28,25 +29,40 @@ class Language:
Turkish = 'tur'


class Engine(IntEnum):
"""
Enum representing the OCR engine to use
"""
ENGINE_1 = 1
ENGINE_2 = 2


class API:
def __init__(
self,
endpoint='https://api.ocr.space/parse/image',
api_key='helloworld',
language=Language.English,
engine=Engine.ENGINE_1,
**kwargs,
):
"""
:param endpoint: API endpoint to contact
:param api_key: API key string
:param language: document language
:param engine: ocr engine to use
:param **kwargs: other settings to API
"""
if not isinstance(engine, Engine):
raise TypeError('engine must be an instance of Engine')
if engine.value != 1 and engine.value != 2:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this check is needed; it's impossible to make an IntEnum class with an invalid value as far as I know

raise ValueError('the value of engine must be either 1 or 2, use ocrspace.Engine')
self.endpoint = endpoint
self.api_key = api_key
self.payload = {
'isOverlayRequired': True,
'apikey': api_key,
'language': language,
'OCREngine': engine.value,
**kwargs
}

Expand All @@ -57,21 +73,44 @@ def _parse(self, raw):
raise Exception(raw['ErrorMessage'][0])
return raw['ParsedResults'][0]['ParsedText']


def ocr_file(self, fp):
def query_api(self, image_url=None, image_file=None):
"""
Process image from a local path.
:param fp: A path or pointer to your file
Process the provided parameter.
:param image_url: An Image url or base64image encoded string

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may have forgotten to use this parameter in the function

:param image_file: A path or file pointer to the image file
:return: Result in JSON format
:raise: request.exceptions or general Exception
"""
with (open(fp, 'rb') if type(fp) == str else fp) as f:

if image_file:
r = requests.post(
self.endpoint,
files={'filename': f},
headers={'apikey': self.api_key},
files={'filename': image_file},
data=self.payload,
timeout=30
)
elif image_url:
r = requests.post(
self.endpoint,
headers={'apikey': self.api_key},
data=image_url,
timeout=30
)
Comment thread
NegassaB marked this conversation as resolved.
else:
raise TypeError('either image_file or image_url must be provided')
r.raise_for_status()
return self._parse(r.json())

def ocr_file(self, fp):
"""
Process image from a local path.
:param fp: A path or pointer to your file
:return: Result in JSON format
"""
with (open(fp, 'rb') if type(fp) == str else fp) as f:

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of dense, can we reorganize to be a bit clearer?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you clarify on this. Do you want to reorganize the code or the documentation? and if so how do you want to reorganize the code or documentation? Dense doesn't really communicate what you want to do

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code. As in, split the ternary if/else into a full if/else statement across multiple lines so that we don't have a ton of logic in a single line making it difficult to read and understand at a glance

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you clarify on this. Do you want to reorganize the code or the documentation? and if so how do you want to reorganize the code or documentation? Dense doesn't really communicate what you want to do

The code could be made more readable (or more verbose).

return self.query_api(image_file=f)

def ocr_url(self, url):
"""
Process an image at a given URL.
Expand All @@ -80,11 +119,7 @@ def ocr_url(self, url):
"""
data = self.payload
data['url'] = url
r = requests.post(
self.endpoint,
data=data,
)
return self._parse(r.json())
return self.query_api(image_url=data)

def ocr_base64(self, base64image):
"""
Expand All @@ -94,8 +129,4 @@ def ocr_base64(self, base64image):
"""
data = self.payload
data['base64Image'] = base64image
r = requests.post(
self.endpoint,
data=data,
)
return self._parse(r.json())
return self.query_api(image_url=data)
19 changes: 10 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from distutils.core import setup

setup(
name = 'ocrspace',
packages = ['ocrspace'], # this must be the same as the name above
version = '2.3.0',
description = 'Perform OCR through ocr.space API',
author = ['Ali Najafi', 'Erik Boesen'],
author_email = 'me@erikboesen.com',
url = 'https://github.com/ErikBoesen/ocrspace',
keywords = ['ocr'],
classifiers = [],
name='ocrspace',
packages=['ocrspace'], # this must be the same as the name above
requires=['requests'],
version='2.4.0',
description='Perform OCR through ocr.space API',
author=['Ali Najafi', 'Erik Boesen', 'Negassa Berhanu'],
author_email='me@erikboesen.com',
url='https://github.com/ErikBoesen/ocrspace',
keywords=['ocr'],
classifiers=[],
)