2020
2121class CameraApp (Activity ):
2222
23+ DEFAULT_WIDTH = 320 # 240 would be better but webcam doesn't support this (yet)
24+ DEFAULT_HEIGHT = 240
25+
2326 button_width = 60
2427 button_height = 45
25- width = 320
26- height = 240
28+ graymode = True
2729
2830 status_label_text = "No camera found."
2931 status_label_text_searching = "Searching QR codes...\n \n Hold still and try varying scan distance (10-25cm) and QR size (4-12cm). Ensure proper lighting."
3032 status_label_text_found = "Decoding QR..."
3133
3234 cam = None
3335 current_cam_buffer = None # Holds the current memoryview to prevent garbage collection
34- current_cam_buffer_copy = None # Holds a copy so that the memoryview can be free'd
36+ width = None
37+ height = None
3538
3639 image = None
3740 image_dsc = None
@@ -52,16 +55,16 @@ class CameraApp(Activity):
5255 def load_resolution_preference (self ):
5356 """Load resolution preference from SharedPreferences and update width/height."""
5457 prefs = SharedPreferences ("com.micropythonos.camera" )
55- resolution_str = prefs .get_string ("resolution" , "320x240 " )
58+ resolution_str = prefs .get_string ("resolution" , f" { self . DEFAULT_WIDTH } x { self . DEFAULT_HEIGHT } " )
5659 try :
5760 width_str , height_str = resolution_str .split ('x' )
5861 self .width = int (width_str )
5962 self .height = int (height_str )
6063 print (f"Camera resolution loaded: { self .width } x{ self .height } " )
6164 except Exception as e :
6265 print (f"Error parsing resolution '{ resolution_str } ': { e } , using default 320x240" )
63- self .width = 320
64- self .height = 240
66+ self .width = self . DEFAULT_WIDTH
67+ self .height = self . DEFAULT_HEIGHT
6568
6669 def onCreate (self ):
6770 self .load_resolution_preference ()
@@ -88,7 +91,6 @@ def onCreate(self):
8891 settings_label .set_text (lv .SYMBOL .SETTINGS )
8992 settings_label .center ()
9093 settings_button .add_event_cb (lambda e : self .open_settings (),lv .EVENT .CLICKED ,None )
91-
9294 self .snap_button = lv .button (self .main_screen )
9395 self .snap_button .set_size (self .button_width , self .button_height )
9496 self .snap_button .align (lv .ALIGN .RIGHT_MID , 0 , 0 )
@@ -104,8 +106,6 @@ def onCreate(self):
104106 zoom_label = lv .label (self .zoom_button )
105107 zoom_label .set_text ("Z" )
106108 zoom_label .center ()
107-
108-
109109 self .qr_button = lv .button (self .main_screen )
110110 self .qr_button .set_size (self .button_width , self .button_height )
111111 self .qr_button .add_flag (lv .obj .FLAG .HIDDEN )
@@ -161,7 +161,6 @@ def onResume(self, screen):
161161 if self .scanqr_mode :
162162 self .finish ()
163163
164-
165164 def onPause (self , screen ):
166165 print ("camera app backgrounded, cleaning up..." )
167166 if self .capture_timer :
@@ -208,11 +207,13 @@ def create_preview_image(self):
208207 "magic" : lv .IMAGE_HEADER_MAGIC ,
209208 "w" : self .width ,
210209 "h" : self .height ,
211- "stride" : self .width * 2 ,
212- "cf" : lv .COLOR_FORMAT .RGB565
213- #"cf": lv.COLOR_FORMAT.L8
210+ #"stride": self.width * 2, # RGB565
211+ "stride" : self .width , # RGB565
212+ #"cf": lv.COLOR_FORMAT.RGB565
213+ "cf" : lv .COLOR_FORMAT .L8
214214 },
215- 'data_size' : self .width * self .height * 2 ,
215+ #'data_size': self.width * self.height * 2, # RGB565
216+ 'data_size' : self .width * self .height , # gray
216217 'data' : None # Will be updated per frame
217218 })
218219 self .image .set_src (self .image_dsc )
@@ -224,7 +225,7 @@ def qrdecode_one(self):
224225 import qrdecode
225226 import utime
226227 before = utime .ticks_ms ()
227- result = qrdecode .qrdecode_rgb565 (self .current_cam_buffer , self .width , self .height )
228+ result = qrdecode .qrdecode (self .current_cam_buffer , self .width , self .height )
228229 after = utime .ticks_ms ()
229230 #result = bytearray("INSERT_QR_HERE", "utf-8")
230231 if not result :
@@ -343,8 +344,10 @@ def handle_settings_result(self, result):
343344 # Note: image_dsc is an LVGL struct, use attribute access not dictionary access
344345 self .image_dsc .header .w = self .width
345346 self .image_dsc .header .h = self .height
346- self .image_dsc .header .stride = self .width * 2
347- self .image_dsc .data_size = self .width * self .height * 2
347+ #self.image_dsc.header.stride = self.width * 2 # RGB565
348+ #self.image_dsc.data_size = self.width * self.height * 2 #RGB565
349+ self .image_dsc .header .stride = self .width
350+ self .image_dsc .data_size = self .width * self .height
348351 print (f"Image descriptor updated to { self .width } x{ self .height } " )
349352
350353 # Reconfigure camera if active
@@ -379,25 +382,23 @@ def try_capture(self, event):
379382 try :
380383 if self .use_webcam :
381384 self .current_cam_buffer = webcam .capture_frame (self .cam , "rgb565" )
382- #self.current_cam_buffer_copy = bytes(self.current_cam_buffer)
383385 elif self .cam .frame_available ():
384386 self .cam .free_buffer ()
385387 self .current_cam_buffer = self .cam .capture ()
386- #self.current_cam_buffer_copy = bytes(self.current_cam_buffer)
387- self .cam .free_buffer ()
388+ #self.cam.free_buffer()
388389
389390 if self .current_cam_buffer and len (self .current_cam_buffer ):
390391 # Defensive check: verify buffer size matches expected dimensions
391- expected_size = self .width * self .height * 2 # RGB565 = 2 bytes per pixel
392+ #expected_size = self.width * self.height * 2 # RGB565 = 2 bytes per pixel
393+ expected_size = self .width * self .height # Grayscale = 1 byte per pixel
392394 actual_size = len (self .current_cam_buffer )
393395
394396 if actual_size == expected_size :
395- #self.image_dsc.data = self.current_cam_buffer_copy
396397 self .image_dsc .data = self .current_cam_buffer
397398 #image.invalidate() # does not work so do this:
398399 self .image .set_src (self .image_dsc )
399- if not self .use_webcam :
400- self .cam .free_buffer () # Free the old buffer
400+ # if not self.use_webcam:
401+ # self.cam.free_buffer() # Free the old buffer
401402 try :
402403 if self .keepliveqrdecoding :
403404 self .qrdecode_one ()
@@ -443,6 +444,7 @@ def init_internal_cam(width, height):
443444 (1024 ,1024 ): FrameSize .R1024X1024 ,
444445 (1280 , 720 ): FrameSize .HD ,
445446 (1280 , 1024 ): FrameSize .SXGA ,
447+ (1280 , 1280 ): FrameSize .R1280X1280 ,
446448 (1600 , 1200 ): FrameSize .UXGA ,
447449 (1920 , 1080 ): FrameSize .FHD ,
448450 }
@@ -465,7 +467,8 @@ def init_internal_cam(width, height):
465467 xclk_freq = 20000000 ,
466468 powerdown_pin = - 1 ,
467469 reset_pin = - 1 ,
468- pixel_format = PixelFormat .RGB565 ,
470+ #pixel_format=PixelFormat.RGB565,
471+ pixel_format = PixelFormat .GRAYSCALE ,
469472 frame_size = frame_size ,
470473 grab_mode = GrabMode .WHEN_EMPTY ,
471474 fb_count = 1
@@ -674,6 +677,7 @@ class CameraSettingsActivity(Activity):
674677 ("1024x1024" ,"1024x1024" ),
675678 ("1280x720" , "1280x720" ), # binned 2x2 (in default ov5640.c)
676679 ("1280x1024" , "1280x1024" ),
680+ ("1280x1280" , "1280x1280" ),
677681 ("1600x1200" , "1600x1200" ),
678682 ("1920x1080" , "1920x1080" ),
679683 ]
@@ -933,30 +937,30 @@ def create_advanced_tab(self, tab, prefs):
933937
934938 # Auto Exposure Control (master switch)
935939 exposure_ctrl = prefs .get_bool ("exposure_ctrl" , True )
936- checkbox , cont = self .create_checkbox (tab , "Auto Exposure" , exposure_ctrl , "exposure_ctrl" )
937- self .ui_controls ["exposure_ctrl" ] = checkbox
940+ aec_checkbox , cont = self .create_checkbox (tab , "Auto Exposure" , exposure_ctrl , "exposure_ctrl" )
941+ self .ui_controls ["exposure_ctrl" ] = aec_checkbox
938942
939943 # Manual Exposure Value (dependent)
940944 aec_value = prefs .get_int ("aec_value" , 300 )
941- slider , label , cont = self .create_slider (tab , "Manual Exposure" , 0 , 1200 , aec_value , "aec_value" )
942- self .ui_controls ["aec_value" ] = slider
945+ me_slider , label , cont = self .create_slider (tab , "Manual Exposure" , 0 , 1200 , aec_value , "aec_value" )
946+ self .ui_controls ["aec_value" ] = me_slider
943947
944948 # Set initial state
945949 if exposure_ctrl :
946- slider .add_state (lv .STATE .DISABLED )
947- slider .set_style_bg_opa (128 , 0 )
950+ me_slider .add_state (lv .STATE .DISABLED )
951+ me_slider .set_style_bg_opa (128 , 0 )
948952
949953 # Add dependency handler
950954 def exposure_ctrl_changed (e ):
951- is_auto = checkbox .get_state () & lv .STATE .CHECKED
955+ is_auto = aec_checkbox .get_state () & lv .STATE .CHECKED
952956 if is_auto :
953- slider .add_state (lv .STATE .DISABLED )
954- slider .set_style_bg_opa (128 , 0 )
957+ me_slider .add_state (lv .STATE .DISABLED )
958+ me_slider .set_style_bg_opa (128 , 0 )
955959 else :
956- slider .remove_state (lv .STATE .DISABLED )
957- slider .set_style_bg_opa (255 , 0 )
960+ me_slider .remove_state (lv .STATE .DISABLED )
961+ me_slider .set_style_bg_opa (255 , 0 )
958962
959- checkbox .add_event_cb (exposure_ctrl_changed , lv .EVENT .VALUE_CHANGED , None )
963+ aec_checkbox .add_event_cb (exposure_ctrl_changed , lv .EVENT .VALUE_CHANGED , None )
960964
961965 # Auto Exposure Level
962966 ae_level = prefs .get_int ("ae_level" , 0 )
@@ -970,8 +974,8 @@ def exposure_ctrl_changed(e):
970974
971975 # Auto Gain Control (master switch)
972976 gain_ctrl = prefs .get_bool ("gain_ctrl" , True )
973- checkbox , cont = self .create_checkbox (tab , "Auto Gain" , gain_ctrl , "gain_ctrl" )
974- self .ui_controls ["gain_ctrl" ] = checkbox
977+ agc_checkbox , cont = self .create_checkbox (tab , "Auto Gain" , gain_ctrl , "gain_ctrl" )
978+ self .ui_controls ["gain_ctrl" ] = agc_checkbox
975979
976980 # Manual Gain Value (dependent)
977981 agc_gain = prefs .get_int ("agc_gain" , 0 )
@@ -983,7 +987,7 @@ def exposure_ctrl_changed(e):
983987 slider .set_style_bg_opa (128 , 0 )
984988
985989 def gain_ctrl_changed (e ):
986- is_auto = checkbox .get_state () & lv .STATE .CHECKED
990+ is_auto = agc_checkbox .get_state () & lv .STATE .CHECKED
987991 gain_slider = self .ui_controls ["agc_gain" ]
988992 if is_auto :
989993 gain_slider .add_state (lv .STATE .DISABLED )
@@ -992,22 +996,21 @@ def gain_ctrl_changed(e):
992996 gain_slider .remove_state (lv .STATE .DISABLED )
993997 gain_slider .set_style_bg_opa (255 , 0 )
994998
995- checkbox .add_event_cb (gain_ctrl_changed , lv .EVENT .VALUE_CHANGED , None )
999+ agc_checkbox .add_event_cb (gain_ctrl_changed , lv .EVENT .VALUE_CHANGED , None )
9961000
9971001 # Gain Ceiling
9981002 gainceiling_options = [
9991003 ("2X" , 0 ), ("4X" , 1 ), ("8X" , 2 ), ("16X" , 3 ),
10001004 ("32X" , 4 ), ("64X" , 5 ), ("128X" , 6 )
10011005 ]
10021006 gainceiling = prefs .get_int ("gainceiling" , 0 )
1003- dropdown , cont = self .create_dropdown (tab , "Gain Ceiling:" , gainceiling_options ,
1004- gainceiling , "gainceiling" )
1007+ dropdown , cont = self .create_dropdown (tab , "Gain Ceiling:" , gainceiling_options , gainceiling , "gainceiling" )
10051008 self .ui_controls ["gainceiling" ] = dropdown
10061009
10071010 # Auto White Balance (master switch)
10081011 whitebal = prefs .get_bool ("whitebal" , True )
1009- checkbox , cont = self .create_checkbox (tab , "Auto White Balance" , whitebal , "whitebal" )
1010- self .ui_controls ["whitebal" ] = checkbox
1012+ wbcheckbox , cont = self .create_checkbox (tab , "Auto White Balance" , whitebal , "whitebal" )
1013+ self .ui_controls ["whitebal" ] = wbcheckbox
10111014
10121015 # White Balance Mode (dependent)
10131016 wb_mode_options = [
@@ -1021,14 +1024,14 @@ def gain_ctrl_changed(e):
10211024 dropdown .add_state (lv .STATE .DISABLED )
10221025
10231026 def whitebal_changed (e ):
1024- is_auto = checkbox .get_state () & lv .STATE .CHECKED
1027+ is_auto = wbcheckbox .get_state () & lv .STATE .CHECKED
10251028 wb_dropdown = self .ui_controls ["wb_mode" ]
10261029 if is_auto :
10271030 wb_dropdown .add_state (lv .STATE .DISABLED )
10281031 else :
10291032 wb_dropdown .remove_state (lv .STATE .DISABLED )
10301033
1031- checkbox .add_event_cb (whitebal_changed , lv .EVENT .VALUE_CHANGED , None )
1034+ wbcheckbox .add_event_cb (whitebal_changed , lv .EVENT .VALUE_CHANGED , None )
10321035
10331036 # AWB Gain
10341037 awb_gain = prefs .get_bool ("awb_gain" , True )
0 commit comments