joshdoe / gst-plugins-vision

GStreamer plugins related to the field of machine vision
Other
134 stars 50 forks source link

Setting pylonsrc properties with PFS file #46

Closed mrstecklo closed 3 years ago

joshdoe commented 3 years ago

@mrstecklo sorry missed this over the holidays. I've partially looked at this but haven't tested it yet.

joshdoe commented 3 years ago

In gst-launch some basic testing works ok, however I'm having issues with one of my applications. The way my GUI app uses a given source plugin, it enumerates the properties and creates widgets (such as spinboxes with min/max values). Take camera, it has a min/max of 0/100, however the default value is 9999. My GUI creates a spinbox with min/max of 0/100, tries to set 9999, but it's already limited by the max value, so becomes 100. When I try to open the camera after clicking through the configuration dialog, it tries to set the camera property to 100. This same issue is repeated for balance, color and transformation properties.

I realize this behavior wasn't introduced by you, but came from the original gst-pylonsrc project. When installing the properties, the default values passed to g_param_spec_double() don't match the real default values assigned to class instance member variables.

I also can't recall having a similar issue in any other element I've used before, in other words setting a property to it's default should be the same as leaving it at it's default.

Since you've been in this code more than I, perhaps you can suggest a fix, though I realize this is beyond the scope of this PR since this issue has existed for a while.

mrstecklo commented 3 years ago

@joshdoe generally using config-file or ignore-defaults should have solved your problem. The trick is to read properties from plugin after they have synchronized with device. As far as I understand, GStreamer manages plugin in this way: 1) calls gst_pylonsrc_init which sets all properties in plugin struct to predefined values 2) calls gst_pylonsrc_set_property for each property in pipeline string (if gst-launch-1.0 or gst_parse_launch() is used) 3) calls gst_pylonsrc_start which connects to device. And if config-file or ignore-defaults are used, it reads all features from camera to plugin struct. 4) calls gst_pylonsrc_create for each frame. Here all properties from plugin struct are actually applied to device. 6) calls gst_pylonsrc_set_property each time you adjust a widget

I think your app is reading properties after step 1 but before step 3 where plugin struct is synchronized with device (probably you don't have step 2 at all). I'm confused though you manage to read properties this way with other plugins. As start function is implied to open resources and therefore device state can't be acquired before a call to start. start is probably called when moving plugin from STOP state to READY.

As for defaults specified in g_param_spec_double they are not applied in the step 2. I assume that those can be applied if a property is mentioned in a pipeline string without parameter. That is these pipelines should be equivalent (I haven't tried this): gst-launch-1.0 pylonsrc camera=0 ... gst-launch-1.0 pylonsrc camera ... Though g_param_spec_boolean does not work this way, so I can be wrong.

Property values that are applied in gst_pylonsrc_init differ from defaults in g_param_spec_double because they were used to recognize if properties have been specified in step 2 or not. So the defaults in init are intentionally invalid. Now that I have added flags to explicitly state that given property was set, these invalid values are no longer needed and can be set to the same values that those in g_param_spec_double. If your app OK with these defaults, then this will be enough. If you need to get the actual state of the camera upon initializing GUI, you should move a plugin to READY state first.

Most properties are applied in gst_pylonsrc_create. So they can be adjusted in a PLAYING state (as long as camera itself does not block them from modifying). But some of them are only applied in gst_pylonsrc_start. Those are camera, reset, binningh, binningv, width, height, config-file and ignore-defaults. In order to modify them you need to invoke gst_pylonsrc_start, i.e. move a plugin from READY to STOP and back to READY.

A good modification would be applying all properties in gst_pylonsrc_start in addition to gst_pylonsrc_create. So that a plugin is fully initialized after calling gst_pylonsrc_start.

mrstecklo commented 3 years ago

The current state of a plugin is pretty satisfactory for me right now. But if you wish, I can implement the changes I mentioned. By the way I've applied some of the ideas that @AB-Eskild suggests in his pull request in my corrupt-patch branch as I'm using GigE camera at high FPS and it sometimes fails to grab frame.

joshdoe commented 3 years ago

You are correct I read properties right after Step 1.

One issue I see with your description of the process, is that some properties, namely for pylonsrc, the camera property, have to be set before syncing the other properties from the camera. There's no generic way for my application to know which properties to set before going to READY.

All this being said, I've changed my application to only set properties that have been explicitly changed by the user (i.e. the framerate spin box was set to a non-zero number), and now I'm able to use the plugin in my application, so I'm happy.

joshdoe commented 3 years ago

The current state of a plugin is pretty satisfactory for me right now. But if you wish, I can implement the changes I mentioned. By the way I've applied some of the ideas that @AB-Eskild suggests in his pull request in my corrupt-patch branch as I'm using GigE camera at high FPS and it sometimes fails to grab frame.

Would you mind giving your feedback on #32? Your branch implements fewer properties, so wondering if you just found those were the only properties you needed to change to avoid failures. Thanks.

joshdoe commented 3 years ago

I've tested the PFS loading, my app saves presets so I can quickly switch between two different PFS files. I'll merge shortly.

On an unrelated note, I notice that some of my camera's feature names are different, like GainRaw instead of Gain, ExposureTimeAbs instead of ExposureTime. Not sure if this is directly from the camera, or because I'm using Pylon 5. Also, my app allows real time changes of properties, so I've had to change set_property() to set exposure, etc if the device is connected, I'll push this as a commit or PR.

joshdoe commented 3 years ago

@mrstecklo seeing an issue now, before this PR, in set_property(), I added gst_pylonsrc_set_exposure_gain_level() to PROP_EXPOSURE if the device is connected, this allowed changing exposure while the camera was open. With this PR, I see that you now set exposure and other properties in create(), however when I change exposure with the camera open, all I see is that ExposureAuto is disabled, but the new exposure value doesn't get set. I'm trying to understand this PR better to see what's going wrong, but thought I'd mention it here.

See #47 for a suggested fix and discussion.

mrstecklo commented 3 years ago

On an unrelated note, I notice that some of my camera's feature names are different, like GainRaw instead of Gain, ExposureTimeAbs instead of ExposureTime. Not sure if this is directly from the camera, or because I'm using Pylon 5. Also, my app allows real time changes of properties, so I've had to change set_property() to set exposure, etc if the device is connected, I'll push this as a commit or PR.

I'm almost sure that ExposureTimeAbs and ExposureTime are equivalent. As I have two cameras one with AcquisitionFrameRate and the other with AcquisitionFrameRateAbs features. PylonViewer describes both of them identically. Though GainRaw and Gain are not the same. As far as I remember Gain is a logarithm of GainRaw Basler documentation on Gain. Basler states that AcquisitionFrameRateAbs is equivalent to AcquisitionFrameRate Basler states that ExposureTimeAbs is equivalent to ExposureTime

mrstecklo commented 3 years ago

With this PR, I see that you now set exposure and other properties in create()

Actually properties were being set in create before my modifications. See ef66205.

if (!src->acquisition_configured) {
  if (!gst_pylonsrc_configure_start_acquisition (src))
    goto error;
  src->acquisition_configured = TRUE;
}

The difference is there is now an array of flags for each property instead of a single src->acquisition_configured. And new flags are updated with set_property() while the old one could not be changed.