Skip to content

Commit 7c219a0

Browse files
Merge pull request #47 from jonathanrocher/feature/add_step_5.3
Feature: add step 5.3
2 parents 0ed5576 + 0701c73 commit 7c219a0

23 files changed

+687
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Second real version of the pycasa ETS pyface application
2+
Building on the application state 5.1, this version adds a button to the folder
3+
and file views so the faces can be detected.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
__version__ = "0.0.1"

stage5.3_fuller_application/pycasa/app/__init__.py

Whitespace-only changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# coding=utf-8
2+
""" TaskApplication object for the Pycasa app.
3+
"""
4+
import logging
5+
6+
from pyface.tasks.api import TasksApplication, TaskFactory
7+
from ..ui.tasks.pycasa_task import PycasaTask
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
class PycasaApplication(TasksApplication):
13+
""" An application to say hello.
14+
"""
15+
id = "pycasa_application"
16+
17+
name = "Pycasa"
18+
19+
description = "An example Tasks application that explores image files."
20+
21+
def _task_factories_default(self):
22+
return [
23+
TaskFactory(
24+
id='pycasa.pycasa_task_factory',
25+
name="Main Pycasa Task Factory",
26+
factory=PycasaTask
27+
)
28+
]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
from pycasa.app.app import PycasaApplication
3+
4+
5+
def main():
6+
app = PycasaApplication()
7+
app.run()
8+
9+
10+
if __name__ == '__main__':
11+
main()

stage5.3_fuller_application/pycasa/model/__init__.py

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from os.path import expanduser
2+
from traits.api import Directory, Event, HasStrictTraits
3+
4+
5+
class FileBrowser(HasStrictTraits):
6+
root = Directory(expanduser("~"))
7+
8+
#: Item last double-clicked on in the tree view
9+
requested_item = Event
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# General imports
2+
import os
3+
import PIL.Image
4+
from PIL.ExifTags import TAGS
5+
from skimage import data
6+
from skimage.feature import Cascade
7+
import numpy as np
8+
9+
# ETS imports
10+
from traits.api import Array, cached_property, Dict, File, HasStrictTraits, \
11+
List, Property
12+
13+
SUPPORTED_FORMATS = [".png", ".jpg", ".jpeg"]
14+
15+
16+
class ImageFile(HasStrictTraits):
17+
""" Model to hold an image file.
18+
"""
19+
filepath = File
20+
21+
metadata = Property(Dict, depends_on="filepath")
22+
23+
data = Property(Array, depends_on="filepath")
24+
25+
faces = List
26+
27+
def to_array(self):
28+
file_ext = os.path.splitext(self.filepath)[1].lower()
29+
if not self.filepath or file_ext not in SUPPORTED_FORMATS:
30+
return np.array([])
31+
32+
with PIL.Image.open(self.filepath) as img:
33+
return np.asarray(img)
34+
35+
@cached_property
36+
def _get_data(self):
37+
return self.to_array()
38+
39+
@cached_property
40+
def _get_metadata(self):
41+
file_ext = os.path.splitext(self.filepath)[1].lower()
42+
if not self.filepath or file_ext not in SUPPORTED_FORMATS:
43+
return {}
44+
45+
with PIL.Image.open(self.filepath) as img:
46+
exif = img._getexif()
47+
48+
if exif:
49+
return {TAGS[k]: v for k, v in exif.items()
50+
if k in TAGS}
51+
else:
52+
return {}
53+
54+
def detect_faces(self, scale_factor=1.2, step_ratio=1, min_size=60,
55+
max_size=600):
56+
""" Detect faces in the image.
57+
"""
58+
trained_file = data.lbp_frontal_face_cascade_filename()
59+
detector = Cascade(trained_file)
60+
faces = detector.detect_multi_scale(img=self.data,
61+
scale_factor=scale_factor,
62+
step_ratio=step_ratio,
63+
min_size=(min_size, min_size),
64+
max_size=(max_size, max_size))
65+
self.faces = faces
66+
return faces
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# General imports
2+
import os
3+
4+
import pandas as pd
5+
import numpy as np
6+
7+
# ETS imports
8+
from traits.api import Directory, Event, HasStrictTraits, Instance
9+
10+
# Local imports
11+
from .image_file import ImageFile, SUPPORTED_FORMATS
12+
13+
FILENAME_COL = "filename"
14+
15+
NUM_FACE_COL = "Num. faces"
16+
17+
18+
class ImageFolder(HasStrictTraits):
19+
""" Model to hold an image folder.
20+
"""
21+
path = Directory
22+
23+
data = Instance(pd.DataFrame)
24+
25+
data_updated = Event
26+
27+
def __init__(self, **traits):
28+
# Don't forget this!
29+
super(ImageFolder, self).__init__(**traits)
30+
if not os.path.isdir(self.path):
31+
msg = f"Unable to create an ImageFolder from {self.path} since" \
32+
f" it is not a valid directory."
33+
raise ValueError(msg)
34+
35+
self.data = self.to_dataframe()
36+
37+
def to_dataframe(self):
38+
if not self.path:
39+
return pd.DataFrame({FILENAME_COL: [], NUM_FACE_COL: []})
40+
41+
data = []
42+
for filename in os.listdir(self.path):
43+
file_ext = os.path.splitext(filename)[1].lower()
44+
if file_ext in SUPPORTED_FORMATS:
45+
filepath = os.path.join(self.path, filename)
46+
img_file = ImageFile(filepath=filepath)
47+
file_data = {FILENAME_COL: filename, NUM_FACE_COL: np.nan}
48+
try:
49+
file_data.update(img_file.metadata)
50+
except Exception:
51+
pass
52+
data.append(file_data)
53+
54+
return pd.DataFrame(data)
55+
56+
def compute_num_faces(self, **kwargs):
57+
cols = list(self.data.columns)
58+
for i, filename in enumerate(self.data[FILENAME_COL]):
59+
print(filename)
60+
filepath = os.path.join(self.path, filename)
61+
img_file = ImageFile(filepath=filepath)
62+
faces = img_file.detect_faces(**kwargs)
63+
j = cols.index(NUM_FACE_COL)
64+
self.data.iloc[i, j] = len(faces)
65+
self.data_updated = True

stage5.3_fuller_application/pycasa/model/tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)