Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hotfix imports for image client #47

Closed
wants to merge 12 commits into from
Closed
82 changes: 82 additions & 0 deletions examples/create_rawimage_application.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
try:
from o2x5xx import O2x5xxRPCDevice, O2x5xxDeviceV2
except ModuleNotFoundError:
from source.rpc.client import O2x5xxRPCDevice
from source.device.client import O2x5xxDeviceV2
import sys
import matplotlib.pyplot as plt
import matplotlib.image

if __name__ == '__main__':
if len(sys.argv) > 1:
address = sys.argv[1]
else:
address = '192.168.0.69'

# create device
print("Create RPC device, request session and setup edit mode.")
with O2x5xxRPCDevice(address) as device:

with device.mainProxy.requestSession():
with device.sessionProxy.setOperatingMode(mode=1):
print("Operation Mode {} set".format(device.getParameter(value="OperatingMode")))
newApplicationIndex = device.edit.createApplication("Camera")
print("Created new application with index: " + str(newApplicationIndex))

with device.editProxy.editApplication(app_index=newApplicationIndex):
print("Starting editing application with index: " + str(newApplicationIndex))
device.application.Name = "Application Uncompressed Image"
print("Changed name of application: " + device.application.Name)
device.application.Description = "Application for retrieving an uncompressed image"
print("Changed description of application: " + device.application.Description)
device.application.TriggerMode = 2 # process interface trigger mode
print("Trigger Mode {} set".format(device.application.TriggerMode))

# Setup of image001
with device.applicationProxy.editImager(imager_index=1):
print("Start calculating auto exposure time...")
device.imager.startCalculateExposureTime()
print("End of calculating exposure time with recommended value: " + str(
device.imager.ExposureTime))
print("Start calculating autofocus...")
device.imager.startCalculateAutofocus()
print("End of calculating autofocus with recommended value(s): " + str(
device.imager.getAutofocusDistances()))
device.imager.Name = "Uncompressed Image"
print("Changed name of image001: " + device.imager.Name)

# Switch to uncompressed images
device.application.UncompressedImages = True
print("Enabled output of uncompressed images")

device.application.save()
print("Saving parameter consistent to memory for application: " + device.application.Name)
device.switchApplication(applicationIndex=newApplicationIndex)
print("Application with new index {} now active".format(newApplicationIndex))

# create device
print("Create O2x5xx V2 device.")
with O2x5xxDeviceV2(address) as device:
# Save 10 uncompressed images
for i in range(10):
print("Executing trigger.")
# device.pcic.execute_asynchronous_trigger()
trigger_result = device.pcic.execute_asynchronous_trigger()
if trigger_result != '*':
print(trigger_result)
sys.exit("Trigger failed!")
else:
_ = device.pcic.read_next_answer()

print("Requesting last image...")
result = device.pcic.request_last_image_taken_deserialized(image_id=2, datatype="ndarray")
if result == "!":
print(result)
sys.exit("Request for last image failed!")
image_uncompressed = result[0][1]

# Uncomment if you want to see the image
# plt.imshow(image_uncompressed, cmap='gray')
# plt.show()
# matplotlib.image.imsave(fname='image_uncompressed_{}.png'.format(str(i)),
# arr=image_uncompressed, cmap='gray')
2 changes: 1 addition & 1 deletion examples/image_viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
address = '192.168.0.69'

# create grabber
image_client = ImageClient(address, 50010)
image_client = ImageClient(address=address, port=50010, timeout=None)

# create figures
figs = [image_client.make_figure(i) for i in range(image_client.number_images)]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def read_requires():

setup(
name='o2x5xx',
version='0.3.0-beta',
version='0.4.3-pre',
description='A Python library for ifm O2x5xx (O2D5xx / O2I5xx) devices',
author='Michael Gann',
author_email='[email protected]',
Expand Down
49 changes: 36 additions & 13 deletions source/device/image_client.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
from __future__ import (absolute_import, division, print_function)
from builtins import *
from ..pcic.client import O2x5xxPCICDevice
from ..static.formats import serialization_format
from .client import O2x5xxPCICDevice
from ..static.formats import serialization_format, ChunkType
from ..static.configs import images_config
import binascii
import struct
import io
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np


SOCKET_TIMEOUT = 10


class ImageClient(O2x5xxPCICDevice):
def __init__(self, address, port):
super(ImageClient, self).__init__(address, port)
def __init__(self, address, port, timeout=SOCKET_TIMEOUT):
super(ImageClient, self).__init__(address=address, port=port, timeout=timeout)

# disable all result output
self.turn_process_interface_output_on_or_off(0)
Expand Down Expand Up @@ -51,9 +54,12 @@ def read_image_ids(self):
ticket, answer = self.read_next_answer()

if ticket == b"0000":
delimiter = str(answer).find('stop')
ids = answer[:delimiter-12].decode()
return ids.split(';')[1:-1]
delimiter = answer.find(b'stop')
if delimiter == -1:
print("stop identifier not found in result")
return []
ids = answer[5:delimiter].decode()
return ids.split(';')[0:-1]

@staticmethod
def _deserialize_image_chunk(data):
Expand All @@ -64,8 +70,7 @@ def _deserialize_image_chunk(data):
:return: deserialized results
"""
results = {}
length = int(data.__len__())
data = binascii.unhexlify(data.hex())
length = len(data)
counter = 0

while length:
Expand All @@ -80,7 +85,22 @@ def _deserialize_image_chunk(data):
results.setdefault(counter, []).append(header)
# append image
image_hex = data[header['HEADER_SIZE']:header['CHUNK_SIZE']]
image = mpimg.imread(io.BytesIO(image_hex), format='jpg')
chunk_type = int(header['CHUNK_TYPE'])
# check end decode image depending on chunk type
if chunk_type == ChunkType.JPEG_IMAGE:
# Check that we have received chunk type JPEG_IMAGE
# Convert jpeg data to image data
image = mpimg.imread(io.BytesIO(image_hex), format='jpg')
results[counter].append(image)
elif chunk_type == ChunkType.MONOCHROME_2D_8BIT:
# Check that we have received chunk type MONOCHROME_2D_8BIT
# Read pixel data and reshape to width/height
image = np.frombuffer(image_hex, dtype=np.uint8) \
.reshape((header["IMAGE_HEIGHT"], header["IMAGE_WIDTH"]))
results[counter].append(image)
else:
image = None
print("Unknown image chunk type", header['CHUNK_TYPE'])
results[counter].append(image)

length -= header['CHUNK_SIZE']
Expand All @@ -100,8 +120,11 @@ def read_next_frames(self):
ticket, answer = self.read_next_answer()

if ticket == b"0000":
delimiter = str(answer).find('stop')
result = self._deserialize_image_chunk(data=answer[delimiter-8:])
delimiter = answer.find(b'stop')
if delimiter == -1:
print("stop identifier not found in result")
self.frames = []
result = self._deserialize_image_chunk(data=answer[delimiter+4:])
self.frames = [result[i][1] for i in result]

def make_figure(self, idx):
Expand Down
30 changes: 23 additions & 7 deletions source/pcic/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from ..static.formats import error_codes, serialization_format
from ..static.formats import error_codes, serialization_format, ChunkType
import matplotlib.image as mpimg
import numpy as np
import binascii
import socket
import struct
Expand Down Expand Up @@ -93,8 +94,8 @@ def recv(self, number_bytes):
fragments = []

while total_recved < number_bytes:
chunk = self.pcicSocket.recv(
Client.BUF_LEN if number_bytes - total_recved >= Client.BUF_LEN else number_bytes)
bytes_to_receive = min(Client.BUF_LEN, number_bytes - total_recved)
chunk = self.pcicSocket.recv(bytes_to_receive)

if len(chunk) == 0:
raise RuntimeError("Connection to server closed")
Expand Down Expand Up @@ -361,7 +362,8 @@ def request_last_image_taken(self, image_id=1):
- <length> (int) char string with exactly 9 digits as decimal number for the image data size in bytes.
- <image data> (bytearray) image data / result data. The data is encapsulated in an image chunk.
- ! No image available
| Wrong ID
| Wrong ID (Applications which transfer uncompressed images are
not able to transfer images as JPG and vise versa)
- ? Invalid command length
"""
if str(image_id).isnumeric():
Expand All @@ -385,11 +387,14 @@ def request_last_image_taken_deserialized(self, image_id=1, datatype='ndarray'):
- <image data> image data / result data. The data is encapsulated
in an image chunk if bytes as datatype is selected.
- ! No image available
| Wrong ID
| Wrong ID (Applications which transfer uncompressed images are
not able to transfer images as JPG and vise versa)
- ? Invalid command length
"""
results = {}
result = self.request_last_image_taken(image_id)
if str(result)[2:3] == "!":
return "!"
length = int(result[:9].decode())
data = binascii.unhexlify(result[9:].hex())
counter = 0
Expand All @@ -406,9 +411,20 @@ def request_last_image_taken_deserialized(self, image_id=1, datatype='ndarray'):
results.setdefault(counter, []).append(header)
# append image
image_hex = data[header['HEADER_SIZE']:header['CHUNK_SIZE']]
chunk_type = int(header['CHUNK_TYPE'])
# check end decode image depending on chunk type
if datatype == 'ndarray':
image = mpimg.imread(io.BytesIO(image_hex), format='jpg')
results[counter].append(image)
if chunk_type == ChunkType.JPEG_IMAGE:
# Check that we have received chunk type JPEG_IMAGE
# Convert jpeg data to image data
image = mpimg.imread(io.BytesIO(image_hex), format='jpg')
results[counter].append(image)
elif chunk_type == ChunkType.MONOCHROME_2D_8BIT:
# Check that we have received chunk type MONOCHROME_2D_8BIT
# Read pixel data and reshape to width/height
image = np.frombuffer(image_hex, dtype=np.uint8)\
.reshape((header["IMAGE_HEIGHT"], header["IMAGE_WIDTH"]))
results[counter].append(image)
elif datatype == 'bytes':
results[counter].append(image_hex)
else:
Expand Down
25 changes: 25 additions & 0 deletions source/rpc/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,31 @@ def Rotate180Degree(self, value: bool) -> None:
self._applicationProxy.proxy.setParameter("Rotate180Degree", value)
self.waitForConfigurationDone()

@property
def UncompressedImages(self) -> bool:
"""
Flag whether images are output uncompressed (raw pixel images as
opposed to JPEG images)

:return: (bool) True / False
"""
result = self._applicationProxy.proxy.getParameter("2dUncompressedImages")
if result == "false":
return False
return True

@UncompressedImages.setter
def UncompressedImages(self, value: bool) -> None:
"""
Set image format for output (True for uncompressed pixel images,
False for JPEG images)

:param value: (bool) True / False
:return: None
"""
self._applicationProxy.proxy.setParameter("2dUncompressedImages", value)
self.waitForConfigurationDone()

@property
def FocusDistance(self) -> float:
"""
Expand Down
12 changes: 0 additions & 12 deletions source/rpc/imageQualityCheck.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from urllib.parse import urlparse
from urllib.request import urlopen
import json
import time
Expand All @@ -14,12 +13,6 @@ def __init__(self, imagerProxy, device):
self._imagerProxy = imagerProxy
self._device = device

# def __enter__(self):
# return self
#
# def __exit__(self, exc_type, exc_val, exc_tb):
# pass

@property
def enabled(self) -> bool:
"""
Expand Down Expand Up @@ -78,11 +71,6 @@ def _QualityCheckConfigSchema(self) -> dict:

:return: (dict) schema of Image Quality Check
"""
# ip = urlparse(self._imagerProxy.proxy.baseURL).netloc
# with urlopen("http://{}/schema/ParamImageFeatures.json".format(ip)) as url:
# data = json.load(url)
# return data

with urlopen("http://{}/schema/ParamImageFeatures.json".format(self._device.address)) as url:
data = json.load(url)
return data
Expand Down
12 changes: 8 additions & 4 deletions source/rpc/imager.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,12 +356,14 @@ def startCalculateExposureTime(self, minAnalogGainFactor: int = None, maxAnalogG
inputAutoExposure["maxAnalogGainFactor"] = maxAnalogGainFactor
if saturatedRatio:
inputAutoExposure["saturatedRatio"] = saturatedRatio
# ROIs
if ROIs:
inputAutoExposure["ROIs"] = ROIs
if RODs and not ROIs:
else:
defaultROIsZone = [{"id": 0, "group": 0, "type": "Rect", "width": 1280,
"height": 960, "angle": 0, "center_x": 640, "center_y": 480}]
"height": 960, "angle": 0, "center_x": 640, "center_y": 480}]
inputAutoExposure["ROIs"] = defaultROIsZone
# RODs
if RODs:
inputAutoExposure["RODs"] = RODs
self._imagerProxy.proxy.startCalculateExposureTime(json.dumps(inputAutoExposure))
Expand All @@ -386,12 +388,14 @@ def startCalculateAutofocus(self, ROIs: list = None, RODs: list = None) -> None:
:return: None
"""
inputAutoFocus = {}
# ROIs
if ROIs:
inputAutoFocus["ROIs"] = ROIs
if RODs and not ROIs:
else:
defaultROIsZone = [{"id": 0, "group": 0, "type": "Rect", "width": 1280,
"height": 960, "angle": 0, "center_x": 640, "center_y": 480}]
"height": 960, "angle": 0, "center_x": 640, "center_y": 480}]
inputAutoFocus["ROIs"] = defaultROIsZone
# RODs
if RODs:
inputAutoFocus["RODs"] = RODs
self._imagerProxy.proxy.startCalculateAutofocus(json.dumps(inputAutoFocus))
Expand Down
4 changes: 4 additions & 0 deletions source/static/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@
{
"id": "jpeg_image",
"type": "blob"
},
{
"id": "raw_image",
"type": "blob"
}
],
"id": "Images",
Expand Down
7 changes: 7 additions & 0 deletions source/static/formats.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import enum

# These are the error codes with the corresponding error message
error_codes = {
000000000: "No error detected",
Expand Down Expand Up @@ -78,3 +80,8 @@
2: "external illumination shall be used",
3: "internal and external illumination shall be used together"
}


class ChunkType(enum.IntEnum):
MONOCHROME_2D_8BIT = 251
JPEG_IMAGE = 260