dict handling down to and from analysis routines (#19)

Этот коммит содержится в:
Inga Ulusoy 2022-08-07 20:48:21 +02:00 коммит произвёл GitHub
родитель 90c6087fdf
Коммит 995cfec923
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 131 добавлений и 35 удалений

Просмотреть файл

@ -1,6 +1,5 @@
[flake8]
ignore = E203, E266, E402, E501, W503, F403, F401, F841
ignore = F401, E402, E501
exclude = .git,__pycache__,.ipynb_checkpoints
max-line-length = 90
max-complexity = 18
select = B,C,E,F,W,T4,B9
max-complexity = 18

Просмотреть файл

@ -1,6 +1,5 @@
[flake8_nb]
ignore = E203, E266, E402, E501, W503, F403, F401, F841
ignore = F401, E402, E501
exclude = .git,__pycache__,.ipynb_checkpoints
max-line-length = 90
max-complexity = 18
select = B,C,E,F,W,T4,B9
max-complexity = 18

Просмотреть файл

@ -17,15 +17,18 @@ class JSONContainer:
return self._data
def explore_analysis(image_paths, identify="faces"):
def explore_analysis(mydict, identify="faces"):
# dictionary mapping the type of analysis to be explored
identify_dict = {
"faces": faces.facial_expression_analysis,
"text-on-image": text.detect_text,
}
# create a list containing the image ids for the widget
# image_paths = [mydict[key]["filename"] for key in mydict.keys()]
image_ids = [key for key in mydict.keys()]
# Create an image selector widget
image_select = ipywidgets.Select(
options=image_paths, layout=ipywidgets.Layout(width="20%"), rows=20
options=image_ids, layout=ipywidgets.Layout(width="20%"), rows=20
)
# Set up the facial recognition output widget
@ -44,14 +47,18 @@ def explore_analysis(image_paths, identify="faces"):
output.clear_output()
# Create the new content
image_widget.children = (ipywidgets.Image.from_file(image_select.value),)
image_widget.children = (
ipywidgets.Image.from_file(mydict[image_select.value]["filename"]),
)
# This output widget absorbes print statements that are messing with
# the widget output and cannot be disabled through the API.
with faces.NocatchOutput():
analysis = identify_dict[identify](image_select.value)
mydict[image_select.value] = identify_dict[identify](
mydict[image_select.value]
)
with output:
display(JSONContainer(analysis))
display(JSONContainer(mydict[image_select.value]))
# Register the handler and trigger it immediately
image_select.observe(switch, names=("value",), type="change")

Просмотреть файл

@ -72,16 +72,21 @@ retinaface_model = DownloadResource(
)
def facial_expression_analysis(img_path):
result = {"filename": img_path}
def facial_expression_analysis(subdict):
# Find (multiple) faces in the image and cut them
retinaface_model.get()
faces = RetinaFace.extract_faces(img_path)
faces = RetinaFace.extract_faces(subdict["filename"])
# If no faces are found, we return an empty dictionary
# If no faces are found, we return empty keys
if len(faces) == 0:
return result
subdict["face"] = None
subdict["wears_mask"] = None
subdict["age"] = None
subdict["gender"] = None
subdict["race"] = None
subdict["emotion"] = None
return subdict
# Sort the faces by sight to prioritize prominent faces
faces = list(reversed(sorted(faces, key=lambda f: f.shape[0] * f.shape[1])))
@ -120,9 +125,9 @@ def facial_expression_analysis(img_path):
# We limit ourselves to three faces
for i, face in enumerate(faces[:3]):
result[f"person{ i+1 }"] = analyze_single_face(face)
subdict[f"person{ i+1 }"] = analyze_single_face(face)
return result
return subdict
def wears_mask(face):

Просмотреть файл

@ -2,9 +2,10 @@ from google.cloud import vision
import io
def detect_text(path):
def detect_text(subdict):
"""Detects text in the file."""
path = subdict["filename"]
client = vision.ImageAnnotatorClient()
with io.open(path, "rb") as image_file:
@ -14,13 +15,13 @@ def detect_text(path):
response = client.text_detection(image=image)
texts = response.text_annotations
result = {"text": []}
subdict = {"text": []}
for text in texts:
result["text"].append(text.description)
subdict["text"].append(text.description)
if response.error.message:
raise Exception(
"{}\nFor more info on error messages, check: "
"https://cloud.google.com/apis/design/errors".format(response.error.message)
)
return result
return subdict

Просмотреть файл

@ -1,5 +1,6 @@
import glob
import os
from pandas import DataFrame
import pooch
@ -29,6 +30,17 @@ def misinformation_prefetch_models():
res.get()
class AnalysisMethod:
"""Base class to be inherited by all analysis methods."""
def __init__(self) -> None:
# define keys that will be set by the analysis
self.mykeys = ["filename"]
def analyse_image(self):
None
def find_files(path=None, pattern="*.png", recursive=True, limit=20):
"""Find image files on the file system
@ -55,3 +67,41 @@ def find_files(path=None, pattern="*.png", recursive=True, limit=20):
result = result[:limit]
return result
def initialize_dict(filelist: list) -> dict:
mydict = {}
for img_path in filelist:
id = img_path.split(".")[0].split("/")[-1]
mydict[id] = {"filename": img_path}
return mydict
def append_data_to_dict(mydict: dict) -> dict:
"""Append entries from list of dictionaries to keys in global dict."""
# first initialize empty list for each key that is present
outdict = {key: [] for key in list(mydict.values())[0].keys()}
# now append the values to each key in a list
for subdict in mydict.values():
for key in subdict.keys():
outdict[key].append(subdict[key])
# mydict = {key: [mydict[key] for mydict in dictlist] for key in dictlist[0]}
print(outdict)
return outdict
def dump_df(mydict: dict) -> DataFrame:
"""Utility to dump the dictionary into a dataframe."""
return DataFrame.from_dict(mydict)
if __name__ == "__main__":
files = find_files(
path="/home/inga/projects/misinformation-project/misinformation/data/test_no_text/"
)
mydict = initialize_dict(files)
outdict = {}
outdict = append_data_to_dict(mydict)
df = dump_df(outdict)
print(df.head(10))

Просмотреть файл

@ -41,7 +41,10 @@
"metadata": {},
"outputs": [],
"source": [
"images = misinformation.find_files(limit=1000)"
"images = misinformation.find_files(\n",
" path=\"/home/inga/projects/misinformation-project/misinformation/data/test_no_text\",\n",
" limit=1000,\n",
")"
]
},
{
@ -64,7 +67,25 @@
},
{
"cell_type": "markdown",
"id": "d8067ad1-ef8a-4e91-bcc6-f8dbef771854",
"id": "705e7328",
"metadata": {},
"source": [
"We need to initialize the main dictionary that contains all information for the images and is updated through each subsequent analysis:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b37c0c91",
"metadata": {},
"outputs": [],
"source": [
"mydict = misinformation.utils.initialize_dict(images)"
]
},
{
"cell_type": "markdown",
"id": "a9372561",
"metadata": {},
"source": [
"Next, we display the face recognition results provided by the DeepFace library. Click on the tabs to see the results in the right sidebar:"
@ -77,7 +98,7 @@
"metadata": {},
"outputs": [],
"source": [
"misinformation.explore_analysis(images, identify=\"faces\")"
"misinformation.explore_analysis(mydict, identify=\"faces\")"
]
},
{

Просмотреть файл

@ -64,6 +64,16 @@
" display(Image(filename=i))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8b32409f",
"metadata": {},
"outputs": [],
"source": [
"mydict = misinformation.utils.initialize_dict(mysubfiles)"
]
},
{
"cell_type": "markdown",
"id": "07b7a7a3",
@ -106,7 +116,7 @@
" \"\"\"Preprocess the image to enhance features for extraction.\"\"\"\n",
" image = cv2.imread(filename)\n",
" # preserve the original image\n",
" original = image.copy()\n",
" # original = image.copy()\n",
" # Grayscale, Gaussian blur, Otsu's threshold\n",
" gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
" # sharpen contrast by first smoothing and then substracting the smoothed and thresholded version\n",
@ -363,8 +373,7 @@
"os.environ[\n",
" \"GOOGLE_APPLICATION_CREDENTIALS\"\n",
"] = \"/home/inga/projects/misinformation-project/misinformation-notes/seismic-bonfire-329406-412821a70264.json\"\n",
"images = mysubfiles[1:5]\n",
"misinformation.explore_analysis(images, identify=\"text-on-image\")"
"misinformation.explore_analysis(mydict, identify=\"text-on-image\")"
]
},
{

11
requirements-dev.txt Обычный файл
Просмотреть файл

@ -0,0 +1,11 @@
deepface
ipywidgets==8.0.0rc1
pooch
retina-face
opencv-python
matplotlib
numpy
keras-ocr
tensorflow
google-cloud-vision
pytesseract

Просмотреть файл

@ -2,10 +2,4 @@ deepface
ipywidgets==8.0.0rc1
pooch
retina-face
opencv-python
matplotlib
numpy
keras-ocr
tensorflow
google-cloud-vision
pytesseract
google-cloud-vision