33"""
44
55import os
6+ import math
67import ulab
78
89class BMPError (Exception ):
@@ -59,7 +60,8 @@ def __init__(self, num_pixels, order='brg', gamma=2.6):
5960 self .blue_index = order .find ('b' )
6061 self .num_pixels = num_pixels
6162 self .gamma = gamma
62- self .bmpfile = None
63+ self .bmp_file = None
64+ self .bmp_specs = None
6365
6466
6567 def read_le (self , num_bytes ):
@@ -73,7 +75,7 @@ def read_le(self, num_bytes):
7375 Converted integer product.
7476 """
7577 result = 0
76- for byte_index , byte in enumerate (self .bmpfile .read (num_bytes )):
78+ for byte_index , byte in enumerate (self .bmp_file .read (num_bytes )):
7779 result += byte << (byte_index * 8 )
7880 return result
7981
@@ -85,13 +87,13 @@ def read_header(self):
8587 Returns:
8688 BMPSpecs object containing size, offset, etc.
8789 """
88- if self .bmpfile .read (2 ) != b'BM' : # Check signature
90+ if self .bmp_file .read (2 ) != b'BM' : # Check signature
8991 raise BMPError ("Not BMP file" )
9092
91- self .bmpfile .read (8 ) # Read & ignore file size & creator bytes
93+ self .bmp_file .read (8 ) # Read & ignore file size & creator bytes
9294
9395 image_offset = self .read_le (4 ) # Start of image data
94- self .bmpfile .read (4 ) # Read & ignore header size
96+ self .bmp_file .read (4 ) # Read & ignore header size
9597 width = self .read_le (4 )
9698 height = self .read_le (4 )
9799 # BMPs are traditionally stored bottom-to-top.
@@ -127,7 +129,7 @@ def scandir(self, path):
127129 valid_list = []
128130 for entry in full_list :
129131 try :
130- with open (entry , "rb" ) as self .file :
132+ with open (path + '/' + entry , 'rb' ) as self .bmp_file :
131133 self .read_header ()
132134 valid_list .append (entry )
133135 except (OSError , BMPError ):
@@ -137,20 +139,7 @@ def scandir(self, path):
137139 return valid_list
138140
139141
140- # old file will be overwritten.
141- # gamma is stored in self,
142- # brightness and loop will be passed in.
143- # Oh...also need to pass in number of rows to stretch.
144- # Number of LEDs is known from constructor. These are the items:
145- # self.red_index = order.find('r')
146- # self.green_index = order.find('g')
147- # self.blue_index = order.find('b')
148- # self.num_pixels = num_pixels
149- # self.gamma = gamma
150- # self.file = None
151- # Delete existing tempfile before checking free space.
152-
153- def read_row (self , row ):
142+ def read_row (self , row , num_bytes ):
154143 """
155144 Read one row of pixels from BMP file, clipped to minimum of BMP
156145 image width or LED strip length.
@@ -162,10 +151,11 @@ def read_row(self, row):
162151 """
163152 # 'flip' logic is intentionally backwards from typical BMP loader,
164153 # this makes BMP image prep an easy 90 degree CCW rotation.
165- if not bmp .flip :
166- row = bmp .height - 1 - row
167- self .file .seek (bmp .image_offset + row * bmp .row_size )
168- return ulab .array (self .file .read (clipped_row_size ), dtype = uint8 )
154+ if not self .bmp_specs .flip :
155+ row = self .bmp_specs .height - 1 - row
156+ self .bmp_file .seek (self .bmp_specs .image_offset +
157+ row * self .bmp_specs .row_size )
158+ return ulab .array (self .bmp_file .read (num_bytes ), dtype = ulab .uint8 )
169159
170160
171161 def process (self , input_filename , output_filename , rows ,
@@ -214,8 +204,8 @@ def process(self, input_filename, output_filename, rows,
214204 # start markers and footer), with colors all '0' to start...these
215205 # will be filled later.
216206 dotstar_buffer = bytearray ([0 ] * 4 +
217- [255 , 0 , 0 , 0 ] * num_pixels +
218- [255 ] * ((num_pixels + 15 ) // 16 ))
207+ [255 , 0 , 0 , 0 ] * self . num_pixels +
208+ [255 ] * ((self . num_pixels + 15 ) // 16 ))
219209 dotstar_row_size = len (dotstar_buffer )
220210
221211 # Delete old temporary file, if any
@@ -234,39 +224,42 @@ def process(self, input_filename, output_filename, rows,
234224 rows = min (rows , bytes_free // dotstar_row_size )
235225
236226 try :
237- with open (input_filename , 'rb' ) as file_in :
227+ with open (input_filename , 'rb' ) as self . bmp_file :
238228 #print("File opened")
239229
240- bmp = self .read_header ()
230+ self . bmp_specs = self .read_header ()
241231
242- #print("WxH: (%d,%d)" % (bmp.width, bmp.height))
232+ #print("WxH: (%d,%d)" % (self.bmp_specs.width,
233+ # self.bmp_specs.height))
243234 #print("Image format OK, reading data...")
244235
245- # Constrain row width to pixel strip length
246- clipped_width = min (bmp .width , self .num_pixels )
236+ # Constrain bytes-to-read to pixel strip length
237+ clipped_width = min (self .bmp_specs .width , self .num_pixels )
238+ row_bytes = 3 * clipped_width
247239
248240 # Each output row is interpolated from two BMP rows,
249241 # we'll call them 'a' and 'b' here.
250242 row_a_data , row_b_data = None , None
251- prev_row_a_index , prev_row_b_index = None
243+ prev_row_a_index , prev_row_b_index = None , None
252244
253- with open (output_filename , 'wb' ) as file_out :
245+ with open (output_filename , 'wb' ) as led_file :
246+ err = 0
254247 for row in range (rows ): # For each output row...
255248 position = row / (rows - 1 ) # 0.0 to 1.0
256249 if callback :
257250 callback (position )
258251 # Scale position into pixel space...
259- if self . loop : # 0 to image height
260- position *= len ( self . columns )
252+ if loop : # 0 to image height
253+ position *= rows
261254 else : # 0 to last row
262- position *= (len ( self . columns ) - 1 )
255+ position *= (rows - 1 )
263256
264257 # Separate absolute position into several values:
265258 # integer 'a' and 'b' row indices, floating 'a' and
266259 # 'b' weights (0.0 to 1.0) for interpolation.
267- row_b_weight , row_a_index = modf (position )
260+ row_b_weight , row_a_index = math . modf (position )
268261 row_a_index = int (row_a_index )
269- row_b_index = (row_a_index + 1 ) % bmp .height
262+ row_b_index = (row_a_index + 1 ) % self . bmp_specs .height
270263 row_a_weight = 1.0 - row_b_weight
271264
272265 # New data ONLY needs reading if row index changed
@@ -277,9 +270,11 @@ def process(self, input_filename, output_filename, rows,
277270 if row_a_index == prev_row_b_index :
278271 row_a_data = row_b_data
279272 else :
280- row_a_data = self .read (row_a_index )
273+ row_a_data = self .read_row (row_a_index ,
274+ row_bytes )
281275 # Read new 'b' data on any row change
282- row_b_data = self .read (row_b_index )
276+ row_b_data = self .read_row (row_b_index ,
277+ row_bytes )
283278 prev_row_a_index = row_a_index
284279 prev_row_b_index = row_b_index
285280
@@ -299,9 +294,10 @@ def process(self, input_filename, output_filename, rows,
299294 # floating-point) pixel values resulting from the
300295 # interpolation, with gamma correction applied and
301296 # scaled back up to the 0-255 range.
302- want = ((row_a_data * row_a_weight +
303- row_b_data * row_b_weight ) **
304- self .gamma * 255.001 )
297+ # ValueError: operands could not be broadcast together
298+ want = ((((row_a_data * row_a_weight ) +
299+ (row_b_data * row_b_weight )) **
300+ self .gamma ) * 255.001 )
305301
306302 # 'got' will be an ndarray of the values that get
307303 # issued to the LED strip, formed through several
@@ -334,23 +330,28 @@ def process(self, input_filename, output_filename, rows,
334330 # allowing for header and start-of-pixel markers
335331 # in the DotStar data.
336332 for column in range (clipped_width ):
337- bmp_pos = x * 3
338- dotstar_pos = 5 + x * 4
339- bgr = data [bmp_pos :bmp_pos + 3 ]
340- dotstar_buffer [dotstar_pos + blue_index ] = bgr [0 ]
341- dotstar_buffer [dotstar_pos + green_index ] = bgr [1 ]
342- dotstar_buffer [dotstar_pos + red_index ] = bgr [2 ]
343-
344- file_out .write (dotstar_buffer )
333+ bmp_pos = column * 3
334+ dotstar_pos = 5 + column * 4
335+ bgr = got [bmp_pos :bmp_pos + 3 ]
336+ dotstar_buffer [dotstar_pos +
337+ self .blue_index ] = bgr [0 ]
338+ dotstar_buffer [dotstar_pos +
339+ self .green_index ] = bgr [1 ]
340+ dotstar_buffer [dotstar_pos +
341+ self .red_index ] = bgr [2 ]
342+
343+ led_file .write (dotstar_buffer )
345344
346345 # If not looping, add an 'all off' row of LED data
347346 # at end to ensure last row timing is consistent.
348347 if not loop :
349348 rows += 1
350- file_out .write (bytearray ([0 ] * 4 +
351- [255 , 0 , 0 , 0 ] * num_pixels +
352- [255 ] * ((num_pixels + 15 ) //
353- 16 )))
349+ led_file .write (bytearray ([0 ] * 4 +
350+ [255 , 0 , 0 , 0 ] *
351+ self .num_pixels +
352+ [255 ] *
353+ ((self .num_pixels + 15 ) //
354+ 16 )))
354355
355356 #print("Loaded OK!")
356357 return rows
0 commit comments