I'm looking for a way to get a buffer from camera2 API in Kivy, like with camera_android.
I am using the code from the Color blind project :https://github.com/inclement/colour-blind-camera/blob/master/camera2/camera2.py
THANKS.
camera_android
from jnius import autoclass, PythonJavaClass, java_method
from kivy.clock import Clock
from kivy.graphics.texture import Texture
from kivy.graphics import Fbo, Callback, Rectangle
from kivy.core.camera import CameraBase
import threading
Camera = autoclass('android.hardware.Camera')
SurfaceTexture = autoclass('android.graphics.SurfaceTexture')
GL_TEXTURE_EXTERNAL_OES = autoclass(
'android.opengl.GLES11Ext').GL_TEXTURE_EXTERNAL_OES
ImageFormat = autoclass('android.graphics.ImageFormat')
class PreviewCallback(PythonJavaClass):
"""
Interface used to get back the preview frame of the Android Camera
"""
__javainterfaces__ = ('android.hardware.Camera$PreviewCallback', )
def __init__(self, callback):
super(PreviewCallback, self).__init__()
self._callback = callback
#java_method('([BLandroid/hardware/Camera;)V')
def onPreviewFrame(self, data, camera):
self._callback(data, camera)
class CameraAndroid(CameraBase):
"""
Implementation of CameraBase using Android API
"""
_update_ev = None
def __init__(self, **kwargs):
self._android_camera = None
self._preview_cb = PreviewCallback(self._on_preview_frame)
self._buflock = threading.Lock()
super(CameraAndroid, self).__init__(**kwargs)
def __del__(self):
self._release_camera()
def init_camera(self):
self._release_camera()
self._android_camera = Camera.open(self._index)
params = self._android_camera.getParameters()
width, height = self._resolution
params.setPreviewSize(width, height)
params.setFocusMode('continuous-picture')
self._android_camera.setParameters(params)
# self._android_camera.setDisplayOrientation()
self.fps = 30.
pf = params.getPreviewFormat()
assert(pf == ImageFormat.NV21) # default format is NV21
self._bufsize = int(ImageFormat.getBitsPerPixel(pf) / 8. *
width * height)
self._camera_texture = Texture(width=width, height=height,
target=GL_TEXTURE_EXTERNAL_OES,
colorfmt='rgba')
self._surface_texture = SurfaceTexture(int(self._camera_texture.id))
self._android_camera.setPreviewTexture(self._surface_texture)
self._fbo = Fbo(size=self._resolution)
self._fbo['resolution'] = (float(width), float(height))
self._fbo.shader.fs = '''
#extension GL_OES_EGL_image_external : require
#ifdef GL_ES
precision highp float;
#endif
/* Outputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tex_coord0;
/* uniform texture samplers */
uniform sampler2D texture0;
uniform samplerExternalOES texture1;
uniform vec2 resolution;
void main()
{
vec2 coord = vec2(tex_coord0.y * (
resolution.y / resolution.x), 1. -tex_coord0.x);
gl_FragColor = texture2D(texture1, tex_coord0);
}
'''
with self._fbo:
self._texture_cb = Callback(lambda instr:
self._camera_texture.bind)
Rectangle(size=self._resolution)
def _release_camera(self):
if self._android_camera is None:
return
self.stop()
self._android_camera.release()
self._android_camera = None
# clear texture and it'll be reset in `_update` pointing to new FBO
self._texture = None
del self._fbo, self._surface_texture, self._camera_texture
def _on_preview_frame(self, data, camera):
with self._buflock:
if self._buffer is not None:
# add buffer back for reuse
self._android_camera.addCallbackBuffer(self._buffer)
self._buffer = data
# check if frame grabbing works
# print self._buffer, len(self.frame_data)
def _refresh_fbo(self):
self._texture_cb.ask_update()
self._fbo.draw()
def start(self):
super(CameraAndroid, self).start()
with self._buflock:
self._buffer = None
for k in range(2): # double buffer
buf = b'\x00' * self._bufsize
self._android_camera.addCallbackBuffer(buf)
self._android_camera.setPreviewCallbackWithBuffer(self._preview_cb)
self._android_camera.startPreview()
if self._update_ev is not None:
self._update_ev.cancel()
self._update_ev = Clock.schedule_interval(self._update, 1 / self.fps)
def stop(self):
super(CameraAndroid, self).stop()
if self._update_ev is not None:
self._update_ev.cancel()
self._update_ev = None
self._android_camera.stopPreview()
self._android_camera.setPreviewCallbackWithBuffer(None)
# buffer queue cleared as well, to be recreated on next start
with self._buflock:
self._buffer = None
def _update(self, dt):
self._surface_texture.updateTexImage()
self._refresh_fbo()
if self._texture is None:
self._texture = self._fbo.texture
self.dispatch('on_load')
self._copy_to_gpu()
def _copy_to_gpu(self):
"""
A dummy placeholder (the image is already in GPU) to be consistent
with other providers.
"""
self.dispatch('on_texture')
def grab_frame(self):
"""
Grab current frame (thread-safe, minimal overhead)
"""
with self._buflock:
if self._buffer is None:
return None
buf = self._buffer.tostring()
return buf
def decode_frame(self, buf):
"""
Decode image data from grabbed frame.
This method depends on OpenCV and NumPy - however it is only used for
fetching the current frame as a NumPy array, and not required when
this :class:`CameraAndroid` provider is simply used by a
:class:`~kivy.uix.camera.Camera` widget.
"""
import numpy as np
from cv2 import cvtColor
w, h = self._resolution
arr = np.fromstring(buf, 'uint8').reshape((h + h / 2, w))
arr = cvtColor(arr, 93) # NV21 -> BGR
return arr
def read_frame(self):
"""
Grab and decode frame in one call
"""
return self.decode_frame(self.grab_frame())
#staticmethod
def get_camera_count():
"""
Get the number of available cameras.
"""
return Camera.getNumberOfCameras()
color blind camera
from kivy.event import EventDispatcher
from kivy.graphics.texture import Texture
from kivy.graphics import Fbo, Callback, Rectangle
from kivy.properties import (BooleanProperty, StringProperty, ObjectProperty, OptionProperty, ListProperty)
from kivy.clock import Clock
from jnius import autoclass, cast, PythonJavaClass, java_method, JavaClass, MetaJavaClass, JavaMethod
import logging
from enum import Enum
logger = logging.getLogger(__file__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
CameraManager = autoclass("android.hardware.camera2.CameraManager")
PythonActivity = autoclass("org.kivy.android.PythonActivity")
Context = autoclass("android.content.Context")
context = cast("android.content.Context", PythonActivity.mActivity)
CameraDevice = autoclass("android.hardware.camera2.CameraDevice")
CaptureRequest = autoclass("android.hardware.camera2.CaptureRequest")
CameraCharacteristics = autoclass("android.hardware.camera2.CameraCharacteristics")
ArrayList = autoclass('java.util.ArrayList')
JavaArray = autoclass('java.lang.reflect.Array')
SurfaceTexture = autoclass('android.graphics.SurfaceTexture')
Surface = autoclass('android.view.Surface')
GL_TEXTURE_EXTERNAL_OES = autoclass(
'android.opengl.GLES11Ext').GL_TEXTURE_EXTERNAL_OES
ImageFormat = autoclass('android.graphics.ImageFormat')
Handler = autoclass("android.os.Handler")
Looper = autoclass("android.os.Looper")
MyStateCallback = autoclass("net.inclem.camera2.MyStateCallback")
CameraActions = autoclass("net.inclem.camera2.MyStateCallback$CameraActions")
# MyStateCallback = autoclass("org.kivy.android.MyStateCallback")
MyCaptureSessionCallback = autoclass("net.inclem.camera2.MyCaptureSessionCallback")
CameraCaptureEvents = autoclass("net.inclem.camera2.MyCaptureSessionCallback$CameraCaptureEvents")
_global_handler = Handler(Looper.getMainLooper())
class LensFacing(Enum):
"""Values copied from CameraCharacteristics api doc, as pyjnius
lookup doesn't work on some devices.
"""
LENS_FACING_FRONT = 0
LENS_FACING_BACK = 1
LENS_FACING_EXTERNAL = 2
class ControlAfMode(Enum):
CONTROL_AF_MODE_CONTINUOUS_PICTURE = 4
class ControlAeMode(Enum):
CONTROL_AE_MODE_ON = 1
class Runnable(PythonJavaClass):
__javainterfaces__ = ['java/lang/Runnable']
def __init__(self, func):
super(Runnable, self).__init__()
self.func = func
#java_method('()V')
def run(self):
try:
self.func()
except:
import traceback
traceback.print_exc()
class PyCameraInterface(EventDispatcher):
"""
Provides an API for querying details of the cameras available on Android.
"""
camera_ids = []
cameras = ListProperty()
java_camera_characteristics = {}
java_camera_manager = ObjectProperty()
def __init__(self):
super().__init__()
logger.info("Starting camera interface init")
self.java_camera_manager = cast("android.hardware.camera2.CameraManager",
context.getSystemService(Context.CAMERA_SERVICE))
self.camera_ids = self.java_camera_manager.getCameraIdList()
characteristics_dict = self.java_camera_characteristics
camera_manager = self.java_camera_manager
logger.info("Got basic java objects")
for camera_id in self.camera_ids:
logger.info(f"Getting data for camera {camera_id}")
characteristics_dict[camera_id] = camera_manager.getCameraCharacteristics(camera_id)
logger.info("Got characteristics dict")
self.cameras.append(PyCameraDevice(
camera_id=camera_id,
java_camera_manager=camera_manager,
java_camera_characteristics=characteristics_dict[camera_id],
))
logger.info(f"Finished interpreting camera {camera_id}")
def select_cameras(self, **conditions):
options = self.cameras
outputs = []
for camera in cameras:
for key, value in conditions.items():
if getattr(camera, key) != value:
break
else:
outputs.append(camera)
return outputs
class PyCameraDevice(EventDispatcher):
camera_id = StringProperty()
output_texture = ObjectProperty(None, allownone=True)
preview_active = BooleanProperty(False)
preview_texture = ObjectProperty(None, allownone=True)
preview_resolution = ListProperty()
preview_fbo = ObjectProperty(None, allownone=True)
java_preview_surface_texture = ObjectProperty(None)
java_preview_surface = ObjectProperty(None)
java_capture_request = ObjectProperty(None)
java_surface_list = ObjectProperty(None)
java_capture_session = ObjectProperty(None)
connected = BooleanProperty(False)
supported_resolutions = ListProperty()
# TODO: populate this
facing = OptionProperty("UNKNOWN", options=["UNKNOWN", "FRONT", "BACK", "EXTERNAL"])
java_camera_characteristics = ObjectProperty()
java_camera_manager = ObjectProperty()
java_camera_device = ObjectProperty()
java_stream_configuration_map = ObjectProperty()
_open_callback = ObjectProperty(None, allownone=True)
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.register_event_type("on_opened")
self.register_event_type("on_closed")
self.register_event_type("on_disconnected")
self.register_event_type("on_error")
self._java_state_callback_runnable = Runnable(self._java_state_callback)
self._java_state_java_callback = MyStateCallback(self._java_state_callback_runnable)
self._java_capture_session_callback_runnable = Runnable(self._java_capture_session_callback)
self._java_capture_session_java_callback = MyCaptureSessionCallback(
self._java_capture_session_callback_runnable)
self._populate_camera_characteristics()
def on_opened(self, instance):
pass
def on_closed(self, instance):
pass
def on_disconnected(self, instance):
pass
def on_error(self, instance, error):
pass
def close(self):
self.java_camera_device.close()
def _populate_camera_characteristics(self):
logger.info("Populating camera characteristics")
self.java_stream_configuration_map = self.java_camera_characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
logger.info("Got stream configuration map")
self.supported_resolutions = [
(size.getWidth(), size.getHeight()) for size in
self.java_stream_configuration_map.getOutputSizes(SurfaceTexture(0).getClass())]
logger.info("Got supported resolutions")
facing = self.java_camera_characteristics.get(
CameraCharacteristics.LENS_FACING)
logger.info(f"Got facing: {facing}")
if facing == LensFacing.LENS_FACING_BACK.value: # CameraCharacteristics.LENS_FACING_BACK:
self.facing = "BACK"
elif facing == LensFacing.LENS_FACING_FRONT.value: # CameraCharacteristics.LENS_FACING_FRONT:
self.facing = "FRONT"
elif facing == LensFacing.LENS_FACING_EXTERNAL.value: # CameraCharacteristics.LENS_FACING_EXTERNAL:
self.facing = "EXTERNAL"
else:
raise ValueError("Camera id {} LENS_FACING is unknown value {}".format(self.camera_id, facing))
logger.info(f"Finished initing camera {self.camera_id}")
def __str__(self):
return "<PyCameraDevice facing={}>".format(self.facing)
def __repr__(self):
return str(self)
def open(self, callback=None):
self._open_callback = callback
self.java_camera_manager.openCamera(
self.camera_id,
self._java_state_java_callback,
_global_handler
)
def _java_state_callback(self, *args, **kwargs):
action = MyStateCallback.camera_action.toString()
camera_device = MyStateCallback.camera_device
self.java_camera_device = camera_device
logger.info("CALLBACK: camera event {}".format(action))
if action == "OPENED":
self.dispatch("on_opened", self)
self.connected = True
elif action == "DISCONNECTED":
self.dispatch("on_disconnected", self)
self.connected = False
elif action == "CLOSED":
self.dispatch("on_closed", self)
self.connected = False
elif action == "ERROR":
error = MyStateCallback.camera_error
self.dispatch("on_error", self, error)
self.connected = False
elif action == "UNKNOWN":
print("UNKNOWN camera state callback item")
self.connected = False
else:
raise ValueError("Received unknown camera action {}".format(action))
if self._open_callback is not None:
self._open_callback(self, action)
def start_preview(self, resolution):
if self.java_camera_device is None:
raise ValueError("Camera device not yet opened, cannot create preview stream")
if resolution not in self.supported_resolutions:
raise ValueError(
"Tried to open preview with resolution {}, not in supported resolutions {}".format(
resolution, self.supported_resolutions))
if self.preview_active:
raise ValueError("Preview already active, can't start again without stopping first")
logger.info("Creating capture stream with resolution {}".format(resolution))
self.preview_resolution = resolution
self._prepare_preview_fbo(resolution)
self.preview_texture = Texture(
width=resolution[0], height=resolution[1], target=GL_TEXTURE_EXTERNAL_OES, colorfmt="rgba")
logger.info("Texture id is {}".format(self.preview_texture.id))
self.java_preview_surface_texture = SurfaceTexture(int(self.preview_texture.id))
self.java_preview_surface_texture.setDefaultBufferSize(*resolution)
self.java_preview_surface = Surface(self.java_preview_surface_texture)
self.java_capture_request = self.java_camera_device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
self.java_capture_request.addTarget(self.java_preview_surface)
self.java_capture_request.set(
CaptureRequest.CONTROL_AF_MODE, ControlAfMode.CONTROL_AF_MODE_CONTINUOUS_PICTURE.value) # CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
self.java_capture_request.set(
CaptureRequest.CONTROL_AE_MODE, ControlAeMode.CONTROL_AE_MODE_ON.value) # CaptureRequest.CONTROL_AE_MODE_ON)
self.java_surface_list = ArrayList()
self.java_surface_list.add(self.java_preview_surface)
self.java_camera_device.createCaptureSession(
self.java_surface_list,
self._java_capture_session_java_callback,
_global_handler,
)
return self.preview_fbo.texture
def _prepare_preview_fbo(self, resolution):
self.preview_fbo = Fbo(size=resolution)
self.preview_fbo['resolution'] = [float(f) for f in resolution]
self.preview_fbo.shader.fs = """
#extension GL_OES_EGL_image_external : require
#ifdef GL_ES
precision highp float;
#endif
/* Outputs from the vertex shader */
varying vec4 frag_color;
varying vec2 tex_coord0;
/* uniform texture samplers */
uniform sampler2D texture0;
uniform samplerExternalOES texture1;
uniform vec2 resolution;
void main()
{
gl_FragColor = texture2D(texture1, tex_coord0);
}
"""
with self.preview_fbo:
Rectangle(size=resolution)
def _java_capture_session_callback(self, *args, **kwargs):
event = MyCaptureSessionCallback.camera_capture_event.toString()
logger.info("CALLBACK: capture event {}".format(event))
self.java_capture_session = MyCaptureSessionCallback.camera_capture_session
if event == "READY":
logger.info("Doing READY actions")
self.java_capture_session.setRepeatingRequest(self.java_capture_request.build(), None, None)
Clock.schedule_interval(self._update_preview, 0.)
def _update_preview(self, dt):
self.java_preview_surface_texture.updateTexImage()
self.preview_fbo.ask_update()
self.preview_fbo.draw()
self.output_texture = self.preview_fbo.texture
The code i want implement from camera_android to color blind
def grab_frame(self):
"""
Grab current frame (thread-safe, minimal overhead)
"""
with self._buflock:
if self._buffer is None:
return None
buf = self._buffer.tostring()
return buf
Related
I've recently stumbled upon this very useful code which I got from this link https://github.com/Douglas6/cputemp.
I've modified the code for clarity reasons and so "application" class is imported as an external class. I'm trying to understand why "set_temperature_callback" is called indefinitely in a loop. The interval of the loop is defined by NOTIFY_TIMEOUT. Here the interval is set to 1000 ms. I'm using this code as a peripheral for one of my Unity projects and it's working fine. I really appreciate any help!
import dbus
from advertisement import Advertisement
from service import Service, Characteristic, Descriptor
from gpiozero import CPUTemperature
#testing code
#------------------------------------------#
from application import Application
import inspect as i
import sys
def FindSource(method):
sys.stdout.write(i.getsource(method))
#------------------------------------------#
GATT_CHRC_IFACE = "org.bluez.GattCharacteristic1"
#the interval between sending messages, is counted in milliseconds, was 5000
NOTIFY_TIMEOUT = 1000
class ThermometerAdvertisement(Advertisement):
def __init__(self, index):
Advertisement.__init__(self, index, "peripheral")
self.add_local_name("Raspberry (thermometer)")
self.include_tx_power = True
class ThermometerService(Service):
#the service we're interested and is displayed in unity
THERMOMETER_SVC_UUID = "00000001-710e-4a5b-8d75-3e5b444bc3cf"
def __init__(self, index):
self.farenheit = True
Service.__init__(self, index, self.THERMOMETER_SVC_UUID, True)
self.add_characteristic(TempCharacteristic(self))
self.add_characteristic(UnitCharacteristic(self))
def is_farenheit(self):
return self.farenheit
def set_farenheit(self, farenheit):
self.farenheit = farenheit
class TempCharacteristic(Characteristic):
TEMP_CHARACTERISTIC_UUID = "00000002-710e-4a5b-8d75-3e5b444bc3cf"
def __init__(self, service):
self.notifying = False
Characteristic.__init__(
self, self.TEMP_CHARACTERISTIC_UUID,
["notify", "read"], service)
self.add_descriptor(TempDescriptor(self))
def get_temperature(self):
value = []
unit = "C"
# code here repeats
cpu = CPUTemperature()
temp = cpu.temperature
if self.service.is_farenheit():
temp = (temp * 1.8) + 32
unit = "F"
strtemp = str(round(temp, 1)) + " " + unit
for c in strtemp:
value.append(dbus.Byte(c.encode()))
return value
# the entire method will run on a loop when unity app hits "subscribe"
def set_temperature_callback(self):
if self.notifying:
print("running inside set_temperature_callback")
value = self.get_temperature()
self.PropertiesChanged(GATT_CHRC_IFACE, {"Value": value}, [])
return self.notifying
def StartNotify(self):
print("Start notify first time")
if self.notifying:
print("returning from start notify")
return
self.notifying = True
value = self.get_temperature()
self.PropertiesChanged(GATT_CHRC_IFACE, {"Value": value}, [])
self.add_timeout(NOTIFY_TIMEOUT, self.set_temperature_callback)
def StopNotify(self):
self.notifying = False
def ReadValue(self, options):
value = self.get_temperature()
return value
class TempDescriptor(Descriptor):
TEMP_DESCRIPTOR_UUID = "2901"
TEMP_DESCRIPTOR_VALUE = "CPU Temperature test"
def __init__(self, characteristic):
Descriptor.__init__(
self, self.TEMP_DESCRIPTOR_UUID,
["read"],
characteristic)
def ReadValue(self, options):
value = []
desc = self.TEMP_DESCRIPTOR_VALUE
for c in desc:
value.append(dbus.Byte(c.encode()))
return value
class UnitCharacteristic(Characteristic):
UNIT_CHARACTERISTIC_UUID = "00000003-710e-4a5b-8d75-3e5b444bc3cf"
def __init__(self, service):
Characteristic.__init__(
self, self.UNIT_CHARACTERISTIC_UUID,
["read", "write"], service)
self.add_descriptor(UnitDescriptor(self))
def WriteValue(self, value, options):
val = str(value[0]).upper()
if val == "C":
self.service.set_farenheit(False)
elif val == "F":
self.service.set_farenheit(True)
def ReadValue(self, options):
value = []
if self.service.is_farenheit(): val = "F"
else: val = "C"
value.append(dbus.Byte(val.encode()))
return value
class UnitDescriptor(Descriptor):
UNIT_DESCRIPTOR_UUID = "2901"
UNIT_DESCRIPTOR_VALUE = "Temperature Units (F or C)"
def __init__(self, characteristic):
Descriptor.__init__(
self, self.UNIT_DESCRIPTOR_UUID,
["read"],
characteristic)
def ReadValue(self, options):
value = []
desc = self.UNIT_DESCRIPTOR_VALUE
for c in desc:
value.append(dbus.Byte(c.encode()))
return value
app = Application()
app.add_service(ThermometerService(0))
app.register()
adv = ThermometerAdvertisement(0)
adv.register()
I have the following two classes:
class QPolygonModel(QtGui.QPolygon):
_idx = None
_selected = None
def __init__(self, idx, polygon: QtGui.QPolygon = None):
# Call default constructor
if polygon is None:
super().__init__()
# Call copy constructor
else:
super().__init__(polygon)
self._idx = idx
self._selected = False
#property
def idx(self):
return self._idx
#property
def is_selected(self):
return self._selected
#is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
This is a custom polygon class that inherits from QPolygon. Objects of this class are stored in a list in the "Scene" class:
class ImageAnnotatorState:
points = None
radius = None
image = None
polygons = None
_finished = None
multiselect = None
def __init__(self, image):
super().__init__()
self.points = QtGui.QPolygon()
self.radius = 8
self.image = image
self.polygons = self._init_polygons()
self.is_finished = False
self.multiselect = False
def _init_polygons(self):
result = []
for annotation in self.image.annotations:
polyline = QPolygonModel(annotation.get_id())
for point in annotation.points:
q_point = QPoint(point.x, point.y)
polyline.append(q_point)
result.append(polyline)
return result
#property
def is_finished(self):
return self._finished
#is_finished.setter
def is_finished(self, flag):
self._finished = flag
Now for the purpose of creating an undo function, I need to create a deepcopy of this scene class so I can store the state that was active before a scene change was made.
So in a QDialog form, I try to do the following:
class ImageAnnotator(QDialog):
_state = None
_previous_state = None
def __init__(self, image):
super().__init__()
self._state = ImageAnnotatorState(image)
self._previous_state = copy.deepcopy(self._state)
self.show()
The deepcopy call here fails with the following exception:
SystemError: attempt to pickle unknown type 'QPolygonModel'
What am I doing wrong?
EDIT:
Reproducible example:
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QApplication
import copy
import sys
class Test(QtGui.QPolygon):
idx = None
def __init__(self, z = None):
if z is None:
super().__init__()
else:
super().__init__(z)
class State:
test = None
def __init__(self):
self.test = [Test(), Test()]
print(self.test)
class Main(QDialog):
state = None
prev = None
def __init__(self):
super().__init__()
self.state = State()
prev = copy.deepcopy(self.state)
print(prev)
app = QApplication(sys.argv)
Main()
It seems that it is a bug similar to the one that ekhumoro points out in this answer. A workaround is to implement the __deepcopy__ method.
On the other hand if you want to set the default value in the case of QPolygon do not use None but an empty QPolygon.
With the above, I have implemented the following:
import copy
import random
from PyQt5 import QtCore, QtGui
class QPolygonModel(QtGui.QPolygon):
def __init__(self, idx, polygon=QtGui.QPolygon()):
super().__init__(polygon)
self._idx = idx
self._selected = False
#property
def idx(self):
return self._idx
#property
def is_selected(self):
return self._selected
#is_selected.setter
def is_selected(self, flag):
self._selected = flag
def get_points(self):
res = []
for i in range(0, self.size()):
res.append(self.point(i))
return res
# https://stackoverflow.com/a/10622689
def __deepcopy__(self, memo):
o = QPolygonModel(self.idx)
o.__dict__.update(self.__dict__)
ba = QtCore.QByteArray()
stream_w = QtCore.QDataStream(ba, QtCore.QIODevice.WriteOnly)
stream_w << self
stream_r = QtCore.QDataStream(ba, QtCore.QIODevice.ReadOnly)
stream_r >> o
return o
class State:
def __init__(self):
self.polylines = []
for _ in range(4):
poly = QtGui.QPolygon(
[QtCore.QPoint(*random.sample(range(10), 2)) for _ in range(4)]
)
polyline = QPolygonModel(random.randint(0, 10), poly)
self.polylines.append(polyline)
if __name__ == "__main__":
curr = State()
prev = copy.deepcopy(curr)
assert len(curr.polylines) == len(prev.polylines)
for polyline1, polyline2 in zip(curr.polylines, prev.polylines):
assert id(polyline1) != id(polyline2)
assert polyline1.size() == polyline2.size()
assert polyline1.is_selected == polyline2.is_selected
assert polyline1.idx == polyline2.idx
for i, j in zip(range(polyline1.size()), range(polyline2.size())):
assert polyline1.point(i) == polyline2.point(j)
I am using Bluez 5.50 example-advertisement to work with a custom server that I edited from the example-gatt-server. It works just fine however I want to reduce the advertising interval i.e. increase the advertisement frequency.
Most help available online is using the hcitool for example here. I read the details about the advertisement packet and it should be possible to set the interval to a described vale as per the supplement to Bluetooth core specifications described here. However, when I try to add it to the properties of the advertisement there is a parsing error. I am unsure how to go about this because of my unfamiliarity with python and dbus.
I am attaching my code for the advertisement below. I added the set_advInterval section where it was needed along the code.
`#!/usr/bin/python
from __future__ import print_function
import dbus
import dbus.exceptions
import dbus.mainloop.glib
import dbus.service
import array
try:
from gi.repository import GObject # python3
except ImportError:
import gobject as GObject # python2
from random import randint
mainloop = None
BLUEZ_SERVICE_NAME = 'org.bluez'
LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
class InvalidArgsException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
class NotSupportedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.NotSupported'
class NotPermittedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.NotPermitted'
class InvalidValueLengthException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.InvalidValueLength'
class FailedException(dbus.exceptions.DBusException):
_dbus_error_name = 'org.bluez.Error.Failed'
class Advertisement(dbus.service.Object):
PATH_BASE = '/org/bluez/example/advertisement'
def __init__(self, bus, index, advertising_type):
self.path = self.PATH_BASE + str(index)
self.bus = bus
self.ad_type = advertising_type
self.service_uuids = None
self.manufacturer_data = None
self.solicit_uuids = None
self.service_data = None
self.local_name = None
self.include_tx_power = None
self.data = None
self.advInterval=None
dbus.service.Object.__init__(self, bus, self.path)
def get_properties(self):
properties = dict()
properties['Type'] = self.ad_type
if self.service_uuids is not None:
properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
signature='s')
if self.solicit_uuids is not None:
properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
signature='s')
if self.manufacturer_data is not None:
properties['ManufacturerData'] = dbus.Dictionary(
self.manufacturer_data, signature='qv')
if self.service_data is not None:
properties['ServiceData'] = dbus.Dictionary(self.service_data,
signature='sv')
if self.local_name is not None:
properties['LocalName'] = dbus.String(self.local_name)
if self.include_tx_power is not None:
properties['IncludeTxPower'] = dbus.Boolean(self.include_tx_power)
if self.advInterval is not None:
properties['Interval']=dbus.Uint16(self.advInterval)
if self.data is not None:
properties['Data'] = dbus.Dictionary(
self.data, signature='yv')
return {LE_ADVERTISEMENT_IFACE: properties}
def get_path(self):
return dbus.ObjectPath(self.path)
def set_advInterval(self,interval):
self.advInterval=dbus.UInt16(interval)
def add_service_uuid(self, uuid):
if not self.service_uuids:
self.service_uuids = []
self.service_uuids.append(uuid)
def add_solicit_uuid(self, uuid):
if not self.solicit_uuids:
self.solicit_uuids = []
self.solicit_uuids.append(uuid)
def add_manufacturer_data(self, manuf_code, data):
if not self.manufacturer_data:
self.manufacturer_data = dbus.Dictionary({}, signature='qv')
self.manufacturer_data[manuf_code] = dbus.Array(data, signature='y')
def add_service_data(self, uuid, data):
if not self.service_data:
self.service_data = dbus.Dictionary({}, signature='sv')
self.service_data[uuid] = dbus.Array(data, signature='y')
def add_local_name(self, name):
if not self.local_name:
self.local_name = ""
self.local_name = dbus.String(name)
def add_data(self, ad_type, data):
if not self.data:
self.data = dbus.Dictionary({}, signature='yv')
self.data[ad_type] = dbus.Array(data, signature='y')
#dbus.service.method(DBUS_PROP_IFACE,
in_signature='s',
out_signature='a{sv}')
def GetAll(self, interface):
print('GetAll')
if interface != LE_ADVERTISEMENT_IFACE:
raise InvalidArgsException()
print('returning props')
return self.get_properties()[LE_ADVERTISEMENT_IFACE]
#dbus.service.method(LE_ADVERTISEMENT_IFACE,
in_signature='',
out_signature='')
def Release(self):
print('%s: Released!' % self.path)
class TestAdvertisement(Advertisement):
def __init__(self, bus, index):
Advertisement.__init__(self, bus, index, 'per
ipheral')
self.add_service_uuid('180F')
self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
self.add_service_data('9999', [0x00, 0x01, 0x02, 0x03, 0x04])
self.add_local_name('TestAdvertisement')
self.set_advInterval(0.2)
self.include_tx_power = True
self.add_data(0x26, [0x01, 0x01, 0x00])
def register_ad_cb():
print('Advertisement registered')
def register_ad_error_cb(error):
print('Failed to register advertisement: ' + str(error))
mainloop.quit()
def find_adapter(bus):
remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
DBUS_OM_IFACE)
objects = remote_om.GetManagedObjects()
for o, props in objects.items():
if LE_ADVERTISING_MANAGER_IFACE in props:
return o
return None
def main():
global mainloop
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
adapter = find_adapter(bus)
if not adapter:
print('LEAdvertisingManager1 interface not found')
return
adapter_props = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
"org.freedesktop.DBus.Properties");
adapter_props.Set("org.bluez.Adapter1", "Powered", dbus.Boolean(1))
ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
LE_ADVERTISING_MANAGER_IFACE)
test_advertisement = TestAdvertisement(bus, 0)
mainloop = GObject.MainLoop()
ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
reply_handler=register_ad_cb,
error_handler=register_ad_error_cb)
mainloop.run()
if __name__ == '__main__':
main()
`
According to https://kernel.googlesource.com/pub/scm/bluetooth/bluez/+/5.50/doc/advertising-api.txt the advertising interface api does not have property "Interval". Therefore we cant set the adv interval via this interface.
class MapFrameBase(wx.Frame):
"""!Base class for map display window
Derived class must use statusbarManager or override
GetProperty, SetProperty and HasProperty methods.
If derived class enables and disables auto-rendering,
it should override IsAutoRendered method.
"""
def __init__(self, parent = None, id = wx.ID_ANY, title = None,
style = wx.DEFAULT_FRAME_STYLE, toolbars = None,
Map = None, auimgr = None, name = None, **kwargs):
"""!
#param toolbars array of activated toolbars, e.g. ['map', 'digit']
#param Map instance of render.Map
#param auimgs AUI manager
#param name frame name
#param kwargs wx.Frame attributes
"""
self.Map = Map # instance of render.Map
self.parent = parent
wx.Frame.__init__(self, parent, id, title, style = style, name = name, **kwargs)
# available cursors
self.cursors = {
# default: cross
# "default" : wx.StockCursor(wx.CURSOR_DEFAULT),
"default" : wx.StockCursor(wx.CURSOR_ARROW),
"cross" : wx.StockCursor(wx.CURSOR_CROSS),
"hand" : wx.StockCursor(wx.CURSOR_HAND),
"pencil" : wx.StockCursor(wx.CURSOR_PENCIL),
"sizenwse": wx.StockCursor(wx.CURSOR_SIZENWSE)
}
#
# set the size & system icon
#
self.SetClientSize(self.GetSize())
self.iconsize = (16, 16)
self.SetIcon(wx.Icon(os.path.join(globalvar.ETCICONDIR, 'grass_map.ico'), wx.BITMAP_TYPE_ICO))
# toolbars
self.toolbars = {}
#
# Fancy gui
#
self._mgr = wx.aui.AuiManager(self)
def _initMap(self, map):
"""!Initialize map display, set dimensions and map region
"""
if not grass.find_program('g.region', ['--help']):
sys.exit(_("GRASS module '%s' not found. Unable to start map "
"display window.") % 'g.region')
self.width, self.height = self.GetClientSize()
Debug.msg(2, "MapFrame._initMap():")
map.ChangeMapSize(self.GetClientSize())
map.region = map.GetRegion() # g.region -upgc
# self.Map.SetRegion() # adjust region to match display window
def SetProperty(self, name, value):
"""!Sets property"""
self.statusbarManager.SetProperty(name, value)
def GetProperty(self, name):
"""!Returns property"""
return self.statusbarManager.GetProperty(name)
def HasProperty(self, name):
"""!Checks whether object has property"""
return self.statusbarManager.HasProperty(name)
def GetPPM(self):
"""! Get pixel per meter
#todo now computed every time, is it necessary?
#todo enable user to specify ppm (and store it in UserSettings)
"""
# TODO: need to be fixed...
### screen X region problem
### user should specify ppm
dc = wx.ScreenDC()
dpSizePx = wx.DisplaySize() # display size in pixels
dpSizeMM = wx.DisplaySizeMM() # display size in mm (system)
dpSizeIn = (dpSizeMM[0] / 25.4, dpSizeMM[1] / 25.4) # inches
sysPpi = dc.GetPPI()
comPpi = (dpSizePx[0] / dpSizeIn[0],
dpSizePx[1] / dpSizeIn[1])
ppi = comPpi # pixel per inch
ppm = ((ppi[0] / 2.54) * 100, # pixel per meter
(ppi[1] / 2.54) * 100)
Debug.msg(4, "MapFrameBase.GetPPM(): size: px=%d,%d mm=%f,%f "
"in=%f,%f ppi: sys=%d,%d com=%d,%d; ppm=%f,%f" % \
(dpSizePx[0], dpSizePx[1], dpSizeMM[0], dpSizeMM[1],
dpSizeIn[0], dpSizeIn[1],
sysPpi[0], sysPpi[1], comPpi[0], comPpi[1],
ppm[0], ppm[1]))
return ppm
def SetMapScale(self, value, map = None):
"""! Set current map scale
#param value scale value (n if scale is 1:n)
#param map Map instance (if none self.Map is used)
"""
if not map:
map = self.Map
region = self.Map.region
dEW = value * (region['cols'] / self.GetPPM()[0])
dNS = value * (region['rows'] / self.GetPPM()[1])
region['n'] = region['center_northing'] + dNS / 2.
region['s'] = region['center_northing'] - dNS / 2.
region['w'] = region['center_easting'] - dEW / 2.
region['e'] = region['center_easting'] + dEW / 2.
# add to zoom history
self.GetWindow().ZoomHistory(region['n'], region['s'],
region['e'], region['w'])
def GetMapScale(self, map = None):
"""! Get current map scale
#param map Map instance (if none self.Map is used)
"""
if not map:
map = self.Map
region = map.region
ppm = self.GetPPM()
heightCm = region['rows'] / ppm[1] * 100
widthCm = region['cols'] / ppm[0] * 100
Debug.msg(4, "MapFrame.GetMapScale(): width_cm=%f, height_cm=%f" %
(widthCm, heightCm))
xscale = (region['e'] - region['w']) / (region['cols'] / ppm[0])
yscale = (region['n'] - region['s']) / (region['rows'] / ppm[1])
scale = (xscale + yscale) / 2.
Debug.msg(3, "MapFrame.GetMapScale(): xscale=%f, yscale=%f -> scale=%f" % \
(xscale, yscale, scale))
return scale
def GetProgressBar(self):
"""!Returns progress bar
Progress bar can be used by other classes.
"""
return self.statusbarManager.GetProgressBar()
def GetMap(self):
"""!Returns current Map instance
"""
return self.Map
def GetWindow(self):
"""!Get map window"""
return self.MapWindow
def GetMapToolbar(self):
"""!Returns toolbar with zooming tools"""
raise NotImplementedError()
def GetToolbar(self, name):
"""!Returns toolbar if exists else None.
Toolbars dictionary contains currently used toolbars only.
"""
if name in self.toolbars:
return self.toolbars[name]
return None
def StatusbarUpdate(self):
"""!Update statusbar content"""
self.statusbarManager.Update()
def IsAutoRendered(self):
"""!Check if auto-rendering is enabled"""
return self.GetProperty('render')
def CoordinatesChanged(self):
"""!Shows current coordinates on statusbar.
Used in BufferedWindow to report change of map coordinates (under mouse cursor).
"""
self.statusbarManager.ShowItem('coordinates')
def StatusbarReposition(self):
"""!Reposition items in statusbar"""
self.statusbarManager.Reposition()
def StatusbarEnableLongHelp(self, enable = True):
"""!Enable/disable toolbars long help"""
for toolbar in self.toolbars.itervalues():
toolbar.EnableLongHelp(enable)
def IsStandalone(self):
"""!Check if Map display is standalone"""
raise NotImplementedError("IsStandalone")
def OnRender(self, event):
"""!Re-render map composition (each map layer)
"""
raise NotImplementedError("OnRender")
def OnDraw(self, event):
"""!Re-display current map composition
"""
self.MapWindow.UpdateMap(render = False)
def OnErase(self, event):
"""!Erase the canvas
"""
self.MapWindow.EraseMap()
def OnZoomIn(self, event):
"""!Zoom in the map.
Set mouse cursor, zoombox attributes, and zoom direction
"""
toolbar = self.GetMapToolbar()
self._switchTool(toolbar, event)
win = self.GetWindow()
self._prepareZoom(mapWindow = win, zoomType = 1)
def OnZoomOut(self, event):
"""!Zoom out the map.
Set mouse cursor, zoombox attributes, and zoom direction
"""
toolbar = self.GetMapToolbar()
self._switchTool(toolbar, event)
win = self.GetWindow()
self._prepareZoom(mapWindow = win, zoomType = -1)
def _prepareZoom(self, mapWindow, zoomType):
"""!Prepares MapWindow for zoom, toggles toolbar
#param mapWindow MapWindow to prepare
#param zoomType 1 for zoom in, -1 for zoom out
"""
mapWindow.mouse['use'] = "zoom"
mapWindow.mouse['box'] = "box"
mapWindow.zoomtype = zoomType
mapWindow.pen = wx.Pen(colour = 'Red', width = 2, style = wx.SHORT_DASH)
# change the cursor
mapWindow.SetCursor(self.cursors["cross"])
def _switchTool(self, toolbar, event):
"""!Helper function to switch tools"""
if toolbar:
toolbar.OnTool(event)
toolbar.action['desc'] = ''
def OnPointer(self, event):
"""!Sets mouse mode to pointer."""
self.MapWindow.mouse['use'] = 'pointer'
def OnPan(self, event):
"""!Panning, set mouse to drag
"""
toolbar = self.GetMapToolbar()
self._switchTool(toolbar, event)
win = self.GetWindow()
self._preparePan(mapWindow = win)
def _preparePan(self, mapWindow):
"""!Prepares MapWindow for pan, toggles toolbar
#param mapWindow MapWindow to prepare
"""
mapWindow.mouse['use'] = "pan"
mapWindow.mouse['box'] = "pan"
mapWindow.zoomtype = 0
# change the cursor
mapWindow.SetCursor(self.cursors["hand"])
def OnZoomBack(self, event):
"""!Zoom last (previously stored position)
"""
self.MapWindow.ZoomBack()
def OnZoomToMap(self, event):
"""!
Set display extents to match selected raster (including NULLs)
or vector map.
"""
self.MapWindow.ZoomToMap(layers = self.Map.GetListOfLayers())
def OnZoomToWind(self, event):
"""!Set display geometry to match computational region
settings (set with g.region)
"""
self.MapWindow.ZoomToWind()
def OnZoomToDefault(self, event):
"""!Set display geometry to match default region settings
"""
self.MapWindow.ZoomToDefault()
When I try to start up GrassRaPlat in command prompt, appears this as follow:
GRASS 6.4.3RC2 (AMC):~ > Traceback (most recent call last):
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/lmgr/frame.py", line 196, in init
self.NewDisplay(show = False)
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/lmgr/frame.py", line 1463, in NewDisplay
auimgr = self._auimgr, showMapDisplay = show)
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/lmgr/layertree.py", line 156, in init
Map = self.Map, auimgr = self.auimgr)
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/mapdisp/frame.py", line 123, in init
self.statusbarManager.Update()
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/mapdisp/statusbar.py", line 217, in Update
item.Update()
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/mapdisp/statusbar.py", line 508, in Update
scale = self.mapFrame.GetMapScale()
File "/usr/local/src/grass-6.4.3RC2/dist.i686-pc-linux-gnu/etc/wxpython/gui_core/mapdisp.py", line 168, in GetMapScale
heightCm = region['rows'] / ppm[1] * 100
KeyError: 'rows'
Can you tell me please where is my mistake?
Thank you in advance.
I want to find contours in an image and further process them e.g. drawing them on the image.
To do that I have two functions running in different threads:
storage = cv.CreateMemStorage(0)
contour = cv.FindContours(inData.content, storage, cv.CV_RETR_EXTERNAL, cv.CV_CHAIN_APPROX_SIMPLE)
and
while contours:
bound_rect = cv.BoundingRect(list(contours))
contours = contours.h_next()
pt1 = (bound_rect[0], bound_rect[1])
pt2 = (bound_rect[0] + bound_rect[2], bound_rect[1] + bound_rect[3])
cv.Rectangle(inImg.content, pt1, pt2, cv.CV_RGB(255,0,0), 1)
Each function runs in a loop processing one image after the other.
When a function is done it puts the image in a buffer from which the other function can get it.
This works except that in the result the contours are drawn in the image one or two images before their corresponding image.
I think this has something to do with the storage of OpenCV but I don't understand why the storage is needed and what it does
EDIT Here is some more code:
My program is meant to be a node based image analasys software.
This is how the node graph of my current code looks like:
|---------| |--------|
|-----| |-----|------>|Threshold|--->|Contours|--->|-------------| |------|
|Input|--->|Split| |---------| |--------| |Draw Contours|--->|Output|
|-----| |-----|----------------------------------->|-------------| |------|
This is the class from which all nodes derive:
from Buffer import Buffer
from threading import Thread
from Data import Data
class Node(Thread):
def __init__(self, inputbuffers, outputbuffers):
Thread.__init__(self)
self.inputbuffers = inputbuffers
self.outputbuffers = outputbuffers
def getInputBuffer(self, index):
return self.inputbuffers[index]
def getOutputBuffer(self, index):
return self.outputbuffers[index]
def _getContents(self, bufferArray):
out = []
for bufferToGet in bufferArray:
if bufferToGet and bufferToGet.data:
out.append(bufferToGet.data)
for bufferToGet in bufferArray:
bufferToGet.data = None
return out
def _allInputsPresent(self):
for bufferToChk in self.inputbuffers:
if not bufferToChk.data:
return False
return True
def _allOutputsEmpty(self):
for bufferToChk in self.outputbuffers:
if bufferToChk.data != None:
return False
return True
def _applyOutputs(self, output):
for i in range(len(output)):
if self.outputbuffers[i]:
self.outputbuffers[i].setData(output[i])
def run(self):
#Thread loop <------------------------------------
while True:
while not self._allInputsPresent(): pass
inputs = self._getContents(self.inputbuffers)
output = [None]*len(self.outputbuffers)
self.process(inputs, output)
while not self._allOutputsEmpty(): pass
self._applyOutputs(output)
def process(self, inputs, outputs):
'''
inputs: array of Data objects
outputs: array of Data objects
'''
pass
The nodes pass around these Data objects:
class Data(object):
def __init__(self, content = None, time = None, error = None, number = -1):
self.content = content #Here the actual data is stored. Mostly images
self.time = time #Not used yet
self.error = error #Not used yet
self.number = number #Used to see if the correct data is put together
This are the nodes:
from Node import Node
from Data import Data
import copy
import cv
class TemplateNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(type(self), self).__init__(inputbuffers, outputbuffers)
def process(self, inputs, outputs):
inData = inputs[0]
#Do something with the content e.g.
#cv.Smooth(inData.content, inData.content, cv.CV_GAUSSIAN, 11, 11)
outputs[0] = inData
class InputNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(InputNode, self).__init__(inputbuffers, outputbuffers)
self.capture = cv.CaptureFromFile("video.avi")
self.counter = 0
def process(self, inputs, outputs):
image = cv.QueryFrame(self.capture)
if image:
font = cv.InitFont(cv.CV_FONT_HERSHEY_SIMPLEX, 1, 1, 0, 3, 8)
x = 30
y = 50
cv.PutText(image, str(self.counter), (x,y), font, 255)
outputs[0] = Data(image,None,None,self.counter)
self.counter = self.counter+1
class OutputNode(Node):
def __init__(self, inputbuffers, outputbuffers, name):
super(type(self), self).__init__(inputbuffers, outputbuffers)
self.name = name
def process(self, inputs, outputs):
if type(inputs[0].content) == cv.iplimage:
cv.ShowImage(self.name, inputs[0].content)
cv.WaitKey()
class ThresholdNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(type(self), self).__init__(inputbuffers, outputbuffers)
def process(self, inputs, outputs):
inData = inputs[0]
inimg = cv.CreateImage(cv.GetSize(inData.content), cv.IPL_DEPTH_8U, 1);
cv.CvtColor(inData.content, inimg, cv.CV_BGR2GRAY)
outImg = cv.CreateImage(cv.GetSize(inimg), cv.IPL_DEPTH_8U, 1);
cv.Threshold(inimg, outImg, 70, 255, cv.CV_THRESH_BINARY_INV);
inData.content = outImg
outputs[0] = inData
class SplitNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(type(self), self).__init__(inputbuffers, outputbuffers)
def process(self, inputs, outputs):
inData = inputs[0]
if type(inData.content) == cv.iplimage:
imagecpy = cv.CloneImage(inData.content)
outputs[1] = Data(imagecpy, copy.copy(inData.time), copy.copy(inData.error), copy.copy(inData.number))
else:
outputs[1] = copy.deepcopy(inData)
print
class ContoursNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(type(self), self).__init__(inputbuffers, outputbuffers)
def process(self, inputs, outputs):
inData = inputs[0]
storage = cv.CreateMemStorage(0)
contours = cv.FindContours(inData.content, storage, cv.CV_RETR_EXTERNAL, cv.CV_CHAIN_APPROX_SIMPLE)
contoursArr = []
while contours:
points = []
for (x,y) in contours:
points.append((x,y))
contoursArr.append(points)
contours = contours.h_next()
outputs[0] = Data(contoursArr, inData.time, inData.error, inData.number)
pass
class DrawContoursNode(Node):
def __init__(self, inputbuffers, outputbuffers):
super(type(self), self).__init__(inputbuffers, outputbuffers)
def process(self, inputs, outputs):
inImg = inputs[0]
contours = inputs[1].content
print "Image start"
for cont in contours:
for (x,y) in cont:
cv.Circle(inImg.content, (x,y), 2, cv.CV_RGB(255, 0, 0))
print "Image end"
outputs[0] = inImg
This is the main function. Here all the nodes and buffers are created.
from NodeImpls import *
from Buffer import Buffer
buffer1 = Buffer()
buffer2 = Buffer()
buffer3 = Buffer()
buffer4 = Buffer()
buffer5 = Buffer()
buffer6 = Buffer()
innode = InputNode([], [buffer1])
split = SplitNode([buffer1], [buffer2, buffer3])
thresh = ThresholdNode([buffer3], [buffer4])
contours = ContoursNode([buffer4], [buffer5])
drawc = DrawContoursNode([buffer2, buffer5],[buffer6])
outnode = OutputNode([buffer6], [], "out1")
innode.start()
split.start()
thresh.start()
contours.start()
drawc.start()
outnode.start()
while True:
pass
The buffer:
class Buffer(object):
def __init__(self):
self.data = None
def setData(self, data):
self.data = data
def getData(self):
return self.data
I think this has something to do with the storage of OpenCV but I don't understand why the storage is needed and what it does
Storage is just a place to keep the results. OpenCV is a C++ library, and relies on manual memory allocation, C++ style. Python bindings are just a thin wrapper around it, and are not very pythonic. That's why you have to allocate storage manually, like if you did it in C or in C++.
I have two functions running in different threads
...
This works except that in the result the contours are drawn in the image one or two images before their corresponding image.
I assume your threads are not properly synchronized. This problem is not likely to be related to OpenCV, but to what functions you have, what data they use and pass around, and how you share the data between them.
In short, please post your code where you create threads and call these functions, as well where inImg, inData, contour, contours and storage are accessed or modified.