Skip to content
This repository was archived by the owner on Nov 8, 2021. It is now read-only.

Commit 856d9bc

Browse files
huxuanwangjun-microsoft
authored andcommitted
Initial version for sample. (#6)
* Initial version for sample. * README update. - Remove TODO section. - Remove redundant empty lines. * Remove Jupyter Notebook and corresponding README. * Several logic bug fix. * Several view bugs have been fixed.
1 parent d4041d6 commit 856d9bc

17 files changed

+1719
-296
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,4 @@ ENV/
9191
# Custom
9292
*.swp
9393
config.py
94+
Subscription.txt
1.24 KB
Loading

Jupyter Notebook/Face Detection Example.ipynb

Lines changed: 0 additions & 283 deletions
This file was deleted.

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ clean:
44
find . -name '*.pyc' -print0 | xargs -0 rm -f
55
find . -name '*.swp' -print0 | xargs -0 rm -f
66
find . -name '__pycache__' -print0 | xargs -0 rm -rf
7-
-rm -rf build dist *.egg-info
7+
-rm -rf build dist *.egg-info .eggs
88

99
deps:
1010
pip install -r requirements.txt
@@ -15,13 +15,13 @@ install:
1515
lint: pep8 pyflakes pylint
1616

1717
pep8:
18-
-pep8 --statistics --count cognitive_face setup.py
18+
-pep8 --statistics --count cognitive_face setup.py sample
1919

2020
pyflakes:
21-
-pyflakes cognitive_face setup.py
21+
-pyflakes cognitive_face setup.py sample
2222

2323
pylint:
24-
-pylint --rcfile=.pylintrc cognitive_face setup.py
24+
-pylint --rcfile=.pylintrc cognitive_face setup.py sample/*
2525

2626
test:
2727
python setup.py test

README.md

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,25 @@ result = CF.face.detect(img_url)
3838
print result
3939
```
4040

41-
## TODO
41+
## Sample
4242

43-
- More robust unittests (Only test the most basic functionality currently).
44-
- Add native support for json response by adding some models like `Face`, `FaceList`, `Person` and `PersonGroup`.
43+
A Python SDK sample built with wxPython is also provided, before execution,
44+
please install all components listed below.
4545

46-
# Microsoft Face API: Jupyter Notebook
47-
This [Jupyter Notebook](<http://jupyter.org/>) demonstrates how to use Python with the Microsoft Face API, an offering within [Microsoft Cognitive Services](https://www.microsoft.com/cognitive-services), formerly known as Project Oxford.
48-
* [Learn about the Face API](https://www.microsoft.com/cognitive-services/en-us/face-api)
49-
* [Read the documentation](https://www.microsoft.com/cognitive-services/en-us/face-api/documentation/overview)
50-
* [Find more SDKs & Samples](https://www.microsoft.com/cognitive-services/en-us/SDK-Sample?api=face)
46+
### Sample Prerequisite
5147

48+
- [Python 2.7](https://www.python.org/downloads/) (only Python 2 supported due
49+
to limitation of wxPython)
50+
- [wxPython](https://wxpython.org/)
51+
- [cognitive_face package](https://pypi.python.org/pypi/cognitive_face)
5252

53+
### Sample Execution
54+
55+
```bash
56+
git clone https://github.com/Microsoft/Cognitive-Face-Python.git
57+
cd Cognitive-Face-Python
58+
python sample
59+
```
5360

5461
## Contributing
5562
We welcome contributions. Feel free to file issues and pull requests on the repo and we'll address them as we can. Learn more about how you can help on our [Contribution Rules & Guidelines](</CONTRIBUTING.md>).
@@ -69,6 +76,5 @@ All Microsoft Cognitive Services SDKs and samples are licensed with the MIT Lice
6976

7077
Sample images are licensed separately, please refer to [LICENSE-IMAGE](</LICENSE-IMAGE.md>)
7178

72-
7379
## Developer Code of Conduct
7480
Developers using Cognitive Services, including this sample, are expected to follow the “Developer Code of Conduct for Microsoft Cognitive Services”, found at [http://go.microsoft.com/fwlink/?LinkId=698895](http://go.microsoft.com/fwlink/?LinkId=698895).

sample/__main__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
File: main.py
5+
Description: main script for Python SDK sample.
6+
"""
7+
8+
from view import MyApp
9+
10+
11+
if __name__ == "__main__":
12+
app = MyApp(False)
13+
app.MainLoop()

sample/model/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
File: __init__.py
5+
Description: Model components for Python SDK sample.
6+
"""
7+
from model.face import Face

sample/model/face.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
File: face.py
5+
Description: Face model for Python SDK Sample.
6+
"""
7+
8+
import wx
9+
10+
import util
11+
12+
13+
class Face(object):
14+
"""Face Model for each face."""
15+
def __init__(self, res, path, size=util.MAX_THUMBNAIL_SIZE):
16+
super(Face, self).__init__()
17+
self.path = path
18+
self.bmp = wx.Bitmap(path)
19+
self.name = None
20+
if res.get('faceId'):
21+
self.id = res['faceId']
22+
if res.get('persistedFaceId'):
23+
self.persisted_id = res['persistedFaceId']
24+
if res.get('faceRectangle'):
25+
rect = res['faceRectangle']
26+
self.left = int(rect['left'])
27+
self.top = int(rect['top'])
28+
self.width = int(rect['width'])
29+
self.height = int(rect['height'])
30+
self.bmp = self.bmp.GetSubBitmap(wx.Rect(
31+
self.left, self.top, self.width, self.height))
32+
if res.get('faceAttributes'):
33+
attr = res['faceAttributes']
34+
self.age = int(attr['age'])
35+
self.gender = attr['gender']
36+
self.smile = float(attr['smile']) > 0 and 'Smile' or 'Not Smile'
37+
self.glasses = attr['glasses']
38+
self.bmp = util.scale_bitmap(self.bmp, size)
39+
40+
def set_name(self, name):
41+
"""Set the name for the face."""
42+
self.name = name

sample/util.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
File: util.py
5+
Description: util module for Python SDK sample.
6+
"""
7+
8+
from threading import Thread
9+
import os.path
10+
11+
import wx
12+
13+
try:
14+
import cognitive_face as CF
15+
except ImportError:
16+
import sys
17+
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
18+
sys.path.insert(0, ROOT_DIR)
19+
import cognitive_face as CF
20+
21+
22+
IMAGE_WILDCARD = 'Image files (*.jpg, *.png)|*.jpg; *.png'
23+
INNER_PANEL_WIDTH = 710
24+
MAX_IMAGE_SIZE = 300
25+
MAX_THUMBNAIL_SIZE = 75
26+
STYLE = wx.SIMPLE_BORDER
27+
SUBSCRIPTION_KEY_FILENAME = 'Subscription.txt'
28+
29+
LOG_FACE_LIST_REQUEST = (
30+
'Request: Face List {} will be used for build person database. '
31+
'Checking whether group exists.'
32+
)
33+
LOG_FACE_LIST_NOT_EXIST = 'Response: Face List {} does not exist before.'
34+
LOG_FACE_LIST_EXIST = 'Response: Face List {} exists.'
35+
36+
37+
class SubscriptionKey(object):
38+
"""Subscription Key."""
39+
40+
@classmethod
41+
def get(cls):
42+
"""Get the subscription key."""
43+
if not hasattr(cls, 'key'):
44+
cls.key = ''
45+
if not cls.key:
46+
if os.path.isfile(SUBSCRIPTION_KEY_FILENAME):
47+
with file(SUBSCRIPTION_KEY_FILENAME) as fin:
48+
cls.key = fin.read().strip()
49+
else:
50+
cls.key = ''
51+
CF.Key.set(cls.key)
52+
return cls.key
53+
54+
@classmethod
55+
def set(cls, key):
56+
"""Set the subscription key."""
57+
cls.key = key
58+
with file(SUBSCRIPTION_KEY_FILENAME, 'w') as fout:
59+
print >>fout, key
60+
CF.Key.set(cls.key)
61+
62+
@classmethod
63+
def delete(cls):
64+
"""Delete the subscription key."""
65+
cls.key = ''
66+
if os.path.isfile(SUBSCRIPTION_KEY_FILENAME):
67+
os.remove(SUBSCRIPTION_KEY_FILENAME)
68+
CF.Key.set(cls.key)
69+
70+
71+
def scale_bitmap(bitmap, size=MAX_IMAGE_SIZE):
72+
"""Scale the image."""
73+
img = bitmap.ConvertToImage()
74+
width = img.GetWidth()
75+
height = img.GetHeight()
76+
if width > height:
77+
new_width = size
78+
new_height = size * height / width
79+
else:
80+
new_height = size
81+
new_width = size * width / height
82+
img = img.Scale(new_width, new_height)
83+
return wx.BitmapFromImage(img)
84+
85+
86+
def draw_bitmap_rectangle(bitmap, faces):
87+
"""Draw rectangle on bitmap."""
88+
dc = wx.MemoryDC(bitmap.bmp)
89+
dc.SetPen(wx.BLUE_PEN)
90+
dc.SetBrush(wx.TRANSPARENT_BRUSH)
91+
dc.SetTextBackground('black')
92+
dc.SetTextForeground('white')
93+
dc.SetBackgroundMode(wx.SOLID)
94+
dc.SetFont(wx.Font(8,
95+
wx.FONTFAMILY_DEFAULT,
96+
wx.FONTSTYLE_NORMAL,
97+
wx.FONTWEIGHT_BOLD))
98+
for face in faces:
99+
dc.DrawRectangle(
100+
face.left * bitmap.scale,
101+
face.top * bitmap.scale,
102+
face.width * bitmap.scale,
103+
face.height * bitmap.scale,
104+
)
105+
if face.name:
106+
text_width, text_height = dc.GetTextExtent(face.name)
107+
dc.DrawText(face.name,
108+
face.left * bitmap.scale,
109+
face.top * bitmap.scale - text_height)
110+
dc.SelectObject(wx.NullBitmap)
111+
bitmap.bitmap.SetBitmap(bitmap.bmp)
112+
113+
114+
def async(func):
115+
"""Async wrapper."""
116+
def wrapper(*args, **kwargs):
117+
"""docstring for wrapper"""
118+
thr = Thread(target=func, args=args, kwargs=kwargs)
119+
thr.start()
120+
return wrapper

sample/view/__init__.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
File: __init__.py
5+
Description: View components for Python SDK sample.
6+
"""
7+
8+
import wx
9+
import wx.lib.agw.labelbook as LB
10+
11+
from wx.lib.agw.fmresources import INB_FIT_LABELTEXT
12+
from wx.lib.agw.fmresources import INB_LEFT
13+
from wx.lib.agw.fmresources import INB_NO_RESIZE
14+
15+
from view.panel_detection import DetectionPanel
16+
from view.panel_subscription import SubscriptionPanel
17+
from view.panel_find_similar import FindSimilarPanel
18+
from view.panel_group import GroupPanel
19+
from view.panel_identification import IdentificationPanel
20+
from view.panel_verification import VerificationPanel
21+
22+
TITLE = u"Microsoft Cognitive Services Face Samples"
23+
24+
25+
class MyLabelBook(LB.LabelBook):
26+
"""LabelBook part in Main Frame."""
27+
def __init__(self, parent):
28+
agw_style = INB_LEFT | INB_FIT_LABELTEXT | INB_NO_RESIZE
29+
super(MyLabelBook, self).__init__(parent, agwStyle=agw_style)
30+
31+
subscription_panel = SubscriptionPanel(self)
32+
subscription_text = u"Subscription Key Management"
33+
self.AddPage(subscription_panel, subscription_text, True)
34+
35+
self.AddPage(wx.Panel(self), u"Select a scenario:")
36+
self.EnableTab(1, False)
37+
38+
self.AddPage(DetectionPanel(self), u" - Face Detection")
39+
self.AddPage(FindSimilarPanel(self), u" - Face Find Similar")
40+
self.AddPage(GroupPanel(self), u" - Face Grouping")
41+
self.AddPage(IdentificationPanel(self), u" - Face Identification")
42+
self.AddPage(VerificationPanel(self), u" - Face Verification")
43+
44+
45+
class MyTitle(wx.Panel):
46+
"""Title part in Main Frame."""
47+
def __init__(self, parent):
48+
super(MyTitle, self).__init__(parent)
49+
self.SetBackgroundColour('#00b294')
50+
self.SetMinSize((-1, 80))
51+
52+
sizer = wx.BoxSizer()
53+
sizer.AddStretchSpacer()
54+
55+
family = wx.FONTFAMILY_DEFAULT
56+
style = wx.FONTSTYLE_NORMAL
57+
weight = wx.FONTWEIGHT_NORMAL
58+
font = wx.Font(20, family, style, weight)
59+
self.text = wx.StaticText(self, label=TITLE, style=wx.ALIGN_CENTER)
60+
self.text.SetFont(font)
61+
sizer.Add(self.text, flag=wx.ALIGN_CENTER_VERTICAL)
62+
63+
sizer.AddStretchSpacer()
64+
self.SetSizer(sizer)
65+
66+
67+
class MyFrame(wx.Frame):
68+
"""Main Frame."""
69+
def __init__(self, parent):
70+
super(MyFrame, self).__init__(parent, title=TITLE, size=(1280, 768))
71+
72+
icon_path = 'Assets/Microsoft-logo_rgb_c-gray.png'
73+
self.SetIcon(wx.Icon(icon_path))
74+
75+
sizer = wx.BoxSizer(wx.VERTICAL)
76+
77+
self.title = MyTitle(self)
78+
sizer.Add(self.title, flag=wx.EXPAND)
79+
80+
self.book = MyLabelBook(self)
81+
sizer.Add(self.book, 1, flag=wx.EXPAND)
82+
83+
status_text = (
84+
'Microsoft will receive the images you upload and may use them to '
85+
'improve Face API and related services. By submitting an image, '
86+
'you confirm you have consent from everyone in it.'
87+
)
88+
self.status = wx.StatusBar(self)
89+
self.status.SetStatusText(status_text)
90+
sizer.Add(self.status, flag=wx.EXPAND)
91+
92+
self.SetSizer(sizer)
93+
self.Layout()
94+
95+
96+
class MyApp(wx.App):
97+
"""The whole app."""
98+
def OnInit(self):
99+
"""Show main frame."""
100+
frame = MyFrame(None)
101+
frame.Show()
102+
return True

0 commit comments

Comments
 (0)