Skip to content

Instantly share code, notes, and snippets.

@rwb27
Last active April 27, 2023 15:09
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save rwb27/a23808e9f4008b48de95692a38ddaa08 to your computer and use it in GitHub Desktop.
Save rwb27/a23808e9f4008b48de95692a38ddaa08 to your computer and use it in GitHub Desktop.
Manually setting gain of raspberry pi camera from within python
from __future__ import print_function
import picamera
from picamera import mmal, mmalobj, exc
from picamera.mmalobj import to_rational
import time
MMAL_PARAMETER_ANALOG_GAIN = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x59
MMAL_PARAMETER_DIGITAL_GAIN = mmal.MMAL_PARAMETER_GROUP_CAMERA + 0x5A
def set_gain(camera, gain, value):
"""Set the analog gain of a PiCamera.
camera: the picamera.PiCamera() instance you are configuring
gain: either MMAL_PARAMETER_ANALOG_GAIN or MMAL_PARAMETER_DIGITAL_GAIN
value: a numeric value that can be converted to a rational number.
"""
if gain not in [MMAL_PARAMETER_ANALOG_GAIN, MMAL_PARAMETER_DIGITAL_GAIN]:
raise ValueError("The gain parameter was not valid")
ret = mmal.mmal_port_parameter_set_rational(cam._camera.control._port,
gain,
to_rational(value))
if ret == 4:
raise exc.PiCameraMMALError(ret, "Are you running the latest version of the userland libraries? Gain setting was introduced in late 2017.")
elif ret != 0:
raise exc.PiCameraMMALError(ret)
def set_analog_gain(camera, value):
"""Set the gain of a PiCamera object to a given value."""
set_gain(camera, MMAL_PARAMETER_ANALOG_GAIN, value)
def set_digital_gain(camera, value):
"""Set the digital gain of a PiCamera object to a given value."""
set_gain(camera, MMAL_PARAMETER_DIGITAL_GAIN, value)
if __name__ == "__main__":
with picamera.PiCamera() as cam:
cam.start_preview(fullscreen=False, window=(0,50,640,480))
time.sleep(2)
# fix the auto white balance gains at their current values
g = cam.awb_gains
cam.awb_mode = "off"
cam.awb_gains = g
# fix the shutter speed
cam.shutter_speed = cam.exposure_speed
print("Current a/d gains: {}, {}".format(cam.analog_gain, cam.digital_gain))
print("Attempting to set analogue gain to 1")
set_analog_gain(cam, 1)
print("Attempting to set digital gain to 1")
set_digital_gain(cam, 1)
# The old code is left in here in case it is a useful example...
#ret = mmal.mmal_port_parameter_set_rational(cam._camera.control._port,
# MMAL_PARAMETER_DIGITAL_GAIN,
# to_rational(1))
#print("Return code: {}".format(ret))
try:
while True:
print("Current a/d gains: {}, {}".format(cam.analog_gain, cam.digital_gain))
time.sleep(1)
except KeyboardInterrupt:
print("Stopping...")

Setting Pi camera analogue/digital gain from Python

This gist contains a very minimal Python script that shows how to set the analogue and digital gains of the Raspberry Pi camera module from within Python. It is a trivial thing, all the actual work was done by the developers of userland in particular 6by9, and of course the developers of picamera. It does require the latest version of the userland libraries in order to be able to send the gain-setting commands. These are not yet (as of January 2018) in the main Raspbian distribution, but it is not scary to build this yourself:

sudo apt-get install cmake
git clone https://github.com/raspberrypi/userland.git
cd userland
./buildme
sudo shutdown -r now

After you've got the latest version of the userland libraries, you should be able to run

python set_gain.py

This will open a preview window and then freeze the analogue and digital gains at 1. Look at the code to see how it's done - it is not complicated. To use this in your own code, just do:

import picamera
from set_picamera_gain import set_analog_gain, set_digital_gain

with picamera.PiCamera() as cam:
    set_analog_gain(cam, 2)
    set_digital_gain(cam, 1)

Hopefully I will get round to making a pull request to picamera - but of course that's only really useful once the userland code is updated in the distribution.

@rwb27
Copy link
Author

rwb27 commented Jan 29, 2018

NB I now have an open pull request which allows you to simply set PiCamera.analog_gain and PiCamera.digital_gain. The forked version of the library is a much neater solution than my work-around here.

@mysteriousHerb
Copy link

Hi Richard,
Line 21:

    ret = mmal.mmal_port_parameter_set_rational(cam._camera.control._port, 
                                                    gain,
                                                    to_rational(value))

Should the cam._camera.control.
be camera._camera.control. ?

@rwb27
Copy link
Author

rwb27 commented Feb 5, 2019

Thanks Tian - I'm sure you are right. However, I'm reccommending everyone use the forked version of the library (see the pull request link above). NB I've packaged it into a "wheel" file which should work on Py2 or Py3, if you go to the releases tab on my fork. That should make it super easy to use until it's pulled upstream :)

@dogadogan
Copy link

Hi Richard,
I looked at the pull request as you suggested but was not sure if it has the latest version. Which files should we download at to be able to use your method for fixing the gains? Thank you for your help!

@rwb27
Copy link
Author

rwb27 commented Jun 1, 2020

@dogadogan, the easiest way to get this working is to use my fork of the picamera library, I've uploaded a wheel here:
https://github.com/rwb27/picamera/releases/tag/JOH_article

You can download the .whl file, and simply install that using pip. It replaces the standard picamera library, and makes analog_gain and digital_gain writeable, so the script above is no longer needed.

The master branch of my repository is closer to the latest version of the picamera library, but it is not yet extensively tested or packaged up. You can install it with

pip install https://github.com/rwb27/picamera.git

All the best,

Richard

@dogadogan
Copy link

Thank you very much for the detailed answer! So you recommend the first option (the .whl file) then since it has been tested, right?

You mentioned that the master branch of your repository is closer to the latest version; do you remember what version the .whl file is based on? Thanks for your help! :)

@PCHSwS
Copy link

PCHSwS commented Nov 23, 2020

Hi,
I don't really have much programming knowledge. I'm trying to understand everything that's written here but I'm struggling. All I want is full manual control over my camera. Apparently, the manual control of analog and digital gain is not yet possible in the picamera that comes with the Raspberry OS. (As of 2020-11)
I found your solution. Will this still work, is this still needed? Is there an easier way to solve this, or is that already in an available "beta" version of picamera I'm not aware of? I don't really get the userland concept, and I have a feeling that the answer is lying there. So please forgive me if I overlooked something.
And thank you very much in advance!

@dogadogan
Copy link

@PCHSwS, yes, you can use userland + this package to manually set the analog and digital gain.
After installing userland, you can install this package using something like pip install https://github.com/rwb27/picamera/archive/JOH_article.tar.gz
This replaces the standard picamera library, and makes analog_gain and digital_gain writeable, so you don't have to manually include the script above anymore.

P.S. (from the documentation) When using this package, it may be necessary to set the camera's iso property to 0 in order for setting the analog gain to work properly, due to the way it is implemented in the GPU firmware.

@rwb27
Copy link
Author

rwb27 commented Dec 7, 2020

One of my colleagues has tidied this up into a pip-installable package, picamerax that includes a bunch of other updates as well. See the forum post for more details, or just pip install picamerax. You'll then need to change any import statements to use picamerax, but import picamerax as picamera should work, because it is backwards-compatible.

@vijayashri91
Copy link

@PCHSwS, yes, you can use userland + this package to manually set the analog and digital gain.
After installing userland, you can install this package using something like pip install https://github.com/rwb27/picamera/archive/JOH_article.tar.gz
This replaces the standard picamera library, and makes analog_gain and digital_gain writeable, so you don't have to manually include the script above anymore.

P.S. (from the documentation) When using this package, it may be necessary to set the camera's iso property to 0 in order for setting the analog gain to work properly, due to the way it is implemented in the GPU firmware.


Defaulting to user installation because normal site-packages is not writeable
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting https://github.com/rwb27/picamera/archive/JOH_article.tar.gz
Using cached https://github.com/rwb27/picamera/archive/JOH_article.tar.gz
Building wheels for collected packages: picamera
Building wheel for picamera (setup.py) ... error
ERROR: Command errored out with exit status 1:
command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"'; file='"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' bdist_wheel -d /tmp/pip-wheel-r03b2ol7
cwd: /tmp/pip-req-build-c078tdel/
Complete output (58 lines):
running bdist_wheel
running build
running build_py
creating build
creating build/lib
creating build/lib/picamera
copying picamera/exc.py -> build/lib/picamera
copying picamera/init.py -> build/lib/picamera
copying picamera/frames.py -> build/lib/picamera
copying picamera/renderers.py -> build/lib/picamera
copying picamera/mmalobj.py -> build/lib/picamera
copying picamera/display.py -> build/lib/picamera
copying picamera/encoders.py -> build/lib/picamera
copying picamera/bcm_host.py -> build/lib/picamera
copying picamera/array.py -> build/lib/picamera
copying picamera/vcsmobj.py -> build/lib/picamera
copying picamera/mmal.py -> build/lib/picamera
copying picamera/streams.py -> build/lib/picamera
copying picamera/color.py -> build/lib/picamera
copying picamera/user_vcsm.py -> build/lib/picamera
copying picamera/camera.py -> build/lib/picamera
running egg_info
creating picamera.egg-info
writing picamera.egg-info/PKG-INFO
writing dependency_links to picamera.egg-info/dependency_links.txt
writing requirements to picamera.egg-info/requires.txt
writing top-level names to picamera.egg-info/top_level.txt
writing manifest file 'picamera.egg-info/SOURCES.txt'
reading manifest file 'picamera.egg-info/SOURCES.txt'
reading manifest template 'MANIFEST.in'
no previously-included directories found matching 'debian'
no previously-included directories found matching 'docs'
writing manifest file 'picamera.egg-info/SOURCES.txt'
installing to build/bdist.linux-armv7l/wheel
running install
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-req-build-c078tdel/setup.py", line 145, in
main()
File "/tmp/pip-req-build-c078tdel/setup.py", line 140, in main
cmdclass = {'install': CustomInstallCommand},
File "/usr/local/lib/python3.7/dist-packages/setuptools/init.py", line 153, in setup
return distutils.core.setup(**attrs)
File "/usr/lib/python3.7/distutils/core.py", line 148, in setup
dist.run_commands()
File "/usr/lib/python3.7/distutils/dist.py", line 966, in run_commands
self.run_command(cmd)
File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "/usr/local/lib/python3.7/dist-packages/wheel/bdist_wheel.py", line 335, in run
self.run_command('install')
File "/usr/lib/python3.7/distutils/cmd.py", line 313, in run_command
self.distribution.run_command(command)
File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "/tmp/pip-req-build-c078tdel/setup.py", line 109, in run
raise ValueError('This system does not appear to be a Raspberry Pi')
ValueError: This system does not appear to be a Raspberry Pi

ERROR: Failed building wheel for picamera
Running setup.py clean for picamera
Failed to build picamera
Installing collected packages: picamera
Running setup.py install for picamera ... error
ERROR: Command errored out with exit status 1:
command: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"'; file='"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' install --record /tmp/pip-record-qmm6frhh/install-record.txt --single-version-externally-managed --user --prefix= --compile --install-headers /home/pi/.local/include/python3.7m/picamera
cwd: /tmp/pip-req-build-c078tdel/
Complete output (18 lines):
running install
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-req-build-c078tdel/setup.py", line 145, in
main()
File "/tmp/pip-req-build-c078tdel/setup.py", line 140, in main
cmdclass = {'install': CustomInstallCommand},
File "/usr/local/lib/python3.7/dist-packages/setuptools/init.py", line 153, in setup
return distutils.core.setup(**attrs)
File "/usr/lib/python3.7/distutils/core.py", line 148, in setup
dist.run_commands()
File "/usr/lib/python3.7/distutils/dist.py", line 966, in run_commands
self.run_command(cmd)
File "/usr/lib/python3.7/distutils/dist.py", line 985, in run_command
cmd_obj.run()
File "/tmp/pip-req-build-c078tdel/setup.py", line 109, in run
raise ValueError('This system does not appear to be a Raspberry Pi')
ValueError: This system does not appear to be a Raspberry Pi
----------------------------------------
ERROR: Command errored out with exit status 1: /usr/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"'; file='"'"'/tmp/pip-req-build-c078tdel/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(file);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, file, '"'"'exec'"'"'))' install --record /tmp/pip-record-qmm6frhh/install-record.txt --single-version-externally-managed --user --prefix= --compile --install-headers /home/pi/.local/include/python3.7m/picamera Check the logs for full command output.

Hello, I am getting error while installing pip install https://github.com/rwb27/picamera/archive/JOH_article.tar.gz. Please let me know if there is any other solution.

I download zip file and extracted it. and ran python camera.py. It ge=ives me error of "new_picamera.exc.picamerammalerror: failed to enable connection: out of resources". Please let me know how to proceed if anybody know any solution.

Thank you.

@jhihn
Copy link

jhihn commented Mar 25, 2021

I am trying to reverse engineer this for a project using the C++ raspicam library. I'm setting the gains, I think appropriately, but it is not working.
What is the significance of line 49? cam.shutter_speed = cam.exposure_speed

In the C++ I set shutter speed to a value I like, and exposure to off. I think this should do the same thing?

@oferbentovim
Copy link

oferbentovim commented Aug 7, 2022

to capture stable images use this code in python
you do not have to use opencv as it is not installed easily


from picamerax import PiCamera #https://gist.github.com/rwb27/a23808e9f4008b48de95692a38ddaa08/ 
from picamerax.array import PiRGBArray

def InitCamera():
	iso = 120
	awbR = 1.25
	awbB = 1.8
	shutter = 35500
	angain = 1
	dggain = 1

	#camera.resolution = (2592, 1944) # this gets an image with a bluish line on the bottom of the image
	camera.resolution = (2592, 1440) #v4l2-ctl --list-formats-ext 
	camera.framerate = 10
	camera.vflip = True
	camera.hflip = True
	#camera.start_preview(fullscreen=False, window = (35, 35, 60, 60))

	camera.exposure_mode = 'off'
	camera.awb_mode = 'off'
	camera.drc_strength = 'off'
	camera.iso = iso	
	camera.shutter_speed = shutter 
	camera.analog_gain = angain
	camera.digital_gain = dggain
	camera.awb_gains = (awbR,awbB) # (red, blue) 

def GetImageAndShow():
	# grab an image from the camera
	rawCapture = PiRGBArray(camera)
	camera.capture(rawCapture, format="bgr")
	image = rawCapture.array
	cv2.imshow("output", image)
	k = cv2.waitKeyEx(5) & 0xFF
	#if k == 27: #Esc - this will only work if thre is a cv2.window working
	#	Exit = True
	filename = PixDir + "/test.png"
	cv2.imwrite(filename, image)

it does capture a stable image however the maximum image size available is not achived even if you set gpu memory to 256 in sudo raspi-config

@rwb27
Copy link
Author

rwb27 commented Aug 19, 2022

@oferbentovim I've never found a definitive answer, but raw RGB captures don't often work at full resolution. I've had better success with JPEG captures at full resolution, or JPEG plus Bayer data (if you want the full uncompressed pixel data). The latter requires a bit more work to process the image and is slow, but seems way more reliable.

@rwb27
Copy link
Author

rwb27 commented Aug 19, 2022

@jhihn that line sets the shutter speed to the last value used by the auto exposure - it means the exposure doesn't change when we switch from auto to manual. If you already have a sensible value for shutter speed, you can safely ignore it.

@TheAllKnowingSeaweed
Copy link

TheAllKnowingSeaweed commented Apr 27, 2023

Did anyone get this to work without getting errors or find another way of setting the analog gain high consitently? @dogadogan @vijayashri91 did you manage to find a solution to this problem around the error wit userland download?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment