FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
tableizer/python/tableizer_ffi.py at main · uzbit/tableizer · GitHub
uzbit
/
tableizer
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
tableizer
/
python
/
tableizer_ffi.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
340 lines (282 loc) · 11.7 KB
Breadcrumbs
tableizer
/
python
/
tableizer_ffi.py
Copy path
File metadata and controls
340 lines (282 loc) · 11.7 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
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
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
"""
Python FFI bindings for the Tableizer C++ library.
Provides access to table detection and coordinate transformation functions.
"""
import
ctypes
from
ctypes
import
c_char_p
,
c_int
,
c_float
,
POINTER
,
c_uint8
import
json
import
numpy
as
np
import
platform
import
os
from
pathlib
import
Path
import
cv2
import
base64
class
TableizerFFI
:
"""Python wrapper for Tableizer C++ library FFI functions."""
def
__init__
(
self
,
library_path
=
None
):
"""
Initialize the FFI wrapper with the C++ library.
Parameters
----------
library_path : str, optional
Path to the compiled C++ library. If None, will try to find it automatically.
"""
self
.
lib
=
None
self
.
_load_library
(
library_path
)
self
.
_setup_functions
()
def
_load_library
(
self
,
library_path
=
None
):
"""Load the C++ library."""
if
library_path
is
None
:
# Try to find the library automatically
library_path
=
self
.
_find_library
()
if
not
os
.
path
.
exists
(
library_path
):
raise
FileNotFoundError
(
f"Tableizer library not found at:
{
library_path
}
"
)
try
:
self
.
lib
=
ctypes
.
CDLL
(
library_path
)
print
(
f"Loaded Tableizer library from:
{
library_path
}
"
)
except
Exception
as
e
:
raise
RuntimeError
(
f"Failed to load Tableizer library:
{
e
}
"
)
def
_find_library
(
self
):
"""Automatically find the compiled library based on platform."""
# Start from the python directory and look for the library
base_path
=
Path
(
__file__
).
parent
.
parent
if
platform
.
system
()
==
"Darwin"
:
# macOS
lib_patterns
=
[
"lib/build/libtableizer_lib.dylib"
,
"lib/build/libtableizer.dylib"
,
"lib/build/Debug/libtableizer_lib.dylib"
,
"lib/build/Debug/libtableizer.dylib"
,
"lib/build/Release/libtableizer_lib.dylib"
,
"lib/build/Release/libtableizer.dylib"
,
"build/libtableizer_lib.dylib"
,
"build/libtableizer.dylib"
,
]
elif
platform
.
system
()
==
"Linux"
:
lib_patterns
=
[
"lib/build/libtableizer.so"
,
"lib/build/Debug/libtableizer.so"
,
"lib/build/Release/libtableizer.so"
,
"build/libtableizer.so"
,
]
elif
platform
.
system
()
==
"Windows"
:
lib_patterns
=
[
"lib/build/tableizer.dll"
,
"lib/build/Debug/tableizer.dll"
,
"lib/build/Release/tableizer.dll"
,
"build/tableizer.dll"
,
]
else
:
raise
RuntimeError
(
f"Unsupported platform:
{
platform
.
system
()
}
"
)
for
pattern
in
lib_patterns
:
lib_path
=
base_path
/
pattern
if
lib_path
.
exists
():
return
str
(
lib_path
)
raise
FileNotFoundError
(
f"Could not find Tableizer library. Searched patterns:
{
lib_patterns
}
"
)
def
_setup_functions
(
self
):
"""Setup function signatures for the C++ FFI functions."""
# Table detection function (BGRA version)
self
.
lib
.
detect_table_bgra
.
argtypes
=
[
POINTER
(
c_uint8
),
# image_bytes
c_int
,
# width
c_int
,
# height
c_int
,
# stride
c_int
,
# channel_format (0=BGRA, 1=RGBA)
]
self
.
lib
.
detect_table_bgra
.
restype
=
c_char_p
# Coordinate transformation function
self
.
lib
.
transform_points_using_quad
.
argtypes
=
[
POINTER
(
c_float
),
# points_data
c_int
,
# points_count
POINTER
(
c_float
),
# quad_data
c_int
,
# quad_count
c_int
,
# image_width
c_int
,
# image_height
c_int
,
# display_width
c_int
,
# display_height
c_int
,
# input_rotation_degrees
]
self
.
lib
.
transform_points_using_quad
.
restype
=
c_char_p
def
detect_table
(
self
,
image
,
rotation_degrees
=
0
,
debug_path
=
None
):
"""
Detect table quad points in an image using C++ implementation.
Parameters
----------
image : np.ndarray
Input image in BGR format
rotation_degrees : int
Rotation in degrees (0, 90, 180, 270) - not used in RGBA version
debug_path : str, optional
Path to save debug image - not used in RGBA version
Returns
-------
dict or None
Dictionary containing:
- 'quad_points': List of 4 quad points as [[x, y], [x, y], ...]
- 'mask': numpy.ndarray (grayscale) if available, None otherwise
- 'image': base64 encoded debug image
Returns None if detection failed
"""
if
len
(
image
.
shape
)
!=
3
or
image
.
shape
[
2
]
!=
3
:
raise
ValueError
(
"Image must be a 3-channel BGR image"
)
# Convert BGR to BGRA for the BGRA function
bgra_image
=
cv2
.
cvtColor
(
image
,
cv2
.
COLOR_BGR2BGRA
)
height
,
width
=
bgra_image
.
shape
[:
2
]
stride
=
bgra_image
.
strides
[
0
]
# Use OpenCV stride
# Convert to ctypes array
image_data
=
bgra_image
.
astype
(
np
.
uint8
)
image_ptr
=
image_data
.
ctypes
.
data_as
(
POINTER
(
c_uint8
))
try
:
# Call C++ BGRA function
# Channel format: 0=BGRA (OpenCV default), 1=RGBA
result_ptr
=
self
.
lib
.
detect_table_bgra
(
image_ptr
,
c_int
(
width
),
c_int
(
height
),
c_int
(
stride
),
c_int
(
0
),
# channel_format: 0=BGRA (OpenCV uses BGRA by default)
)
if
not
result_ptr
:
return
None
# Convert result to Python string
result_json
=
result_ptr
.
decode
(
"utf-8"
)
result_data
=
json
.
loads
(
result_json
)
if
"error"
in
result_data
:
print
(
f"Table detection error:
{
result_data
[
'error'
]
}
"
)
return
None
# Process mask data if present
if
"mask"
in
result_data
:
mask_data
=
result_data
[
"mask"
]
try
:
# Decode base64 mask back to numpy array
mask_bytes
=
base64
.
b64decode
(
mask_data
[
"data"
])
mask_array
=
cv2
.
imdecode
(
np
.
frombuffer
(
mask_bytes
,
np
.
uint8
),
cv2
.
IMREAD_GRAYSCALE
)
result_data
[
"mask"
]
=
mask_array
except
Exception
as
e
:
print
(
f"Warning: Failed to decode mask data:
{
e
}
"
)
result_data
[
"mask"
]
=
None
return
result_data
except
Exception
as
e
:
print
(
f"Error in table detection:
{
e
}
"
)
return
None
def
transform_points
(
self
,
points
,
quad_points
,
image_size
,
display_size
,
rotation_degrees
=
0
):
"""
Transform points using quad-to-rectangle perspective transformation.
Parameters
----------
points : list or np.ndarray
List of points to transform, each as [x, y] or (x, y)
quad_points : list
List of 4 quad points as [x, y] pairs
image_size : tuple
(width, height) of the source image
display_size : tuple
(width, height) of the destination display
rotation_degrees : int, optional
Rotation in degrees (0, 90, 180, 270), default 0
Returns
-------
list or None
List of transformed points as [{"x": float, "y": float}, ...] or None if failed
"""
if
len
(
quad_points
)
!=
4
:
raise
ValueError
(
"quad_points must contain exactly 4 points"
)
if
len
(
points
)
==
0
:
return
[]
# Convert points to flat array [x1, y1, x2, y2, ...]
points_array
=
np
.
array
(
points
,
dtype
=
np
.
float32
).
flatten
()
if
len
(
points_array
)
%
2
!=
0
:
raise
ValueError
(
"Points must be pairs of (x, y) coordinates"
)
points_count
=
len
(
points_array
)
//
2
# Convert quad points to flat array [x1, y1, x2, y2, x3, y3, x4, y4]
quad_array
=
np
.
array
(
quad_points
,
dtype
=
np
.
float32
).
flatten
()
if
len
(
quad_array
)
!=
8
:
raise
ValueError
(
"quad_points must contain exactly 4 (x, y) pairs"
)
# Create ctypes pointers
points_ptr
=
points_array
.
ctypes
.
data_as
(
POINTER
(
c_float
))
quad_ptr
=
quad_array
.
ctypes
.
data_as
(
POINTER
(
c_float
))
try
:
# Call C++ function
result_ptr
=
self
.
lib
.
transform_points_using_quad
(
points_ptr
,
c_int
(
points_count
),
quad_ptr
,
c_int
(
4
),
c_int
(
image_size
[
0
]),
# width
c_int
(
image_size
[
1
]),
# height
c_int
(
display_size
[
0
]),
# width
c_int
(
display_size
[
1
]),
# height
c_int
(
rotation_degrees
),
# input_rotation_degrees
)
if
not
result_ptr
:
return
None
# Convert result to Python string
result_json
=
result_ptr
.
decode
(
"utf-8"
)
result_data
=
json
.
loads
(
result_json
)
if
"error"
in
result_data
:
print
(
f"Transform points error:
{
result_data
[
'error'
]
}
"
)
return
None
return
result_data
.
get
(
"transformed_points"
, [])
except
Exception
as
e
:
print
(
f"Error in point transformation:
{
e
}
"
)
return
None
# Global instance for easy access
_tableizer_ffi
=
None
def
get_tableizer_ffi
():
"""Get the global TableizerFFI instance, creating it if necessary."""
global
_tableizer_ffi
if
_tableizer_ffi
is
None
:
_tableizer_ffi
=
TableizerFFI
()
return
_tableizer_ffi
# Convenience functions
def
detect_table_cpp
(
image
,
rotation_degrees
=
0
,
debug_path
=
None
):
"""
Convenience function for table detection using C++ implementation.
Returns
-------
dict or None
Dictionary containing:
- 'quad_points': List of 4 corner points as [[x, y], [x, y], ...]
- 'mask': numpy.ndarray (grayscale mask) if available, None otherwise
- 'image': base64 encoded debug image
Returns None if detection failed
"""
ffi
=
get_tableizer_ffi
()
return
ffi
.
detect_table
(
image
,
rotation_degrees
,
debug_path
)
def
transform_points_cpp
(
points
,
quad_points
,
image_size
,
display_size
,
rotation_degrees
=
0
):
"""Convenience function for point transformation using C++ implementation."""
ffi
=
get_tableizer_ffi
()
return
ffi
.
transform_points
(
points
,
quad_points
,
image_size
,
display_size
,
rotation_degrees
)
if
__name__
==
"__main__"
:
# Test the FFI bindings
print
(
"Testing Tableizer FFI bindings..."
)
try
:
ffi
=
TableizerFFI
()
print
(
"✅ FFI initialization successful"
)
# Test with a simple image
test_image
=
np
.
zeros
((
480
,
640
,
3
),
dtype
=
np
.
uint8
)
test_image
[
100
:
380
,
160
:
480
]
=
[
0
,
128
,
0
]
# Green rectangle
quad_points
=
ffi
.
detect_table
(
test_image
)
if
quad_points
:
print
(
f"✅ Table detection successful:
{
len
(
quad_points
)
}
points found"
)
# Test point transformation
test_points
=
[[
320
,
240
], [
300
,
200
]]
# Center and offset points
transformed
=
ffi
.
transform_points
(
test_points
,
[(
pt
[
"x"
],
pt
[
"y"
])
for
pt
in
quad_points
],
(
640
,
480
),
(
400
,
600
),
)
if
transformed
:
print
(
f"✅ Point transformation successful:
{
len
(
transformed
)
}
points transformed"
)
else
:
print
(
"❌ Point transformation failed"
)
else
:
print
(
"❌ Table detection failed"
)
except
Exception
as
e
:
print
(
f"❌ FFI test failed:
{
e
}
"
)
Back
|
FazBrowse Home
|
New Git URL