Skip to content

Commit 5b0affe

Browse files
committed
refactoring, cleaning and getting ready to work with different color modes and bit-depths for any camera (color or monochrome)
1 parent c933841 commit 5b0affe

6 files changed

Lines changed: 487 additions & 240 deletions

File tree

README.md

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,46 @@ the camera parameters printed in the terminal.
6060

6161
## IDS cameras python interface
6262

63-
blablabla
63+
Easy example for just one camera
6464

65-
## IDS cameras LabView interface
65+
from interface import IDSinterface
66+
67+
my_interface = IDSinterface()
68+
my_interface.set_pixel_format(bit_rate=12, colorness="RGB") # RGB12 (optional)
69+
my_interface.select_and_start_device() # Select the first device found and start acquisition
70+
my_interface.set_fps(1000) # Set the fps to 1000 (optional)
71+
my_interface.set_exposure_time(1000*50) # Set the exposure time to 50 ms (optional)
72+
my_interface.set_gain() # Set the gain to 1 (optional)
73+
image = my_interface.capture() # Capture an image as a numpy array
74+
my_interface.stop() # Stop the acquisition
75+
del my_interface # Close the interface and release the camera
76+
77+
If more than one camera is wired, every method has an optional argument
78+
`idx` to select the camera to manage.
6679

67-
blablabla
80+
my_interface = IDSinterface()
81+
# Exploring available devices
82+
all_devices = my_interface.get_devices()
83+
print(f"\nFound {len(all_devices)} devices:")
84+
for idx, device in enumerate(all_devices):
85+
print(f" > {idx} : {device.ModelName()}")
6886

69-
## Interfície per a càmeres IDS
87+
# Output:
88+
$ Found 2 devices:
89+
$ > 0 : U3-368xXLE-M
90+
$ > 1 : U3-368xXLE-C
7091

71-
El script `interface.py` conté un wrapper de la API de IDS per a controlar les
72-
seves pròpies càmeres. D'aquesta forma, en facilitem la posada a punt i l'ús
73-
amb funcions de conveniència, que preparen la càmera automàticament per a la presa de
74-
dades. L'ús d'aquesta interfície és ben senzill, tal i com surt al script `test_camera.py`
92+
my_interface.select_and_start_device(idx=0) # Select the first device found and start acquisition
93+
my_interface.select_and_start_device(idx=1) # Select the second device found and start acquisition
94+
my_interface.set_pixel_format(bit_rate=8, idx=0) # Seting 8 bit pixel format for the first camera
95+
my_interface.set_exposure(1000*50, idx=1) # Set the exposure time to 50 ms to the second camera
96+
image0 = my_interface.capture(idx=0) # Capture an image from the first camera
97+
image1 = my_interface.capture(idx=1) # Capture an image from the second camera
7598

76-
```python
77-
from interface import IDSCamera
78-
```
7999

100+
## IDS cameras LabView interface
80101

102+
blablabla
81103

82104

83105
**************************************************************************

analyze_picture.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
""" This program is used to analyze the picture taken by a IDS camera.
5+
6+
Take a picture with the IDS camera, either with their-own software (cockpit),
7+
or with the interface in this repository, save it as a PNG file, and then
8+
run this program to analyze the picture.
9+
10+
You can run it:
11+
- as script (in command line)
12+
$ python analyze_picture.py <picture_path>
13+
- by PLAY button in Spyder (then, type the picture_path in the console)
14+
- import it as a module (in python console or in any other script, check last step of test_camera.py)
15+
from analyze_picture import analyze_picture
16+
analyze_picture(picture_path)
17+
18+
The program will show the picture, the histogram of the picture, and the
19+
color channels and their histograms.
20+
21+
The program will also print the shape, dtype, min and max of the picture.
22+
23+
Notice that the picture has 16 bits per channel and pixel (dtype=uint16).
24+
However, the camera only uses 12 bits per channel and pixel,
25+
the 4 least significant bits are always 0 and then the histogram is sparse.
26+
So, you can squeeze the picture to 12 bits per channel and pixel,
27+
just by dividing the image by 2**4=16. Check the option squeeze=True.
28+
29+
"""
30+
31+
import sys
32+
import matplotlib.pyplot as plt
33+
import numpy as np
34+
import imageio.v2 as imageio
35+
36+
# If you don't have the freeimage plugin (FORMAT ERROR), you can download it with:
37+
# imageio.plugins.freeimage.download() # this should be done only once, then comment it again
38+
39+
def analyze_picture(picture_path, squeeze=False, bit_depth=12):
40+
41+
if isinstance(picture_path, str):
42+
im = imageio.imread(picture_path, format='PNG-FI') # if FORMAT ERROR -> check a couple of lines above
43+
else:
44+
""" If the picture_path is not a string,
45+
it is assumed that it is already a numpy array.
46+
"""
47+
im = picture_path
48+
49+
print(f"raw_im.shape = {im.shape} ; raw_im.dtype = {im.dtype} ; "
50+
f"raw_im.min() = {im.min()} ; raw_im.max() = {im.max()}")
51+
if im.ndim == 2:
52+
im = np.repeat(im[:, :, np.newaxis], 3, axis=2)
53+
print('The picture is grayscale, so it has been converted to RGB.')
54+
elif im.shape[2] == 4:
55+
im = im[:, :, :3]
56+
print('The picture has an alpha channel, so it has been removed.')
57+
58+
raw_br = int(np.round(np.log2(im.max()))) # = 16, 12, 10, 8...
59+
print(f"raw_br = {raw_br}")
60+
final_br = bit_depth if squeeze else raw_br
61+
62+
if squeeze:
63+
""" Let's squeeze the picture to 12 bits per channel and pixel,
64+
just by dividing the image by 2**4=16.
65+
"""
66+
squeeze_level = int(2**16 / 2**bit_depth) # = 16 (bit_depth=12)
67+
im = (im/squeeze_level).astype(np.uint16)
68+
hist = lambda im: np.histogram(im, bins=np.arange(2**bit_depth))[0]
69+
else:
70+
hist = lambda im: np.histogram(im, bins=np.arange(2**raw_br))[0]
71+
72+
print(f"im.shape = {im.shape} ; im.dtype = {im.dtype} ; "
73+
f"im.min() = {im.min()} ; im.max() = {im.max()}")
74+
75+
fig, ax = plt.subplots(2, 3)
76+
77+
ax[0, 0].set_title('Red channel')
78+
r_plot = ax[0, 0].imshow(im[:, :, 0], vmin=0, vmax=2**final_br, cmap='Reds_r')
79+
plt.colorbar(r_plot, ax=ax[0, 0])
80+
81+
ax[0, 1].set_title('Green channel')
82+
g_plot = ax[0, 1].imshow(im[:, :, 1], vmin=0, vmax=2**final_br, cmap='Greens_r')
83+
plt.colorbar(g_plot, ax=ax[0, 1])
84+
85+
ax[0, 2].set_title('Blue channel')
86+
b_plot = ax[0, 2].imshow(im[:, :, 2], vmin=0, vmax=2**final_br, cmap='Blues_r')
87+
plt.colorbar(b_plot, ax=ax[0, 2])
88+
89+
ax[1, 0].set_title('Original picture')
90+
ax[1, 0].imshow(im/2**final_br, cmap='gray')
91+
92+
gs = ax[1, 2].get_gridspec()
93+
# remove the underlying axes
94+
for axi in ax[1, 1:]:
95+
axi.remove()
96+
axhist = fig.add_subplot(gs[1, 1:])
97+
axhist.set_title('Histograms (when not squeezed, check if it dense or sparse)')
98+
axhist.plot(hist(im[:, :, 0]), 'x-r', label='Red channel')
99+
axhist.plot(hist(im[:, :, 1]), '+-g', label='Green channel')
100+
axhist.plot(hist(im[:, :, 2]), '.-b', label='Blue channel')
101+
axhist.set_xlim([-1, 2**final_br+1])
102+
if final_br >= 10:
103+
axins = axhist.inset_axes([0.4, 0.6, 0.57, 0.37], xlim=(900, 990),
104+
ylim=(0, (hist(im[:, :, 2])[900:990]).max()*1.1))
105+
axins.plot(hist(im[:, :, 2]), '.b')
106+
axhist.indicate_inset_zoom(axins, edgecolor="black")
107+
axhist.legend()
108+
109+
plt.show()
110+
111+
if __name__ == '__main__':
112+
""" Usage: python analyze_picture.py <path_to_picture> [--squeeze]
113+
114+
If --squeeze is specified, the picture is squeezed to 12 bit depth.
115+
116+
Example: python analyze_picture.py my_picture.png --squeeze
117+
"""
118+
119+
squeeze = False
120+
if len(sys.argv) > 1:
121+
picture_path = sys.argv[1]
122+
if '--squeeze' in sys.argv:
123+
squeeze = True
124+
else:
125+
picture_path = input('Please enter the path of the picture: ')
126+
127+
analyze_picture(picture_path, squeeze=squeeze)

0 commit comments

Comments
 (0)