Skip to content

Commit

Permalink
Merge pull request #141 from ototadana/fix/criteria_match
Browse files Browse the repository at this point in the history
fix criteria match
  • Loading branch information
ototadana authored Jul 29, 2023
2 parents e65d383 + 0add0ec commit 94002bd
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 31 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,21 @@ In this project, the components used in the workflow are also referred to as "in
- `rules` (array or object, required): One or more rules to be applied.
- Each rule can be an object that consists of `when` and `then`:
- `when` (object, optional): The condition for the rule.
- `tag` (string, optional): A tag corresponding to the type of face detected by the face detector.
- `tag` (string, optional): A tag corresponding to the type of face detected by the face detector. This tag can optionally include a query following the tag name, separated by a '?'. This query is a complex condition that defines attribute-value comparisons using operators. The query can combine multiple comparisons using logical operators. For example, a tag could be "face?age<30&gender=M", which means that the tag name is "face" and the query is "age<30&gender=M". The query indicates that the rule should apply to faces that are identified as male and are less than 30 years old.
- The available operators are as follows:
- `=`: Checks if the attribute is equal to the value.
- `<`: Checks if the attribute is less than the value.
- `>`: Checks if the attribute is greater than the value.
- `<=`: Checks if the attribute is less than or equal to the value.
- `>=`: Checks if the attribute is greater than or equal to the value.
- `!=`: Checks if the attribute is not equal to the value.
- `~=`: Checks if the attribute value contains the value.
- `*=`: Checks if the attribute value starts with the value.
- `=*`: Checks if the attribute value ends with the value.
- `~*`: Checks if the attribute value does not contain the value.
- The logical operators are as follows:
- `&`: Represents logical AND.
- `|`: Represents logical OR.
- `criteria` (string, optional): This determines which faces will be processed, based on their position or size. Available options for position include 'left', 'right', 'center', 'top', 'middle', 'bottom'. For size, 'small', 'large' are available. The selection of faces to be processed that match the specified criteria can be defined in this string, following the pattern `{position/size}:{index range}`. The `{index range}` can be a single index, a range of indices, or a combination of these separated by a comma. For example, specifying left:0 will process the face that is located the most to the left on the screen. left:0-2 will process the three faces that are the most left, and left:0,2,5 will process the most left face, the third from the left, and the sixth from the left. If left is specified without an index or range, it will default to processing the face most left in the frame. Essentially, this is the same as specifying left:0.
- `then` (object or array of objects, required): The job or list of jobs to be executed if the `when` condition is met.
- Each job is an object with the following properties:
Expand Down
2 changes: 2 additions & 0 deletions scripts/inferencers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ This face processor applies a Gaussian blur to the detected face region. The int
#### Usage in Workflows
- [blur.json](../../workflows/examples/blur.json)
- [blur_non_center_faces.json](../../workflows/examples/blur_non_center_faces.json)
- [blur_young_people.json](../../workflows/examples/blur_young_people.json)

---

Expand Down Expand Up @@ -214,3 +215,4 @@ This option generates a "mask" that is simply an all-white image of the same siz

#### Usage in Workflows
- [blur_non_center_faces.json](../../workflows/examples/blur_non_center_faces.json)
- [blur_young_people.json](../../workflows/examples/blur_young_people.json)
5 changes: 5 additions & 0 deletions scripts/inferencers/insightface/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ A Face Detector implemented using [the InsightFace Detection module](https://git
- "age": The detected age of the face.
- landmarks: 5 (both eyes, the nose, and both ends of the mouth)

**Note**
Please be aware that the accuracy of gender and age detection with the InsightFace Detector is at a level where it can just about be used for experimental purposes.


#### Usage in Workflows
- [insightface.json](../../../workflows/examples/insightface.json)
- [blur_young_people.json](../../../workflows/examples/blur_young_people.json)

---

Expand Down
61 changes: 31 additions & 30 deletions scripts/use_cases/workflow_manager.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,18 @@ def detect_faces(self, image: Image, option: Option) -> List[Rect]:
return results

def select_rule(self, faces: List[Face], index: int, width: int, height: int) -> Rule:
if faces[index].face_area is None:
face = faces[index]
if face.face_area is None:
return None

for rule in self.workflow.rules:
if rule.when is None:
return rule

if self.__is_tag_match(rule.when, faces[index]) and self.__is_criteria_match(
rule.when, faces, index, width, height
):
return rule
if self.__is_tag_match(rule.when, face):
tag_matched_faces = [f for f in faces if self.__is_tag_match(rule.when, f)]
if self.__is_criteria_match(rule.when, tag_matched_faces, face, width, height):
return rule

return None

Expand All @@ -87,7 +88,7 @@ def __is_tag_match(self, condition: Condition, face: Face) -> bool:
return True
return query_matcher.evaluate(query, face.face_area.attributes)

def __is_criteria_match(self, condition: Condition, faces: List[Face], index: int, width: int, height: int) -> bool:
def __is_criteria_match(self, condition: Condition, faces: List[Face], face: Face, width: int, height: int) -> bool:
if not condition.has_criteria():
return True

Expand All @@ -98,54 +99,54 @@ def __is_criteria_match(self, condition: Condition, faces: List[Face], index: in
return True

if criteria in {"left", "leftmost"}:
return self.__is_left(indices, faces, index)
return self.__is_left(indices, faces, face)
if criteria in {"center", "center_horizontal", "middle_horizontal"}:
return self.__is_center(indices, faces, index, width)
return self.__is_center(indices, faces, face, width)
if criteria in {"right", "rightmost"}:
return self.__is_right(indices, faces, index)
return self.__is_right(indices, faces, face)
if criteria in {"top", "upper", "upmost"}:
return self.__is_top(indices, faces, index)
return self.__is_top(indices, faces, face)
if criteria in {"middle", "center_vertical", "middle_vertical"}:
return self.__is_middle(indices, faces, index, height)
return self.__is_middle(indices, faces, face, height)
if criteria in {"bottom", "lower", "downmost"}:
return self.__is_bottom(indices, faces, index)
return self.__is_bottom(indices, faces, face)
if criteria in {"small", "tiny", "smaller"}:
return self.__is_small(indices, faces, index)
return self.__is_small(indices, faces, face)
if criteria in {"large", "big", "bigger"}:
return self.__is_large(indices, faces, index)
return self.__is_large(indices, faces, face)
return False

def __is_left(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_left(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.left)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_center(self, indices: List[int], faces: List[Face], index: int, width: int) -> bool:
def __is_center(self, indices: List[int], faces: List[Face], face: Face, width: int) -> bool:
sorted_faces = sorted(faces, key=lambda f: abs((f.face_area.center - width / 2)))
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_right(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_right(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.right, reverse=True)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_top(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_top(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.top)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_middle(self, indices: List[int], faces: List[Face], index: int, height: int) -> bool:
def __is_middle(self, indices: List[int], faces: List[Face], face: Face, height: int) -> bool:
sorted_faces = sorted(faces, key=lambda f: abs(f.face_area.middle - height / 2))
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_bottom(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_bottom(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.bottom, reverse=True)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_small(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_small(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.size)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def __is_large(self, indices: List[int], faces: List[Face], index: int) -> bool:
def __is_large(self, indices: List[int], faces: List[Face], face: Face) -> bool:
sorted_faces = sorted(faces, key=lambda f: f.face_area.size, reverse=True)
return sorted_faces.index(faces[index]) in indices
return sorted_faces.index(face) in indices

def process(self, jobs: List[Job], face: Face, p: StableDiffusionProcessingImg2Img, option: Option) -> Image:
for job in jobs:
Expand Down
27 changes: 27 additions & 0 deletions workflows/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,30 @@ For faces located in the center of the image (as specified by the `"criteria": "
On the other hand, for faces not located in the center, the `Blur` face processor and `NoMask` mask generator are applied, effectively blurring these faces.

This workflow could be handy in situations where you want to emphasize the subject in the middle of the photo, or to anonymize faces in the background for privacy reasons.

---

### Example 7: Basic Workflow - InsightFace

This workflow utilizes the InsightFace face detector and applies the 'img2img' face processor and 'InsightFace' mask generator to all detected faces.

[View the workflow definition](insightface.json)

Please note that to use this workflow, the 'insightface' option under "Additional components" in the Face Editor section of the "Settings" tab needs to be enabled.

---

### Example 8: Blurring Young People's Faces

This workflow employs the InsightFace face detector and adjusts the processing of detected faces based on their ages.

[View the workflow definition](blur_young_people.json)

For faces that are under 30 years old (as specified by the `"tag": "face?age<30"` condition), the Blur face processor and NoMask mask generator are applied. This results in blurring the faces of younger individuals.

For all other faces (i.e., those aged 30 or over), the img2img face processor and BiSeNet mask generator are used. This leads to advanced masking and img2img transformations.

This workflow can be beneficial in scenarios where you need to blur the faces of young people for anonymization in image processing. It also serves as an example of applying different processing based on age.

Please note that the accuracy of age detection depends on the face detector's performance. There might be variations or inaccuracies in the detected age.

20 changes: 20 additions & 0 deletions workflows/examples/blur_young_people.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"face_detector": "InsightFace",
"rules": [
{
"when": {
"tag": "face?age<30"
},
"then": {
"face_processor": "Blur",
"mask_generator": "NoMask"
}
},
{
"then": {
"face_processor": "img2img",
"mask_generator": "BiSeNet"
}
}
]
}

0 comments on commit 94002bd

Please sign in to comment.