99 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			99 lines
		
	
	
	
		
			4.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import tkinter
 | |
| import cv2
 | |
| from PIL import Image
 | |
| import numpy as np
 | |
| import PySimpleGUI as sg
 | |
| 
 | |
| font_size = 3
 | |
| CAMERA_FRONT = 1
 | |
| CAMERA_REAR = 0
 | |
| 
 | |
| USE_CAMERA = CAMERA_REAR
 | |
| 
 | |
| """
 | |
| Interesting program that shows your webcam's image as ASCII text. Runs in realtime, producing a stream of
 | |
| images so that it is actually animated ASCII text. Wild stuff that came about from a post on Reddit of all
 | |
| places. The software bits that turn the image into ASCII text were shamelessly taken from this gist:
 | |
| https://gist.github.com/cdiener/10491632
 | |
| Brilliant work to have pulled off so much with so little Numpy
 | |
| What's remarkable about this program is that the animation is created by updating individual Text Elements going
 | |
| down the window, one line at a time, every time through the loop. That's 48 lines of text every time. Rough
 | |
| timing shows an animation of more than 10 fps when running any of the PySimpleGUI ports.
 | |
| Also added onto this are a spinner and a slider. They do essentially the same thing, enable a pair of parameters
 | |
| to be modified on the fly.
 | |
| 
 | |
| You need PySimpleGUI installed as well as OpenCV. Both are easily installed via pip:
 | |
| pip install PySimpleGUI
 | |
| pip install opencv-python
 | |
| 
 | |
| On Linux / Mac use pip3 instead of pip
 | |
| """
 | |
| 
 | |
| # The magic bits that make the ASCII stuff work shamelessly taken from https://gist.github.com/cdiener/10491632
 | |
| chars = np.asarray(list(' .,:;irsXA253hMHGS#9B&@'))
 | |
| SC, GCF, WCF = .1, 1, 7/4
 | |
| 
 | |
| sg.change_look_and_feel('Dark Black 1') # make it look cool
 | |
| 
 | |
| # ---------------- A Quick Little Window - to get camera to use ----------------
 | |
| win_get_cam = sg.Window('Which Camera?',[[sg.T('Which camera?')],[sg.Combo(['Front', 'Rear'], default_value='Rear', font='any 20')],[sg.T(size=(1,2))], [sg.Ok()]], location=(0,0))
 | |
| event, values = win_get_cam.read()
 | |
| win_get_cam.close()
 | |
| if event != 'Ok': exit()
 | |
| USE_CAMERA = [CAMERA_FRONT, CAMERA_REAR][values[0]=='Rear']
 | |
| 
 | |
| # ------------ Turn on camera and read a frame to find the number of lines of text needed ---------
 | |
| cap = cv2.VideoCapture(USE_CAMERA)      # Setup camera using your camera choice
 | |
| 
 | |
| ret, frame = cap.read()
 | |
| 
 | |
| img = Image.fromarray(frame)    # create PIL image from frame
 | |
| GCF = 1.0                       # Variable user will be able to change
 | |
| WCF = 1.75                      # Variable user will be able to change
 | |
| # ----------- magic that coverts the image to ascii -----------
 | |
| S = (round(img.size[0] * SC * WCF), round(img.size[1] * SC))
 | |
| img = np.sum(np.asarray(img.resize(S)), axis=2)
 | |
| img -= img.min()
 | |
| img = (1.0 - img / img.max()) ** GCF * (chars.size - 1)
 | |
| 
 | |
| # Find number of lines of text that will be drawn so can create the right size of window
 | |
| NUM_LINES = len(chars[img.astype(int)])
 | |
| LINE_LENGTH = len("".join(chars[img.astype(int)][0]))
 | |
| print(f'line len = {LINE_LENGTH}')
 | |
| #  ------------- define the window layout -------------
 | |
| # number of lines of text elements. Depends on cameras image size and the variable SC (scaller)
 | |
| 
 | |
| layout = [[sg.Text(i, size=(LINE_LENGTH, 1), font=('Courier', font_size), pad=(0, 0), key='-OUT-'+str(i))] for i in range(NUM_LINES)]
 | |
| 
 | |
| layout += [[sg.Button('Exit', size=(5, 1)),
 | |
|             sg.Text('GCF', size=(4, 1)),
 | |
|             sg.Spin([round(i, 2) for i in np.arange(0.1, 20.0, 0.1)], initial_value=1, key='-SPIN-GCF-', size=(5, 1), font='any 20')],
 | |
|             [sg.Text('WCF', size=(4, 1)),
 | |
|             sg.Slider((1, 4), resolution=.05, default_value=1.75, orientation='h', key='-SLIDER-WCF-', size=(15, 30))]]
 | |
| 
 | |
| # ------------- create the window -------------
 | |
| window = sg.Window('Demo Application - OpenCV Integration', layout, location=(0,0))
 | |
| 
 | |
| # ---===--- Event LOOP Read and display frames, operate the GUI --- #
 | |
| while True:
 | |
| 
 | |
|     event, values = window.read(timeout=0)
 | |
|     if event in ('Exit', None):
 | |
|         break
 | |
|     # Read image from capture device (camera)
 | |
|     ret, frame = cap.read()
 | |
| 
 | |
|     img = Image.fromarray(frame) # create PIL image from frame
 | |
|     GCF = float(values['-SPIN-GCF-'])
 | |
|     WCF = values['-SLIDER-WCF-']
 | |
|     # More magic that coverts the image to ascii
 | |
|     S = (round(img.size[0] * SC * WCF), round(img.size[1] * SC))
 | |
|     img = np.sum(np.asarray(img.resize(S)), axis=2)
 | |
|     img -= img.min()
 | |
|     img = (1.0 - img / img.max()) ** GCF * (chars.size - 1)
 | |
| 
 | |
|     # "Draw" the image in the window, one line of text at a time!
 | |
|     for i, r in enumerate(chars[img.astype(int)]):
 | |
|         window['-OUT-'+str(i)].update("".join(r))
 | |
| 
 | |
| window.close()
 |