mabuchilab / Instrumental

Python-based instrumentation library from the Mabuchi Lab.
http://instrumental-lib.readthedocs.org/
GNU General Public License v3.0
121 stars 80 forks source link

[cameras.uc480] Unable to set gamma on Thorlabs DCx camera #69

Open fauxzor opened 5 years ago

fauxzor commented 5 years ago

Hello,

I've recently gotten a Thorlabs DCx (UC480) camera working with Instrumental. I can take data, adjust the ROI, change the gain, all that good stuff. However when I try to set the camera gamma I run into a problem.

Here's how I set up the camera:

from instrumental import instrument, list_instruments
avail = list_instruments()
# Output: [<ParamSet[UC480_Camera] model='C1284R13C' id=1 serial='4103363284'>]
mycam = instrument(avail[0])
# Output: <instrumental.drivers.cameras.uc480.UC480_Camera at 0xa5c99b0>

So far, so good, but running mycam.gamma gives me the following error:

Traceback (most recent call last):

  File "<ipython-input-5-ae67e1c3ad4e>", line 1, in <module>
    mycam.gamma

  File "C:\Anaconda2\lib\site-packages\instrumental\drivers\__init__.py", line 245, in __get__
    return self.get_value(obj)

  File "C:\Anaconda2\lib\site-packages\instrumental\drivers\__init__.py", line 255, in get_value
    instance.cached_val = self.conv_get(self.fget(obj))

  File "C:\Anaconda2\lib\site-packages\instrumental\drivers\cameras\uc480.py", line 989, in gamma
    return self._dev.Gamma(lib.GAMMA_CMD_GET) / 100.

AttributeError: 'Camera' object has no attribute 'Gamma'

Is adjusting the gamma of the DCx cameras supported by Instrumental? I couldn't find any previous issues relating to people being unable to do this so I suspect the problem is on my end.

For reasons, I am running 32 bit Python 2.7.15 on 64 bit Windows 10. I got Python to find the 32 bit uc480.dll required by stealing it from Thorlabs\Scientific Imaging\DCx Camera Support\OtherDrivers\LabVIEW\For_32-bit_LabVIEW\ and have had success with it so far except with the gamma parameter. Could it be that this is an old version of the library missing the functionality to control the gamma?

I would be glad to provide any additional information that would be helpful. Thank you for developing this package, it's perfect for what I need to do!

natezb commented 5 years ago

It looks like a typo on our end: that Gamma should instead be _Gamma. I'll push a change soon that fixes it. You'll then have to use the dev version from Github (if you aren't already using it), or you can edit the local files to add the fix yourself.

natezb commented 5 years ago

I just pushed a change that should fix this. Let me know whether or not it fixes your issue, and we can reopen this if needed!

And thanks for reporting!

fauxzor commented 5 years ago

Thank you for such a quick reply!

I installed the latest version from Github and I'm still getting a similar error:

Traceback (most recent call last):

  File "<ipython-input-8-657257b06618>", line 1, in <module>
    mycam.gamma

  File "C:\Anaconda2\lib\site-packages\instrumental_lib-0.6.dev0-py2.7.egg\instrumental\drivers\__init__.py", line 248, in __get__
    return self.get_value(obj)

  File "C:\Anaconda2\lib\site-packages\instrumental_lib-0.6.dev0-py2.7.egg\instrumental\drivers\__init__.py", line 258, in get_value
    instance.cached_val = self.conv_get(self.fget(obj))

  File "C:\Anaconda2\lib\site-packages\instrumental_lib-0.6.dev0-py2.7.egg\instrumental\drivers\cameras\uc480.py", line 989, in gamma
    return self._dev._Gamma(lib.GAMMA_CMD_GET) / 100.

AttributeError: 'Camera' object has no attribute '_Gamma'

I don't know if this is useful, but here's what I get from entering mycam versus mycam._dev:

mycam._dev
# Out[14]: <instrumental.drivers.cameras.uc480.Camera at 0xa3c3bd0>
mycam
# Out[15]: <instrumental.drivers.cameras.uc480.UC480_Camera at 0xa3c3df0>

Maybe it's asking the right question of the wrong device, or the wrong question of the right device?

EDIT: Okay, I think I figured out a fix. I don't know if this solution jives with the rest of your code, but replacing _Gamma with Gamma did the job. I can now query mycam.gamma and adjust it with mycam.gamma = X. I changed the definition in the Camera class to Gamma() and updated these references to self._dev._Gamma with self._dev.Gamma:

    @Facet(limits=(1.0, 10.0))
    def gamma(self):
        """The gamma correction value (1.0-10.0)"""
        return self._dev.Gamma(lib.GAMMA_CMD_GET) / 100.

    @gamma.setter
    def gamma(self, gamma):
        gamma_factor = int(round(gamma * 100))
        self._dev.Gamma(lib.GAMMA_CMD_SET, gamma_factor)

I only changed these two references to _Gamma, but they all looked consistent with each other before I made the change, so I don't know why Gamma works and _Gamma doesn't. Maybe there's another stray Gamma being expected somewhere?

natezb commented 5 years ago

Yeah, unfortunately I'm away from the lab right now and couldn't test my "fix" before pushing it. Did you remove the underscore from all locations, including in the definition on line 234?

fauxzor commented 5 years ago

Sorry for the delay. I was also out of the lab for the holidays. Happy New Year!

I did remove the underscore from line 234 and from the two functions I listed above (lines 989 and 994). That seemed to work on the first machine I tried it with, hence my "I figured it out!" post, but after trying it on another system I still have the same issue as previously:

AttributeError: 'Camera' object has no attribute '_Gamma'

Even if I rename _Gamma to Gamma on line 234 -- or rename it to some totally new name like Gammadev, keeping the new name consistent with the definitions on lines 234, 989, and 994 -- it still doesn't appear to be registered as a method of the Camera class. For example, I can start typing mycam._dev. and my editor shows me all of its bound methods (including 'Exposure' and 'Blacklevel' which both work if I type mycam._dev.Exposure or whatever) but _Gamma or anything else I rename it to doesn't show up. It's almost as if it doesn't 'see' the _Gamma one even if I change the name.

I did save an instrument when I previously did this fix, i.e. opened the camera with mycam = instrument('myDC'), so maybe that has something to do with it? I have no idea why but that is about the only difference between this second machine and the first one. I have been trying to use the more general list_instruments() approach to find/open the camera with this second machine, which has been working fine except for the reappearance of the gamma issue.

I had no issues setting the gamma once I got it working on my other computer so fortunately I don't think this issue runs very deep. Once I call the right method it works without issue. I'm just unsure how to replicate my earlier 'success.'

EDIT: This is another harebrained thought and may not be helpful. While my two systems are practically identical, of course they are not actually identical, and it is near-impossible to verify that I have the exact same configuration of Python on this other system as I do on the previously working one. I see that NiceLib is involved in handling the Camera class somehow. Could this perhaps be an issue related to not having correct versions of some of Instrumental's dependencies? In other words, _Gamma isn't 'registered' or 'seen' because whatever is responsible for those tasks isn't working properly?

I don't think this is the case because all the other methods (like Blacklevel and Exposure) do work and even if my Python installations are a little different, I installed Instrumental the exact same way on both machines. But what do I know? 🤷‍♀️

natezb commented 5 years ago

Happy New Year!

Sorry for the delay, I just got a chance to look at this again. Your fix (removing the underscore prefix on line 234) appears to me to be the correct solution. Doing that fixed the issue for me.

It does seem strange that you're having issues on another machine. My first thought was that the version of NiceLib could be the issue, but as you said, Blacklevel et al use the same machinery and are working fine for you.

What I did notice when playing around with this is that if there's an error when the Gamma method (or any of these other methods) is being created (e.g. due to a missing function in the header file), the error will be swallowed and the method simply won't be created and attached. So, it is possible that one of your computers is using an older version of the DLL which is lacking the is_Gamma function, and this prevents the Gamma method from being created successfully.

It might be best if you provided some logging information, e.g.

>>> from instrumental.log import log_to_file, DEBUG
>>> log_to_file('log.txt', level=DEBUG)
>>> import instrumental.drivers.cameras.uc480

and then upload the generated log file as a GitHub gist or something.