IC-Imaging-Control-Samples
IC-Imaging-Control-Samples copied to clipboard
IC3D SDK example for Python?
Is there a python example for IC3DSDK? I am using IC 3D to calibrate a pair of Imaging Source camera. The calibration results are great and I have generated the calibration xml file. Now I want to use these files in python to get XYZ cordinates and disparity maps from left and right images. currently, I am not looking into a live stream solution rather just a offline example where I can load left and right images and pass it the IC3D sdk and return the relevant XYZ coordinates and disparity maps. I see there is a dll file and a ic3d.h file. Any way I could use them to import the functions to python, similar to IC capture sdk?
Any help on this would be much appreciated. Thank you.
Hello
I am very sorry, but there is no Python sample for the IC3DSK. I think, you can import the libic3d.dll with the Python ctypes module. The download of the IC 3D SDK is at https://www.theimagingsource.com/support/downloads-for-windows/software-development-kits-sdks/ic3dsdk/ You must create the structs passed to and received from the functions of the DLL in Python, similar as I did for the tisgrabber DLL at https://github.com/TheImagingSource/IC-Imaging-Control-Samples/tree/master/Python/tisgrabber.
I hope, this helps a little bit. The IC 3D SDK is available for download, but we do not support or develope it anymore.
Stefan
Thank you for your help. I have tried as you mentioned about the ctypes, however the sdk return error while reading the xml file. I have tried several ways to reformat the way I input the file path the error persists. There's also no source code for the .dll that I can take a look into to debug. If you could help with loading the files and data format part, that would be very helpful.
Thank you again for your help.
import ctypes, os
from enum import Enum
abs_path_to_directory = os.path.abspath(os.path.dirname(__file__))
# add the directory to the os PATH
print(abs_path_to_directory)
os.environ['PATH'] = abs_path_to_directory + os.pathsep + os.environ['PATH']
ic3d = ctypes.cdll.LoadLibrary("libic3d.dll")
class ic3d_status(Enum):
IC3D_SUCCESS = 0
IC3D_FILE_TO_ERROR = 1
IC3D_MEMORY_ALLOCATION_ERROR = 2
IC3D_INVALID_POINTER_ERROR = 3
IC3D_MATCHER_INIT_ERROR = 4
IC3D_INPUT_DIMENSION_ERROR = 5
IC3D_INPUT_FORMAT_ERROR = 6
IC3D_MATCHER_NOT_VALID = 7
IC3D_ERROR = 8
class ic3d_image_format(Enum):
IC3D_IMG_INVALID_FORMAT = 0
IC3D_IMG_Y800 = 1 # An single channel (8-bit) grayscale image
IC3D_IMG_RGB32 = 2 # A four channel RGBA image (8-bit per channel)
IC3D_IMG_XYZA32 = 3 # A four channel floating point image (32-bit per channel)
class ic3d_matcher_type(Enum):
IC3D_CPU_MATCHER = 0
IC3D_CUDA_MATCHER = 1
class ic3d_origin_type(Enum):
IC3D_ORIGIN_CALIB = 0
IC3D_ORIGIN_LEFT_CAM = 1
IC3D_ORIGIN_RIGHT_CAM = 2
class ic3d_image:
def __init__(self, img, img_format):
self.data = ctypes.c_ubyte(img.data)
self.img_format = ctypes.c_int(img_format)
self.width = ctypes.c_intimg.shape[0]
self.height = ctypes.c_intimg.shape[1]
img1 =cv2.imread('cam0_3.bmp', 0)
img2 =cv2.imread('cam1_3.bmp', 0)
c = ctypes.POINTER(ctypes.POINTER(context))
status = ic3d.ic3d_init_context()
print(ic3d_status(status.value))
ic3d.ic3d_load_calibration.argtype = ctypes.c_char_p
# this part doesn't work
status = ic3d.ic3d_load_calibration(b"C:\\Users\\ASUS\Desktop\\3dCalib\\tis3d\\rig_calibration.xml")
print(ic3d_status(status))
the returned error is like this:
ic3d_status.IC3D_SUCCESS
IC3D: Could not load H+詡$ H
ic3d_status.IC3D_FILE_TO_ERROR
Hello
You may need to to the same, as I do in tisgrabber.py:
status = ic3d.ic3d_load_calibration("C:\\Users\\ASUS\Desktop\\3dCalib\\tis3d\\rig_calibration.xml".encode("utf-8"))
Because the "H+詡$ H" looks for a wrongly interpreted unicode string.
Stefan
Thank you again, after studying the header file and your tisgrabber.py, I fix that issue this way. Now the string values are interpreted correctly however the code runs into a OSError: access violation reading 0xFFFFFFFFFFFFFFFF
The Code is as follow:
import numpy as np
import cv2
import ctypes, os
from enum import Enum
abs_path_to_directory = os.path.abspath(os.path.dirname(__file__))
# add the directory to the os PATH
print(abs_path_to_directory)
os.environ['PATH'] = abs_path_to_directory + os.pathsep + os.environ['PATH']
ic3d = ctypes.cdll.LoadLibrary("libic3d.dll")
class ic3d_status(Enum):
IC3D_SUCCESS = 0
IC3D_FILE_TO_ERROR = 1
IC3D_MEMORY_ALLOCATION_ERROR = 2
IC3D_INVALID_POINTER_ERROR = 3
IC3D_MATCHER_INIT_ERROR = 4
IC3D_INPUT_DIMENSION_ERROR = 5
IC3D_INPUT_FORMAT_ERROR = 6
IC3D_MATCHER_NOT_VALID = 7
IC3D_ERROR = 8
class ic3d_image_format(Enum):
IC3D_IMG_INVALID_FORMAT = 0
IC3D_IMG_Y800 = 1 # An single channel (8-bit) grayscale image
IC3D_IMG_RGB32 = 2 # A four channel RGBA image (8-bit per channel)
IC3D_IMG_XYZA32 = 3 # A four channel floating point image (32-bit per channel)
class ic3d_matcher_type(Enum):
IC3D_CPU_MATCHER = 0
IC3D_CUDA_MATCHER = 1
class ic3d_origin_type(Enum):
IC3D_ORIGIN_CALIB = 0
IC3D_ORIGIN_LEFT_CAM = 1
IC3D_ORIGIN_RIGHT_CAM = 2
class ic3d_image:
def __init__(self, img, img_format):
self.data = ctypes.c_ubyte(img.data)
self.img_format = ctypes.c_int(img_format)
self.width = ctypes.c_intimg.shape[0]
self.height = ctypes.c_intimg.shape[1]
# assign function to python objects
start = ic3d.ic3d_init_context
load_file = ic3d.ic3d_load_calibration
# define the argtypes and restypes
start.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
start.restype = ctypes.c_long
load_file.argtypes = [ctypes.POINTER(ctypes.c_void_p), ctypes.c_char_p]
load_file.restype = ctypes.c_long
Handle = ctypes.c_void_p() # create a Handle to the context
# prepare the file path
fpath = ctypes.c_char_p("rig_calibration_rect.xml".encode('utf-8'))
filepath = ctypes.create_string_buffer(1000)
ctypes.memmove(filepath, fpath, len("rig_calibration_rect.xml"))
"""This method of filename works too but returns the same error code IC3D_FILE_TO_ERROR"""
# filepath = ctypes.c_char_p("rig_calibration_rect.xml".encode('utf-8'))
status = start(ctypes.byref(Handle))
print(ic3d_status(status))
status2 =load_file(Handle,filepath) # this is where error happens
print(ic3d_status(status2))
The error
Traceback (most recent call last):
File "c:\Users\ASUS\Desktop\3dCalib\tis3d\tis_stereo.py", line 85, in <module>
status2 =load_file(Handle,filepath)
OSError: exception: access violation reading 0xFFFFFFFFFFFFFFFF
The example code runs in C++ with visual studio 2019 compiler without any errors. To my surprise, the unicode is parsed correctly by the DLL since when I change the filepath to an incorrect destination like,
# prepare the file path
fpath = ctypes.c_char_p("abc.xml".encode('utf-8'))
it returns following error:
<ctypes.c_char_Array_1000 object at 0x00000226C6612BC0>
ic3d_status.IC3D_SUCCESS
IC3D: Could not load abc.xml
ic3d_status.IC3D_FILE_TO_ERROR
#Edit: seems like the code is working when context is passed this way. I will keep working on this method and try to implement other functions.
class Handle(ctypes.Structure):
'''
This class is used to handle the pointer to the internal
context class, which contains the context.
A pointer to this class is used by ic3d DLL.
'''
_fields_ = [('unused', ctypes.c_void_p)]
start.argtypes = [ctypes.POINTER(Handle)]
start.restype = ctypes.c_long
# load_file.argtypes = [ctypes.POINTER(Handle), ctypes.c_char_p] #--> by not defining this part the code below works
load_file.restype = ctypes.c_long
"""This method of filename works IC3D_FILE_TO_ERROR"""
filepath = ctypes.c_char_p("rig_calivbration_rect.xml".encode('utf-8'))
Handler = Handle()
print(filepath)
status = start(Handler)
print('1',ic3d_status(status))
status2 = load_file(Handler,"rig_calibration_rect.xml".encode('utf-8'))
print('2',ic3d_status(status2))
and returns:
1 ic3d_status.IC3D_SUCCESS
2 ic3d_status.IC3D_SUCCESS
Thanks a lot again for your guidance.
Good to read, you got it working now. The fields = [('unused', ctypes.c_void_p)] is similar to what I have in tisgrabber.py.
Stefan
I forgot to mention, we no longer support and develope the IC 3D software.
Stefan
Thank you for all the help you provided. I have one last question if you may be able to help me.
in c++ there is a method for converting cv2 :: MAT to ic3d image and from ic3d to cv2.
void cv_as_ic3d(const cv::Mat &src,
ic3d_image *dst)
{
ic3d_image_format ic3d_format;
auto src_type = src.type();
switch(src_type) {
case CV_8UC1:
ic3d_format = IC3D_IMG_Y800;
break;
case CV_8UC4:
ic3d_format = IC3D_IMG_RGB32;
break;
case CV_32FC4:
ic3d_format = IC3D_IMG_XYZA32;
break;
default:
ic3d_format = IC3D_IMG_INVALID_FORMAT;
break;
}
if(ic3d_format == IC3D_IMG_INVALID_FORMAT) {
return;
}
dst->data = (unsigned char *)src.data;
dst->width = src.cols;
dst->height = src.rows;
dst->format = ic3d_format;
return;
}
void ic3d_as_cv(const ic3d_image *src,
cv::Mat &dst)
{
int cv_format;
if(src->format == IC3D_IMG_Y800) {
cv_format = CV_8UC1;
}
else if(src->format == IC3D_IMG_RGB32) {
cv_format = CV_8UC4;
}
else if(src->format == IC3D_IMG_XYZA32) {
cv_format = CV_32FC4;
}
else {
return;
}
dst = cv::Mat(src->height, src->width, cv_format, src->data);
return;
}
I am trying to convert the python numpy array representation to the IC3D format as follow. but I am getting IC3D_INPUT_FORMAT_ERROR
import numpy as np
# image formats for to and from i3d functions
class ic3d_image(ctypes.Structure):
''' The structure of ic3D image'''
_fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
('width', ctypes.c_int),
('height', ctypes.c_int),
('format', ctypes.c_int)]
def cv2_to_ic3d(cv2_img:np.ndarray):
'''
Convert a cv2 image to ic3d image.
'''
# convert the image to ic3d image
ic3d_img = ic3d_image()
ic3d_img.width = cv2_img.shape[1]
ic3d_img.height = cv2_img.shape[0]
ic3d_img.format = IC3D_IMG_RGB32
ic3d_img.data = ctypes.cast(cv2_img.ctypes.data, ctypes.POINTER(ctypes.c_ubyte))
return ic3d_img
def ic3d_to_cv2(ic3d_img:ic3d_image):
'''
Convert a ic3d image to cv2 image.
'''
# convert the image to cv2 image
cv2_img = np.ndarray(shape=(ic3d_img.height, ic3d_img.width, 4), dtype=np.uint8, buffer=ic3d_img.data)
return cv2_img
I am very sorry, but here I am not able to help at all. I can convert a GstBuffer to numpy and numpy to QPixmap. But nothing else. I simply do not know, how to do that with Python. You are sure, you have a 32 bit image? Stefan
I am sure I have a 32bit image (RGBA) as shown in functions. Actually never mind. I would just gonna leave this to be. Again thanks a lot for your help.