add support for monochrome distributors
Stefan Schuermans

Stefan Schuermans commited on 2018-11-01 14:46:02
Showing 8 changed files, with 64 additions and 14 deletions.

... ...
@@ -12,8 +12,8 @@ are sent for one frame - a single MCUF frame to every distributor.
12 12
 The coordinates of the pixels in the MCUF packets do not correspond to pixel
13 13
 locations, but to pixel addresses. The y coordinate is the number of the
14 14
 output of the distributor and the x coordinate is the number of the pixel
15
-in the chain. The number of channels is always 3 (RGB) and the maximum value
16
-is always 255 (8 bit per channel).
15
+in the chain. The number of channels is always 1 (monochrome)  or 3 (RGB) and
16
+the maximum value is always 255 (8 bit per channel).
17 17
 
18 18
 This library can be used to send the required UDP packets to the EtherPix
19 19
 display to make it show images and/or videos.
... ...
@@ -29,7 +29,8 @@ distributor 15 = 6,64
29 29
 
30 30
 # those lines introduce a mapping between the channel values in the video and the values transmitted to the display
31 31
 #  - mapping is done per distributor and applies to all pixels connected to it
32
-#  - the general format is: mapping <distributor number> (red|green|blue) = <base> <factor> <gamma>
32
+#  - RGB distributors use red, green and blue, monochrone distributors use white
33
+#  - the general format is: mapping <distributor number> (red|green|blue|white) = <base> <factor> <gamma>
33 34
 #  - the mapping formula is: <display value> := <base> + <factor> * <original value> ^ (1 / <gamma>)
34 35
 mapping 0 red = 0.005 0.99 1.0
35 36
 mapping 0 green = 0.005 0.99 1.0
... ...
@@ -0,0 +1,26 @@
1
+# EtherPix configuration file
2
+
3
+# the address to bind the local socket to
4
+#  - the EtherPix network 10.70.80.0/16 must be reachable from this address
5
+bindAddr = 0.0.0.0:0
6
+
7
+# the size of the display
8
+#  - <width>,<height> in pixels
9
+size = 8,8
10
+
11
+# this line adds a new distributor
12
+#  - the general format is: distributor <distributor number> = <number of outputs>,<number of pixel per output>
13
+distributor 0 = 6,64
14
+
15
+# those lines introduce a mapping between the channel values in the video and the values transmitted to the display
16
+#  - mapping is done per distributor and applies to all pixels connected to it
17
+#  - RGB distributors use red, green and blue, monochrone distributors use white
18
+#  - the general format is: mapping <distributor number> (red|green|blue|white) = <base> <factor> <gamma>
19
+#  - the mapping formula is: <display value> := <base> + <factor> * <original value> ^ (1 / <gamma>)
20
+mapping 0 white = 0.0 1.0 1.0
21
+
22
+# these lines specify the logical positions of the pixels at an output in movie coordinates
23
+#  - the general format is: output <distributor number>,<output number> = <pixel x>,<pixel y> ...
24
+#  - the order of the pixels is the order they are connected in the chain
25
+output 0,0 = 0,0 1,0 2,0 3,0 4,0 5,0 6,0 7,0 7,1 6,1 5,1 4,1 3,1 2,1 1,1 0,1 0,2 1,2 2,2 3,2 4,2 5,2 6,2 7,2 7,3 6,3 5,3 4,3 3,3 2,3 1,3 0,3 0,4 1,4 2,4 3,4 4,4 5,4 6,4 7,4 7,5 6,5 5,5 4,5 3,5 2,5 1,5 0,5 0,6 1,6 2,6 3,6 4,6 5,6 6,6 7,6 7,7 6,7 5,7 4,7 3,7 2,7 1,7 0,7
26
+
... ...
@@ -14,7 +14,8 @@ distributor 0 = 6,64
14 14
 
15 15
 # those lines introduce a mapping between the channel values in the video and the values transmitted to the display
16 16
 #  - mapping is done per distributor and applies to all pixels connected to it
17
-#  - the general format is: mapping <distributor number> (red|green|blue) = <base> <factor> <gamma>
17
+#  - RGB distributors use red, green and blue, monochrone distributors use white
18
+#  - the general format is: mapping <distributor number> (red|green|blue|white) = <base> <factor> <gamma>
18 19
 #  - the mapping formula is: <display value> := <base> + <factor> * <original value> ^ (1 / <gamma>)
19 20
 mapping 0 red = 0.0 1.0 1.0
20 21
 mapping 0 green = 0.0 1.0 1.0
... ...
@@ -185,11 +185,17 @@ class Display(object):
185 185
         # color channel
186 186
         color_str = fields[2]
187 187
         if color_str == "red":
188
+            channels = 3
188 189
             color = Mapping.RED
189 190
         elif color_str == "green":
191
+            channels = 3
190 192
             color = Mapping.GREEN
191 193
         elif color_str == "blue":
194
+            channels = 3
192 195
             color = Mapping.BLUE
196
+        elif color_str == "white":
197
+            channels = 1
198
+            color = Mapping.WHITE
193 199
         else:
194 200
             self._msg.msg(Msg.ERR, "invalid color channel \"%s\""
195 201
                                    " in line %u of config file" %
... ...
@@ -233,6 +239,8 @@ class Display(object):
233 239
              return False
234 240
         # set mapping
235 241
         self._distris[distri_no].set_mapping(color, base, factor, gamma)
242
+        # update number of channels
243
+        self._distris[distri_no].set_channels(channels)
236 244
         return True
237 245
 
238 246
     def _proc_config_output(self, setting, value, lineno):
... ...
@@ -41,9 +41,8 @@ class Distributor(object):
41 41
             self._mappings.append(Mapping())
42 42
         # pixel coordinates: all unknown
43 43
         self._pixel_coords = [[None] * self._pixels] * self._outputs
44
-        # header for UDP packets
45
-        self._udp_hdr = struct.pack("!I4H", 0x23542666,
46
-                                    outputs, pixels, Mapping.CHANNELS, 255)
44
+        # initialize channels and header for UDP packets
45
+        self.set_channels(Mapping.CHANNELS)
47 46
         # initial image data is cleared
48 47
         self.data_clear()
49 48
 
... ...
@@ -75,7 +74,7 @@ class Distributor(object):
75 74
         """clear image data, i.e. set entire image to black"""
76 75
         # prepare message buffer with all pixels cleared (black)
77 76
         clr = bytearray()
78
-        for channel in range(Mapping.CHANNELS):
77
+        for channel in range(self._channels):
79 78
             clr += self._mappings[channel].table[0]
80 79
         self._buffer = self._udp_hdr + (clr * (self._outputs * self._pixels))
81 80
 
... ...
@@ -99,7 +98,13 @@ class Distributor(object):
99 98
                         # get pixel from image
100 99
                         pix = image.getpixel(x_y)
101 100
                 # add pixel to pixel data
102
-                for channel in range(Mapping.CHANNELS):
101
+                #   monochrome (convert, emphasize green)
102
+                if self._channels == 1:
103
+                    mono = (pix[0] + (pix[1] << 1) + pix[2] + 2) >> 2
104
+                    data += self._mappings[0].table[mono]
105
+                #   RGB
106
+                elif self._channels == 3:
107
+                    for channel in range(3):
103 108
                         data += self._mappings[channel].table[pix[channel]]
104 109
         # store UDP message
105 110
         self._buffer = data
... ...
@@ -113,6 +118,18 @@ class Distributor(object):
113 118
             except:
114 119
                 pass
115 120
 
121
+    def set_channels(self, channels):
122
+        """set number of channels, supported values: 1 or 3"""
123
+        if channels == 1 or channels == 3:
124
+            self._channels = channels
125
+            # header for UDP packets
126
+            self._udp_hdr = struct.pack("!I4H", 0x23542666, self._outputs,
127
+                                        self._pixels, self._channels, 255)
128
+
129
+    def set_mapping(self, color, base, factor, gamma):
130
+        """set distributor mapping for one color channel"""
131
+        self._mappings[color].set_params(base, factor, gamma)
132
+
116 133
     def set_pixel_coords(self, output_no, pixel_coords):
117 134
         """set distributor address
118 135
            output_no: number of output
... ...
@@ -123,7 +140,3 @@ class Distributor(object):
123 140
             pad_cnt = self._pixels - len(self._pixel_coords[output_no])
124 141
             self._pixel_coords[output_no] += [None] * pad_cnt
125 142
 
126
-    def set_mapping(self, color, base, factor, gamma):
127
-        """set distributor mapping for one color channel"""
128
-        self._mappings[color].set_params(base, factor, gamma)
129
-
... ...
@@ -21,6 +21,7 @@ from struct import Struct
21 21
 class Mapping(object):
22 22
 
23 23
     RED = 0
24
+    WHITE = 0 # red and white share first mapping
24 25
     GREEN = 1
25 26
     BLUE = 2
26 27
     CHANNELS = 3
... ...
@@ -1,7 +1,7 @@
1 1
 from setuptools import setup
2 2
 
3 3
 setup(name='pyetherpix',
4
-      version='0.2',
4
+      version='0.3',
5 5
       description='python implementation of EtherPix output library',
6 6
       url='https://git.blinkenarea.org?p=pyetherpix',
7 7
       author='Stefan Schuermans',
8 8