Skip to content

Presidio Image Redactor API Reference

ImageRedactorEngine class

ImageRedactorEngine performs OCR + PII detection + bounding box redaction.

Parameters:

Name Type Description Default
image_analyzer_engine ImageAnalyzerEngine

Engine which performs OCR + PII detection.

None
Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_redactor_engine.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class ImageRedactorEngine:
    """ImageRedactorEngine performs OCR + PII detection + bounding box redaction.

    :param image_analyzer_engine: Engine which performs OCR + PII detection.
    """

    def __init__(self, image_analyzer_engine: ImageAnalyzerEngine = None):
        if not image_analyzer_engine:
            self.image_analyzer_engine = ImageAnalyzerEngine()
        else:
            self.image_analyzer_engine = image_analyzer_engine

        self.bbox_processor = BboxProcessor()

    def redact(
        self,
        image: Image,
        fill: Union[int, Tuple[int, int, int]] = (0, 0, 0),
        ocr_kwargs: Optional[dict] = None,
        **text_analyzer_kwargs,
    ) -> Image:
        """Redact method to redact the given image.

        Please notice, this method duplicates the image, creates a new instance and
        manipulate it.
        :param image: PIL Image to be processed.
        :param fill: colour to fill the shape - int (0-255) for
        grayscale or Tuple(R, G, B) for RGB.
        :param ocr_kwargs: Additional params for OCR methods.
        :param text_analyzer_kwargs: Additional values for the analyze method
        in AnalyzerEngine.

        :return: the redacted image
        """

        image = ImageChops.duplicate(image)

        bboxes = self.image_analyzer_engine.analyze(
            image, ocr_kwargs, **text_analyzer_kwargs
        )
        draw = ImageDraw.Draw(image)

        for box in bboxes:
            x0 = box.left
            y0 = box.top
            x1 = x0 + box.width
            y1 = y0 + box.height
            draw.rectangle([x0, y0, x1, y1], fill=fill)

        return image

redact(image, fill=(0, 0, 0), ocr_kwargs=None, **text_analyzer_kwargs)

Redact method to redact the given image.

Please notice, this method duplicates the image, creates a new instance and manipulate it.

Parameters:

Name Type Description Default
image Image

PIL Image to be processed.

required
fill Union[int, Tuple[int, int, int]]

colour to fill the shape - int (0-255) for grayscale or Tuple(R, G, B) for RGB.

(0, 0, 0)
ocr_kwargs Optional[dict]

Additional params for OCR methods.

None
text_analyzer_kwargs

Additional values for the analyze method in AnalyzerEngine.

{}

Returns:

Type Description
Image

the redacted image

Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_redactor_engine.py
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def redact(
    self,
    image: Image,
    fill: Union[int, Tuple[int, int, int]] = (0, 0, 0),
    ocr_kwargs: Optional[dict] = None,
    **text_analyzer_kwargs,
) -> Image:
    """Redact method to redact the given image.

    Please notice, this method duplicates the image, creates a new instance and
    manipulate it.
    :param image: PIL Image to be processed.
    :param fill: colour to fill the shape - int (0-255) for
    grayscale or Tuple(R, G, B) for RGB.
    :param ocr_kwargs: Additional params for OCR methods.
    :param text_analyzer_kwargs: Additional values for the analyze method
    in AnalyzerEngine.

    :return: the redacted image
    """

    image = ImageChops.duplicate(image)

    bboxes = self.image_analyzer_engine.analyze(
        image, ocr_kwargs, **text_analyzer_kwargs
    )
    draw = ImageDraw.Draw(image)

    for box in bboxes:
        x0 = box.left
        y0 = box.top
        x1 = x0 + box.width
        y1 = y0 + box.height
        draw.rectangle([x0, y0, x1, y1], fill=fill)

    return image

ImageAnalyzerEngine class

ImageAnalyzerEngine class.

Parameters:

Name Type Description Default
analyzer_engine Optional[AnalyzerEngine]

The Presidio AnalyzerEngine instance to be used to detect PII in text

None
ocr Optional[OCR]

the OCR object to be used to detect text in images.

None
Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_analyzer_engine.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
class ImageAnalyzerEngine:
    """ImageAnalyzerEngine class.

    :param analyzer_engine: The Presidio AnalyzerEngine instance
        to be used to detect PII in text
    :param ocr: the OCR object to be used to detect text in images.
    """

    def __init__(
        self,
        analyzer_engine: Optional[AnalyzerEngine] = None,
        ocr: Optional[OCR] = None,
    ):
        if not analyzer_engine:
            analyzer_engine = AnalyzerEngine()
        self.analyzer_engine = analyzer_engine

        if not ocr:
            ocr = TesseractOCR()
        self.ocr = ocr

    def analyze(
        self, image: object, ocr_kwargs: Optional[dict] = None, **text_analyzer_kwargs
    ) -> List[ImageRecognizerResult]:
        """Analyse method to analyse the given image.

        :param image: PIL Image/numpy array or file path(str) to be processed.
        :param ocr_kwargs: Additional params for OCR methods.
        :param text_analyzer_kwargs: Additional values for the analyze method
        in AnalyzerEngine.

        :return: List of the extract entities with image bounding boxes.
        """
        # Perform OCR
        perform_ocr_kwargs, ocr_threshold = self._parse_ocr_kwargs(ocr_kwargs)
        ocr_result = self.ocr.perform_ocr(image, **perform_ocr_kwargs)

        # Apply OCR confidence threshold if it is passed in
        if ocr_threshold:
            ocr_result = self.threshold_ocr_result(ocr_result, ocr_threshold)

        # Analyze text
        text = self.ocr.get_text_from_ocr_dict(ocr_result)

        analyzer_result = self.analyzer_engine.analyze(
            text=text, language="en", **text_analyzer_kwargs
        )
        bboxes = self.map_analyzer_results_to_bounding_boxes(
            analyzer_result, ocr_result, text
        )
        return bboxes

    @staticmethod
    def threshold_ocr_result(ocr_result: dict, ocr_threshold: float) -> dict:
        """Filter out OCR results below confidence threshold.

        :param ocr_result: OCR results (raw).
        :param ocr_threshold: Threshold value between -1 and 100.

        :return: OCR results with low confidence items removed.
        """
        if ocr_threshold < -1 or ocr_threshold > 100:
            raise ValueError("ocr_threshold must be between -1 and 100")

        # Get indices of items above threshold
        idx = list()
        for i, val in enumerate(ocr_result["conf"]):
            if float(val) >= ocr_threshold:
                idx.append(i)

        # Only retain high confidence items
        filtered_ocr_result = {}
        for key in list(ocr_result.keys()):
            filtered_ocr_result[key] = [ocr_result[key][i] for i in idx]

        return filtered_ocr_result

    @staticmethod
    def map_analyzer_results_to_bounding_boxes(
        text_analyzer_results: List[RecognizerResult], ocr_result: dict, text: str
    ) -> List[ImageRecognizerResult]:
        """Map extracted PII entities to image bounding boxes.

        Matching is based on the position of the recognized entity from analyzer
        and word (in ocr dict) in the text.

        :param text_analyzer_results: PII entities recognized by presidio analyzer
        :param ocr_result: dict results with words and bboxes from OCR
        :param text: text the results are based on

        return: list of extracted entities with image bounding boxes
        """
        if (not ocr_result) or (not text_analyzer_results):
            return []

        bboxes = []
        proc_indexes = 0
        indexes = len(text_analyzer_results)

        pos = 0
        iter_ocr = enumerate(ocr_result["text"])
        for index, word in iter_ocr:
            if not word:
                pos += 1
            else:
                for element in text_analyzer_results:
                    text_element = text[element.start : element.end]
                    # check position and text of ocr word matches recognized entity
                    if (
                        max(pos, element.start) < min(element.end, pos + len(word))
                    ) and ((text_element in word) or (word in text_element)):
                        bboxes.append(
                            ImageRecognizerResult(
                                element.entity_type,
                                element.start,
                                element.end,
                                element.score,
                                ocr_result["left"][index],
                                ocr_result["top"][index],
                                ocr_result["width"][index],
                                ocr_result["height"][index],
                            )
                        )

                        # add bounding boxes for all words in ocr dict
                        # contained within the text of recognized entity
                        # based on relative position in the full text
                        while pos + len(word) < element.end:
                            prev_word = word
                            index, word = next(iter_ocr)
                            if word:
                                bboxes.append(
                                    ImageRecognizerResult(
                                        element.entity_type,
                                        element.start,
                                        element.end,
                                        element.score,
                                        ocr_result["left"][index],
                                        ocr_result["top"][index],
                                        ocr_result["width"][index],
                                        ocr_result["height"][index],
                                    )
                                )
                            pos += len(prev_word) + 1
                        proc_indexes += 1

                if proc_indexes == indexes:
                    break
                pos += len(word) + 1

        return bboxes

    @staticmethod
    def _parse_ocr_kwargs(ocr_kwargs: dict) -> Tuple[dict, float]:
        """Parse the OCR-related kwargs.

        :param ocr_kwargs: Parameters for OCR operations.

        :return: Params for ocr.perform_ocr and ocr_threshold
        """
        ocr_threshold = None
        if ocr_kwargs is not None:
            if "ocr_threshold" in ocr_kwargs:
                ocr_threshold = ocr_kwargs["ocr_threshold"]
                ocr_kwargs = {
                    key: value
                    for key, value in ocr_kwargs.items()
                    if key != "ocr_threshold"
                }
        else:
            ocr_kwargs = {}

        return ocr_kwargs, ocr_threshold

analyze(image, ocr_kwargs=None, **text_analyzer_kwargs)

Analyse method to analyse the given image.

Parameters:

Name Type Description Default
image object

PIL Image/numpy array or file path(str) to be processed.

required
ocr_kwargs Optional[dict]

Additional params for OCR methods.

None
text_analyzer_kwargs

Additional values for the analyze method in AnalyzerEngine.

{}

Returns:

Type Description
List[ImageRecognizerResult]

List of the extract entities with image bounding boxes.

Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_analyzer_engine.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def analyze(
    self, image: object, ocr_kwargs: Optional[dict] = None, **text_analyzer_kwargs
) -> List[ImageRecognizerResult]:
    """Analyse method to analyse the given image.

    :param image: PIL Image/numpy array or file path(str) to be processed.
    :param ocr_kwargs: Additional params for OCR methods.
    :param text_analyzer_kwargs: Additional values for the analyze method
    in AnalyzerEngine.

    :return: List of the extract entities with image bounding boxes.
    """
    # Perform OCR
    perform_ocr_kwargs, ocr_threshold = self._parse_ocr_kwargs(ocr_kwargs)
    ocr_result = self.ocr.perform_ocr(image, **perform_ocr_kwargs)

    # Apply OCR confidence threshold if it is passed in
    if ocr_threshold:
        ocr_result = self.threshold_ocr_result(ocr_result, ocr_threshold)

    # Analyze text
    text = self.ocr.get_text_from_ocr_dict(ocr_result)

    analyzer_result = self.analyzer_engine.analyze(
        text=text, language="en", **text_analyzer_kwargs
    )
    bboxes = self.map_analyzer_results_to_bounding_boxes(
        analyzer_result, ocr_result, text
    )
    return bboxes

map_analyzer_results_to_bounding_boxes(text_analyzer_results, ocr_result, text) staticmethod

Map extracted PII entities to image bounding boxes.

Matching is based on the position of the recognized entity from analyzer and word (in ocr dict) in the text.

Parameters:

Name Type Description Default
text_analyzer_results List[RecognizerResult]

PII entities recognized by presidio analyzer

required
ocr_result dict

dict results with words and bboxes from OCR

required
text str

text the results are based on return: list of extracted entities with image bounding boxes

required
Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_analyzer_engine.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@staticmethod
def map_analyzer_results_to_bounding_boxes(
    text_analyzer_results: List[RecognizerResult], ocr_result: dict, text: str
) -> List[ImageRecognizerResult]:
    """Map extracted PII entities to image bounding boxes.

    Matching is based on the position of the recognized entity from analyzer
    and word (in ocr dict) in the text.

    :param text_analyzer_results: PII entities recognized by presidio analyzer
    :param ocr_result: dict results with words and bboxes from OCR
    :param text: text the results are based on

    return: list of extracted entities with image bounding boxes
    """
    if (not ocr_result) or (not text_analyzer_results):
        return []

    bboxes = []
    proc_indexes = 0
    indexes = len(text_analyzer_results)

    pos = 0
    iter_ocr = enumerate(ocr_result["text"])
    for index, word in iter_ocr:
        if not word:
            pos += 1
        else:
            for element in text_analyzer_results:
                text_element = text[element.start : element.end]
                # check position and text of ocr word matches recognized entity
                if (
                    max(pos, element.start) < min(element.end, pos + len(word))
                ) and ((text_element in word) or (word in text_element)):
                    bboxes.append(
                        ImageRecognizerResult(
                            element.entity_type,
                            element.start,
                            element.end,
                            element.score,
                            ocr_result["left"][index],
                            ocr_result["top"][index],
                            ocr_result["width"][index],
                            ocr_result["height"][index],
                        )
                    )

                    # add bounding boxes for all words in ocr dict
                    # contained within the text of recognized entity
                    # based on relative position in the full text
                    while pos + len(word) < element.end:
                        prev_word = word
                        index, word = next(iter_ocr)
                        if word:
                            bboxes.append(
                                ImageRecognizerResult(
                                    element.entity_type,
                                    element.start,
                                    element.end,
                                    element.score,
                                    ocr_result["left"][index],
                                    ocr_result["top"][index],
                                    ocr_result["width"][index],
                                    ocr_result["height"][index],
                                )
                            )
                        pos += len(prev_word) + 1
                    proc_indexes += 1

            if proc_indexes == indexes:
                break
            pos += len(word) + 1

    return bboxes

threshold_ocr_result(ocr_result, ocr_threshold) staticmethod

Filter out OCR results below confidence threshold.

Parameters:

Name Type Description Default
ocr_result dict

OCR results (raw).

required
ocr_threshold float

Threshold value between -1 and 100.

required

Returns:

Type Description
dict

OCR results with low confidence items removed.

Source code in /opt/hostedtoolcache/Python/3.10.11/x64/lib/python3.10/site-packages/presidio_image_redactor/image_analyzer_engine.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
@staticmethod
def threshold_ocr_result(ocr_result: dict, ocr_threshold: float) -> dict:
    """Filter out OCR results below confidence threshold.

    :param ocr_result: OCR results (raw).
    :param ocr_threshold: Threshold value between -1 and 100.

    :return: OCR results with low confidence items removed.
    """
    if ocr_threshold < -1 or ocr_threshold > 100:
        raise ValueError("ocr_threshold must be between -1 and 100")

    # Get indices of items above threshold
    idx = list()
    for i, val in enumerate(ocr_result["conf"]):
        if float(val) >= ocr_threshold:
            idx.append(i)

    # Only retain high confidence items
    filtered_ocr_result = {}
    for key in list(ocr_result.keys()):
        filtered_ocr_result[key] = [ocr_result[key][i] for i in idx]

    return filtered_ocr_result