alex-dinh commited on
Commit
7952bce
·
verified ·
1 Parent(s): 9f0763c

Create batched_inference_example.py

Browse files
Files changed (1) hide show
  1. batched_inference_example.py +152 -0
batched_inference_example.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Install dependencies:
2
+ # pip install numpy opencv-python onnxruntime
3
+
4
+ import numpy as np
5
+ import cv2
6
+ import onnxruntime as ort
7
+ from pathlib import Path
8
+
9
+ def preprocess_image_doclayout(image, target_input_size=(800, 800)):
10
+ """
11
+ Preprocessing for DocLayoutV3 with 800x800 input.
12
+ Returns CHW tensor (no batch dim) + scale factors.
13
+ """
14
+ orig_h, orig_w = image.shape[:2]
15
+ target_h, target_w = target_input_size
16
+ scale_h = target_h / orig_h
17
+ scale_w = target_w / orig_w
18
+
19
+ resized = cv2.resize(image, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
20
+ rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)
21
+ blob = rgb.astype(np.float32) / 255.0
22
+
23
+ mean = np.array([0.485, 0.456, 0.406], dtype=np.float32)
24
+ std = np.array([0.229, 0.224, 0.225], dtype=np.float32)
25
+ blob = (blob - mean) / std
26
+
27
+ # CHW — no batch dim yet; caller stacks the batch
28
+ blob = blob.transpose(2, 0, 1)
29
+ return blob, scale_h, scale_w
30
+
31
+
32
+ def preprocess_batch(image_paths, target_input_size=(800, 800)):
33
+ """
34
+ Load and preprocess a list of image paths.
35
+ Returns:
36
+ input_blob : (N, 3, H, W) float32
37
+ shape_list : (N, 2) float32 [[H, W], ...]
38
+ scale_list : (N, 2) float32 [[scale_h, scale_w], ...]
39
+ images : list of original BGR images (for debug / visualisation)
40
+ """
41
+ blobs, shapes, scales, images = [], [], [], []
42
+
43
+ for path in image_paths:
44
+ img = cv2.imread(str(path))
45
+ if img is None:
46
+ raise FileNotFoundError(f"Could not read image: {path}")
47
+
48
+ blob, scale_h, scale_w = preprocess_image_doclayout(img, target_input_size)
49
+ blobs.append(blob)
50
+ shapes.append(target_input_size) # (H, W)
51
+ scales.append((scale_h, scale_w))
52
+ images.append(img)
53
+
54
+ input_blob = np.stack(blobs, axis=0).astype(np.float32) # (N, 3, H, W)
55
+ shape_arr = np.array(shapes, dtype=np.float32) # (N, 2)
56
+ scale_arr = np.array(scales, dtype=np.float32) # (N, 2)
57
+
58
+ return input_blob, shape_arr, scale_arr, images
59
+
60
+
61
+ def run_doclayout_onnx_batch(image_paths, model_path, conf_thresh=0.5):
62
+ """
63
+ Run DocLayoutV3 on a batch of images.
64
+
65
+ The model's three inputs are:
66
+ input_names[0] : image shape – expected shape (N, 2)
67
+ input_names[1] : image tensor – expected shape (N, 3, H, W)
68
+ input_names[2] : scale factors – expected shape (N, 2)
69
+
70
+ Output shape: (N * max_dets, 7)
71
+ Values: [image_index, label_index, score, xmin, ymin, xmax, ymax]
72
+ (Some ONNX exports omit image_index — see note in post-processing.)
73
+ """
74
+ model = ort.InferenceSession(model_path)
75
+ input_names = [i.name for i in model.get_inputs()]
76
+ output_names = [o.name for o in model.get_outputs()]
77
+
78
+ input_blob, shape_arr, scale_arr, images = preprocess_batch(image_paths)
79
+ n = len(image_paths)
80
+
81
+ input_feed = {
82
+ "im_shape": shape_arr, # (N, 2)
83
+ "image": input_blob, # (N, 3, 800, 800)
84
+ "scale_factor": scale_arr, # (N, 2)
85
+ }
86
+
87
+ raw_output = model.run(output_names, input_feed)[0] # (N*dets, 7) or (N*dets, 6)
88
+
89
+ return postprocess_batch(raw_output, n, conf_thresh)
90
+
91
+
92
+ def postprocess_batch(raw_output, n_images, conf_thresh=0.5):
93
+ """
94
+ Split flat detection output back into per-image results.
95
+
96
+ PP-DocLayout ONNX output columns:
97
+ [img_idx, label, score, x0, y0, x1, y1, read_order] (8 cols)
98
+ or
99
+ [label, score, x0, y0, x1, y1, read_order] (7 cols — single-image compat)
100
+
101
+ We handle both layouts automatically.
102
+ """
103
+ n_cols = raw_output.shape[1]
104
+
105
+ if n_cols == 8:
106
+ # Batched export: first column is the image index
107
+ img_idx_col = raw_output[:, 0].astype(int)
108
+ detections = raw_output[:, 1:] # drop img_idx → 7 cols
109
+ else:
110
+ # Single-image export used for a batch: distribute evenly
111
+ dets_per_image = len(raw_output) // n_images
112
+ img_idx_col = np.repeat(np.arange(n_images), dets_per_image)
113
+ detections = raw_output
114
+
115
+ results = []
116
+ for i in range(n_images):
117
+ mask = img_idx_col == i
118
+ boxes = detections[mask]
119
+ boxes = boxes[boxes[:, 1] > conf_thresh] # confidence filter
120
+ boxes = boxes[np.argsort(boxes[:, 6])] # sort by read_order
121
+ results.append(boxes)
122
+
123
+ return results
124
+
125
+
126
+ def print_doclayout_res(boxes, image_label=""):
127
+ header = f"--- {image_label} ---" if image_label else "--- Results ---"
128
+ print(header)
129
+ print("cls_id\tscore\txmin\tymin\txmax\tymax\tread_order")
130
+ for box in boxes:
131
+ print(
132
+ f"{box[0]:.0f}\t\t{box[1]:.3f}\t"
133
+ f"{box[2]:.2f}\t{box[3]:.2f}\t"
134
+ f"{box[4]:.2f}\t{box[5]:.2f}\t{box[6]:.0f}"
135
+ )
136
+
137
+
138
+ if __name__ == '__main__':
139
+ MODEL_PATH = "your/path/to/PP-DocLayoutV3.onnx"
140
+
141
+ image_paths = [
142
+ "your_test_image_1.png",
143
+ "your_test_image_2.png",
144
+ "your_test_image_3.png",
145
+ "your_test_image_4.png",
146
+ ]
147
+
148
+ results = run_doclayout_onnx_batch(image_paths, MODEL_PATH, conf_thresh=0.5)
149
+
150
+ for path, boxes in zip(image_paths, results):
151
+ print_doclayout_res(boxes, image_label=Path(path).name)
152
+ print()