Skip to content

Commit

Permalink
Merged PR 26069: changes for pylon 7.5 and 3D
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasgau authored and bjoernrennfanz committed Jun 28, 2024
2 parents 1c86368 + 022a28c commit 741e6b8
Show file tree
Hide file tree
Showing 26 changed files with 1,494 additions and 370 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[build-system]
requires = ["setuptools>=42", "swig>=4.0", "wheel"]
requires = ["setuptools>=42", "swig>=4.2", "wheel"]
build-backend = "setuptools.build_meta"

[tool.cibuildwheel]
Expand Down
68 changes: 68 additions & 0 deletions samples/datacontainer_load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# ===============================================================================
# This sample shows how to access data containers.
# This is needed when using 3D cameras, e.g., Basler blaze.
# ===============================================================================
import os
from pypylon import pylon
from pypylon import genicam

import sys

# This is used for visualization.
import cv2
import time

# The exit code of the sample application.
exitCode = 0

try:
# Create a pylon data container object.
pylonDataContainer = pylon.PylonDataContainer()

# Load the recipe file.
thisdir = os.path.dirname(__file__)
filename = os.path.join(thisdir, 'images/3D/little_boxes.gendc')
pylonDataContainer.Load(filename)

print("Component Count: ", pylonDataContainer.DataComponentCount);

# Access data components if the component type indicates image data
for componentIndex in range(pylonDataContainer.DataComponentCount):
pylonDataComponent = pylonDataContainer.GetDataComponent(componentIndex);
# Access the component data.
print("ComponentType: ", pylonDataComponent.ComponentType)
print("PixelType: ", pylonDataComponent.PixelType)
print("SizeX: ", pylonDataComponent.Width)
print("SizeY: ", pylonDataComponent.Height)
print("OffsetX: ", pylonDataComponent.OffsetX)
print("OffsetY: ", pylonDataComponent.OffsetY)
print("PaddingX: ", pylonDataComponent.PaddingX)
print("DataSize: ", pylonDataComponent.DataSize)
print("TimeStamp: ", pylonDataComponent.TimeStamp)
img = pylonDataComponent.Array
print("Gray value of first pixel: ", img[0, 0])
if pylonDataComponent.PixelType == pylon.PixelType_Coord3D_ABC32f:
None
else:
cv2.imshow('Image' + str(componentIndex), img)
# Release the data, otherwise it will not be freed
# The data is held by the container and the components until all of them are released.
pylonDataComponent.Release()

for i in range(60):
time.sleep(1)
# Break the endless loop by pressing ESC.
k = cv2.waitKey(5) & 0xFF
if k == 27:
break

# Free the data held by the container
pylonDataContainer.Release()

except genicam.GenericException as e:
# Error handling.
print("An exception occurred.")
print(e)
exitCode = 1

sys.exit(exitCode)
82 changes: 82 additions & 0 deletions samples/grabdatacontainer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# ===============================================================================
# This sample illustrates how to grab and process data using the CInstantCamera class.
# The data is grabbed and processed asynchronously, i.e.,
# while the application is processing a buffer, the acquisition of the next buffer is done
# in parallel.
#
# Utilizes the API for accessing GenICam Generic Data Container (GenDC).
# This will allow the use of, e.g., Basler blaze 3D cameras.
# ===============================================================================
from pypylon import pylon
from pypylon import genicam

import sys

# Number of results to be grabbed.
countOfResultsToGrab = 100

# The exit code of the sample application.
exitCode = 0

try:
# Create an instant camera object with the camera device found first.
camera = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice())
camera.Open()

# Print the model name of the camera.
print("Using device ", camera.GetDeviceInfo().GetModelName())

# demonstrate some feature access
new_width = camera.Width.Value - camera.Width.Inc
if new_width >= camera.Width.Min:
camera.Width.Value = new_width

# The parameter MaxNumBuffer can be used to control the count of buffers
# allocated for grabbing. The default value of this parameter is 10.
camera.MaxNumBuffer.Value = 5

# Start the grabbing of c_countOfImagesToGrab grab results.
# The camera device is parameterized with a default configuration which
# sets up free-running continuous acquisition.
camera.StartGrabbingMax(countOfResultsToGrab)

# Camera.StopGrabbing() is called automatically by the RetrieveResult() method
# when c_countOfImagesToGrab grab results have been retrieved.
while camera.IsGrabbing():
# Wait for grabbed data and then retrieve it. A timeout of 5000 ms is used.
grabResult = camera.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)

# Data grabbed successfully?
if grabResult.GrabSucceeded():
# Get the grab result as a PylonDataContainer, e.g. when working with 3D cameras.
pylonDataContainer = grabResult.GetDataContainer();
print("Component Count: ", pylonDataContainer.DataComponentCount);
# Access data components if the component type indicates image data
for componentIndex in range(pylonDataContainer.DataComponentCount):
pylonDataComponent = pylonDataContainer.GetDataComponent(componentIndex);
if pylonDataComponent.ComponentType == pylon.ComponentType_Intensity:
# Access the component data.
print("PixelType: ", pylonDataComponent.PixelType)
print("SizeX: ", pylonDataComponent.Width)
print("SizeY: ", pylonDataComponent.Height)
print("OffsetX: ", pylonDataComponent.OffsetX)
print("OffsetY: ", pylonDataComponent.OffsetY)
print("PaddingX: ", pylonDataComponent.PaddingX)
print("DataSize: ", pylonDataComponent.DataSize)
print("TimeStamp: ", pylonDataComponent.TimeStamp)
img = pylonDataComponent.Array
print("Gray value of first pixel: ", img[0, 0])
pylonDataComponent.Release()
pylonDataContainer.Release()
else:
print("Error: ", grabResult.ErrorCode, grabResult.ErrorDescription)
grabResult.Release()
camera.Close()

except genicam.GenericException as e:
# Error handling.
print("An exception occurred.")
print(e)
exitCode = 1

sys.exit(exitCode)
Binary file added samples/images/3D/little_boxes.gendc
Binary file not shown.
17 changes: 3 additions & 14 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ class BuildSupport(object):
"gentl",
"extra",
"pylondataprocessing",
"cxp",
}

# Global switch to toggle pylon data processing support on or off
Expand Down Expand Up @@ -457,18 +456,11 @@ class BuildSupportWindows(BuildSupport):
"gentl": [
("PylonGtc_*.dll", ""),
],

"cxp": [
],
}

GENTL_CXP_PRODUCER_DIR = "pylonCXP"
PYLON_DATA_PROCESSING_VTOOLS_DIR = "pylonDataProcessingPlugins"

RuntimeFolders = {
"cxp": [
(GENTL_CXP_PRODUCER_DIR, GENTL_CXP_PRODUCER_DIR, ()),
],
"pylondataprocessing": [
(PYLON_DATA_PROCESSING_VTOOLS_DIR, PYLON_DATA_PROCESSING_VTOOLS_DIR, ("*Editor*.dll",)),
],
Expand All @@ -485,7 +477,6 @@ class BuildSupportWindows(BuildSupport):
DefineMacros = [
("UNICODE", None),
("_UNICODE", None),
("GENTL_CXP_PRODUCER_DIR", gentl_dir_fmt % GENTL_CXP_PRODUCER_DIR),

# let swig share its type information between the 'genicam' and the
# 'pylon' module by using the same name for the type table.
Expand Down Expand Up @@ -702,8 +693,6 @@ class BuildSupportLinux(BuildSupport):
"usb": [
(r"pylon-libusb-.*\.so", ""),
],
"cxp": [
],
}

# up to one symlink per library -> match shared objects only
Expand Down Expand Up @@ -752,11 +741,11 @@ class BuildSupportLinux(BuildSupport):
(r"libpylonutilitypcl\.so\.\d+\.\d+", ""),
],
"gentl": [
("libpylon_TL_gtc\.so", ""),
(r"libpylon_TL_gtc\.so", ""),
],
"pylondataprocessing": [
(r"libPylonDataProcessing\.so\.\d+", ""),
("libPylonDataProcessing.sig", ""),
(r"libPylonDataProcessing.sig", ""),
(r"libPylonDataProcessingCore\.so\.\d+", ""),
],
}
Expand Down Expand Up @@ -1019,7 +1008,7 @@ def copy_runtime(self):
# cleanup double TL entries in pylon 6.2.0
if self.get_pylon_version() == "6.2.0.18677":
for p in Path(f"{full_dst}").glob("**/*TL*.so"):
if re.match(".*TL_[a-z]+\.so", p.name):
if re.match(r".*TL_[a-z]+\.so", p.name):
info(f"DELETE {p}")
os.remove(p)

Expand Down
13 changes: 0 additions & 13 deletions src/pylon/GrabResultData.i
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,6 @@
%#endif
}

int GetNumBufferExports(PyObject * omv)
{
// need at least Python 3.3 for memory view
%#if PY_VERSION_HEX >= 0x03030000
PyMemoryViewObject * mv = (PyMemoryViewObject *) omv;
int ret = (int) mv->mbuf->exports;
Py_DECREF(omv);
return ret;
%#else
return 0;
%#endif
}

PyObject * _Unpack10or12BitPacked()
{
// Current pixel type of our data
Expand Down
91 changes: 44 additions & 47 deletions src/pylon/GrabResultPtr.i
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
%ignore operator IImage&;
%rename(GrabResult) Pylon::CGrabResultPtr;

%pythoncode %{
from contextlib import contextmanager
import sys
%}

%extend Pylon::CGrabResultPtr {
%pythoncode %{
@needs_numpy
Expand All @@ -11,7 +16,7 @@
pt = self.GetPixelType()
if IsPacked(pt):
raise ValueError("Packed Formats are not supported with numpy interface")
if pt in ( PixelType_Mono8, PixelType_BayerGR8, PixelType_BayerRG8, PixelType_BayerGB8, PixelType_BayerBG8 ):
if pt in ( PixelType_Mono8, PixelType_BayerGR8, PixelType_BayerRG8, PixelType_BayerGB8, PixelType_BayerBG8, PixelType_Confidence8, PixelType_Coord3D_C8 ):
shape = (self.GetHeight(), self.GetWidth())
format = "B"
dtype = _pylon_numpy.uint8
Expand All @@ -23,7 +28,7 @@
shape = (self.GetHeight(), self.GetWidth())
format = "H"
dtype = _pylon_numpy.uint16
elif pt in ( PixelType_Mono16, PixelType_BayerGR16, PixelType_BayerRG16, PixelType_BayerGB16, PixelType_BayerBG16 ):
elif pt in ( PixelType_Mono16, PixelType_BayerGR16, PixelType_BayerRG16, PixelType_BayerGB16, PixelType_BayerBG16, PixelType_Confidence16, PixelType_Coord3D_C16 ):
shape = (self.GetHeight(), self.GetWidth())
format = "H"
dtype = _pylon_numpy.uint16
Expand All @@ -35,6 +40,10 @@
shape = (self.GetHeight(), self.GetWidth(), 2)
dtype = _pylon_numpy.uint8
format = "B"
elif pt in ( PixelType_Coord3D_ABC32f, ):
shape = (self.GetHeight(), self.GetWidth(), 3)
dtype = _pylon_numpy.float32
format = "f"
else:
raise ValueError("Pixel format currently not supported")

Expand Down Expand Up @@ -115,52 +124,40 @@
def __exit__(self, type, value, traceback):
self.Release()

from sys import version_info as _gazc_python_version_info
# need at least Python 3.3 for memory view
if _gazc_python_version_info >= (3, 3, 0):
from contextlib import contextmanager
@contextmanager
@needs_numpy
def GetArrayZeroCopy(self, raw = False):
'''
Get a numpy array for the image buffer as zero copy reference to the underlying buffer.
Note: The context manager variable MUST be released before leaving the scope.
'''

# For packed formats, we cannot zero-copy, so use GetArray
pt = self.GetPixelType()
if IsPacked(pt):
yield self.GetArray()
return
@contextmanager
@needs_numpy
def GetArrayZeroCopy(self, raw = False):
'''
Get a numpy array for the image buffer as zero copy reference to the underlying buffer.
Note: The context manager variable MUST be released before leaving the scope.
'''

# For packed formats, we cannot zero-copy, so use GetArray
pt = self.GetPixelType()
if IsPacked(pt):
yield self.GetArray()
return

mv = self.GetImageMemoryView()
if not raw:
shape, dtype, format = self.GetImageFormat()
mv = mv.cast(format, shape)

ar = _pylon_numpy.asarray(mv)

# trace external references to array
initial_refcount = sys.getrefcount(ar)

# yield the array to the context code
yield ar

# detect if more refs than the one from the yield are held
if sys.getrefcount(ar) > initial_refcount + 1:
raise RuntimeError("Please remove any references to the array before leaving context manager scope!!!")

# release the memory view
mv.release()

# Here is the procedure:
# 1. prepare and get image format info
# 2. get a memory view for our image buffer and
# cast it to the right shape and data format
# 3. build an array upon the view (zero copy!)
# 4. as context manager, we yield this array
# 5. delete the array and release our memory
# 6. check the number of exports of the encapsuled buffer
# => if this is > 0 => somebody else still has a reference!

mv = self.GetImageMemoryView()
if not raw:
shape, dtype, format = self.GetImageFormat()
mv = mv.cast(format, shape)

ar = _pylon_numpy.asarray(mv)

yield ar

del ar
mv.release() # Only release() so we can check the references

# There will be one outstanding reference for the 'with target'.
# That is OK since that will be released right after this function
# returns.
if self.GetNumBufferExports(mv) > 1:
raise RuntimeError("Please remove any references to the array before leaving context manager scope!!!")
del _gazc_python_version_info
%}
}

Expand Down
Loading

0 comments on commit 741e6b8

Please sign in to comment.