Skip to content

Instantly share code, notes, and snippets.

@reagames
Forked from hightemp/Seek_2.0.py
Last active April 26, 2023 21:03
Show Gist options
  • Save reagames/5bab03f0706b2f7d2b6c79b72e6a1ca3 to your computer and use it in GitHub Desktop.
Save reagames/5bab03f0706b2f7d2b6c79b72e6a1ca3 to your computer and use it in GitHub Desktop.
Python script for Raspberry Pi to run Seek Thermal Camera, with openCV and pygame output - based on Seek by Cynfab http://www.eevblog.com/forum/thermal-imaging/yet-another-cheap-thermal-imager-incoming/msg571129/#msg571129
###########################################################################
#
#
# This is a program to read thermal image data from the Seek PIR206 Thermal Camera
# This program is intended to be used with Raspberry Pi
# Pygame allows to output directly to framebuffer.
# Here I use small TFT display connected to GPIO and sending data to framebuffer fb1 for output to this display.
# I also use three simple GPIO buttons to have realtime control for image processing parameters
# This comes particularly useful e.g. when you use Seek at low temperature (you will need to lower down "lower threshold limit"
# And you can change color palettes with a click of button
# Menu use: middle button - cycle through menu items
# 1 - TFT screen backlight
# 2 - Lower threshold limit (lowest temperature reading to show on display)
# 3 - Upper threshold limit (lowest temperature reading which will be shown as 100% white (255) pixel
# 4 - Calibration adjuctment (how calibration impacts the ID3 image data
# 5 - Calibration delay in frames count
# 6 - Color palette mode (can use openCV palettes or Custom look up table (LUT) based palette
# You can add other modes with increasing UiMaxModes parameter
# In this version of code the calibration is done every N frames, where N is counterAct parameter, which can be adjusted realtime using buttons
#
# Raspberry Pi 3 runs this processing realtime, Pi Zero rather slowly
# You will need to have python 2.7
# and PyUSB 1.0 (needs to be gotten as source from Github and installed as root)
# and PIL (Pillow fork, often in debian distros)
# and numpy (often in debian base distros)
# and scipy
# and OpenCV
# and pygame
# and maybe some other stuff
# You have to run this as root (sudo python)
# Many thanks to the folks at eevblog, especially (in no particular order)
# Cynfab (code including comments is heavily used here as this is a fork of original code)
# miguelvp, marshallh, mikeselectricstuff, sgstair, Fry-kun, frenky and many others
# Use this product as you like and at your own risk :)
###########################################################################
###########################################################################
#
#Based on beautiful code from cynfab.
#This is a port for fbtft-enabled Raspberry Pi to output directly to connected tft display.
#Particularly in this case it's a display with ST7735 controller.
#Pygame is used to pass image data stream to fbtft driver.
#Display is custom wired (as described in fbtft wiki for 1.8 Adafruit display but backlight pin is changed to apply PWM for dimming)
#
###########################################################################
#Core imports
import usb.core
import usb.util
import sys
import cv2
from PIL import Image
import numpy
from numpy import array
from scipy.misc import toimage
from scipy import ndimage
import matplotlib
import math
#external module imports
import RPi.GPIO as GPIO
import time
#import fbtft driver
import pygame, sys, os
from pygame.locals import *
#Reload display driver
command = 'sudo modprobe -r fbtft_device'
p = os.system('sudo %s' % (command))
print "display reset"
time.sleep(0.1) # delay for 0.1 seconds
#Initialize display
command = 'sudo modprobe fbtft_device custom name=adafruit18 width=128 height=160 rotate=90'
p = os.system('sudo %s' % (command))
time.sleep(0.1)
print "display started"
#init pygame output
os.environ["SDL_FBDEV"] = "/dev/fb1"
pygame.init()
MAINSURF=pygame.display.set_mode((160, 128),0,32)
DISPLAYSURF=pygame.Surface((160, 128))
pygame.mouse.set_visible(0)
# set up the colors BGR
BLACK = ( 0, 0, 0)
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = ( 0, 255, 0)
RED = ( 255, 0, 0)
UIGREEN = (8, 150, 8 )
UIRED = (220, 77 , 48)
UIBLUE = (20, 80, 160)
UIWHITE = (128, 128 , 128)
UIGRAY = (50, 50 , 50)
#init pygame timer
clock = pygame.time.Clock()
#global calibration array variables
imcalib = 0
imgain = 0
#frame variables
counter=0
mode=0
try:
import pygame.surfarray as surfarray
except ImportError:
raise ImportError, "pygame failed to initialize"
#pygame font init
pygame.font.init()
myfont = pygame.font.SysFont('Nimbus Sans', 15)
#GUI variables
uiMenuItem = 0
uiSelector = 0
uiTimer=0
uiVisible = 0
scl=1
scl1=26
uiCrosshair=0
uiColormode=0
uiColormodesMax=5
uiMaxItems=7
redlight=0
pal=200
killtimer=0
actnum=0
dbgX=292
dbgY=0
dc = 50 # duty cycle (0-100) for PWM backlight pin
#GPIO Pin definitons: make sure they are not mixed with tft connections
fwPin = 16 # Broadcom pin 16 - p36 R3
midPin = 20 # Broadcom pin 20 - p38 R3
revPin = 21 # Broadcom pin 17 - p40 R3
ledPin = 26 # Broadcom pin 26 - p37 R3
GPIO.cleanup() # cleanup all GPIO
GPIO.setmode(GPIO.BCM) # Broadcom pin-numbering scheme
GPIO.setup(ledPin, GPIO.OUT) # PWM pin set as output
pwm = GPIO.PWM(ledPin, 50) # Initialize PWM on pwmPin 50Hz frequency
pwm.start(dc)
#Setup GPIO buttons
GPIO.setup(fwPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Button pin set as input w/ pull-up
GPIO.setup(midPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Button pin set as input w/ pull-up
GPIO.setup(revPin, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Button pin set as input w/ pull-up
#Thermal camera class
class Thermal:
def close(event):
print("closing...")
GPIO.cleanup() # cleanup all GPIO
sys.exit() # if you want to exit the entire thing
# defs
def usbinit(self):
# find our Seek Thermal device 289d:0010
dev = usb.core.find(idVendor=0x289d, idProduct=0x0010)
# was it found?
if dev is None:
raise ValueError('Device not found')
# set the active configuration. With no arguments, the first
# configuration will be the active one
dev.set_configuration()
# get an endpoint instance
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
assert ep is not None
return dev
# send_msg sends a message that does not need or get an answer
def send_msg(self,dev,bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None):
assert (dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) == len(data_or_wLength))
# alias method to make code easier to read
# receive msg actually sends a message as well.
def receive_msg(self,dev,bmRequestType, bRequest, wValue=0, wIndex=0, data_or_wLength=None, timeout=None):
zz = dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) # == len(data_or_wLength))
return zz
# De-init the device
def deinit(self,dev):
msg = '\x00\x00'
for i in range(3):
self.send_msg(dev,0x41, 0x3C, 0, 0, msg) # 0x3c = 60 Set Operation Mode 0x0000 (Sleep)
# Camera initilization
def camerainit(self,dev):
try:
msg = '\x01'
self.send_msg(dev,0x41, 0x54, 0, 0, msg) # 0x54 = 84 Target Platform 0x01 = Android
except Exception as e:
self.deinit(dev)
msg = '\x01'
self.send_msg(dev,0x41, 0x54, 0, 0, msg) # 0x54 = 84 Target Platform 0x01 = Android
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x00\x00') # 0x3c = 60 Set operation mode 0x0000 (Sleep)
ret1 = self.receive_msg(dev,0xC1, 0x4E, 0, 0, 4) # 0x4E = 78 Get Firmware Info
#print ret1
#array('B', [1, 3, 0, 0])
#ret2 = self.receive_msg(dev,0xC1, 0x36, 0, 0, 12) # 0x36 = 54 Read Chip ID
#print ret2
#array('B', [20, 0, 12, 0, 86, 0, 248, 0, 199, 0, 69, 0])
self.send_msg(dev,0x41, 0x56, 0, 0, '\x20\x00\x30\x00\x00\x00') # 0x56 = 86 Set Factory Settings Features
#ret3 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x40) # 0x58 = 88 Get Factory Settings
#print ret3
#array('B', [2, 0, 0, 0, 0, 112, 91, 69, 0, 0, 140, 65, 0, 0, 192, 65, 79, 30, 86, 62, 160, 137, 64, 63, 234, 149, 178, 60, 0, 0, 0, 0, 0, 0, 0, 0, 72, 97, 41, 66, 124, 13, 1, 61, 206, 70, 240, 181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 66, 0, 0, 2, 67])
self.send_msg(dev,0x41, 0x56, 0, 0, '\x20\x00\x50\x00\x00\x00') # 0x56 = 86 Set Factory Settings Features
#ret4 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x40) # 0x58 = 88 Get Factory Settings
#print ret4
#array('B', [0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 161, 248, 65, 63, 40, 127, 119, 60, 44, 101, 55, 193, 240, 133, 129, 63, 244, 253, 96, 66, 40, 15, 155, 63, 43, 127, 103, 186, 9, 144, 186, 52, 0, 0, 0, 0, 0, 0, 2, 67, 0, 0, 150, 67, 0, 0, 0, 0])
self.send_msg(dev,0x41, 0x56, 0, 0, '\x0C\x00\x70\x00\x00\x00') # 0x56 = 86 Set Factory Settings Features
#ret5 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x18) # 0x58 = 88 Get Factory Settings
#print ret5
#array('B', [0, 0, 0, 0, 255, 255, 255, 255, 190, 193, 249, 65, 205, 204, 250, 65, 48, 42, 177, 191, 200, 152, 147, 63])
self.send_msg(dev,0x41, 0x56, 0, 0, '\x06\x00\x08\x00\x00\x00') # 0x56 = 86 Set Factory Settings Features
#ret6 = self.receive_msg(dev,0xC1, 0x58, 0, 0, 0x0C) # 0x58 = 88 Get Factory Settings
#print ret6
#array('B', [49, 52, 48, 99, 49, 48, 69, 52, 50, 78, 55, 49])
self.send_msg(dev,0x41, 0x3E, 0, 0, '\x08\x00') # 0x3E = 62 Set Image Processing Mode 0x0008 Normal, 0x00 no shutter, other modes also possible, try at you own risk
#ret7 = self.receive_msg(dev,0xC1, 0x3D, 0, 0, 2) # 0x3D = 61 Get Operation Mode
#print ret7
#array('B', [0, 0])
self.send_msg(dev,0x41, 0x37, 0, 0, '\x00\x00') # 0x37 = 55 Toggle shutter
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x01\x00') # 0x3c = 60 Set Operation Mode 0x0001 (Run)
#ret8 = self.receive_msg(dev,0xC1, 0x3D, 0, 0, 2) # 0x3D = 61 Get Operation Mode
#print ret8
#array('B', [1, 0])
def read_frame(self,dev): # Send a read frame request
self.send_msg(dev,0x41, 0x53, 0, 0, '\xC0\x7E\x00\x00') # 0x53 = 83 Set Start Get Image Transfer
try:
data = dev.read(0x81, 0x3F60, 1000)
data += dev.read(0x81, 0x3F60, 1000)
data += dev.read(0x81, 0x3F60, 1000)
data += dev.read(0x81, 0x3F60, 1000)
except usb.USBError as e:
print "device error"
GPIO.cleanup() # cleanup all GPIO
sys.exit()
return data
def set_fast(self,dev): #set no shutter mode
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x00\x00') # 0x3c = 60 Set operation mode 0x0000 (Sleep)
time.sleep(0.05)
self.send_msg(dev,0x41, 0x3E, 0, 0, '\x00\x00') #send image processing mode change to No Shutter
time.sleep(0.05)
print "mode changed to NS"
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x01\x00') # 0x3c = 60 Set operation mode 0x0001 (Run)
return
def set_normal(self,dev): #set normal frame mode
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x00\x00') # 0x3c = 60 Set operation mode 0x0000 (Sleep)
time.sleep(0.01)
self.send_msg(dev,0x41, 0x3E, 0, 0, '\x08\x00') #send image processing mode change
self.send_msg(dev,0x41, 0x37, 0, 0, '\x00\x00')
time.sleep(0.01)
#print "mode changed to normal"
self.send_msg(dev,0x41, 0x3C, 0, 0, '\x01\x00') # 0x3c = 60 Set operation mode 0x0001 (Run)
return
###################################
#--------------------*************-------------------#
# Start of main routine
#--------------------*************-------------------#
# Main program starts here (you can tell I'm new to Python ;)
#Initialization
def initialize(self):
global dev, scl, scl1, scl2
global calImage, calImagex, pal, snapshot, killtimer
# Default palette is "iron"
snapshot = 0
# Set up device
dev = self.usbinit()
self.camerainit(dev)
# get a cal image so the data isn't null if/when we miss the first one
self.get_cal_image(dev)
# var to store adj scl1 value
# self.topadj=0
#init fast frame mode
self.set_fast(dev)
# Start the update image routine with defined delay
self.UpdateImage(0.001)
# End of the initilization routine
#--------------------*************-------------------#
# This is actually the main loop
#--------------------*************-------------------#
def UpdateImage(self, delay, event=None):
global scl, scl1, dev, status, calImage, label, dc, pal, counter, mode, actnum
global uiMenuItem, uiSelector, uiTimer, uiVisible, uiMaxItems, redlight, uiCrosshair, dbgX, uiColormode, uiColormodesMax, killtimer
while 1:
#time.sleep(0.001)
self.image = self.get_image(dev)
#increase frame counter to enable mode change
counter+=1
if mode==0: #ff mode
if counter>180:
self.set_normal(dev)
mode=1
counter=0
elif mode==1:
#if counter>0:
self.get_cal_image(dev)
self.set_fast(dev)
mode=0
counter=0
#go on with GPIO
if GPIO.input(midPin): # button is released
uiSelector=0
killtimer=0
else:
#shutdown function
killtimer+=1
actnum = killtimer
if killtimer>=50:
killtimer=0
print "shutdown"
command = 'sudo shutdown -h now'
p = os.system('sudo %s' % (command))
if uiSelector==0:
uiSelector = 1
#cycle through menu items
uiMenuItem += 1
if uiMenuItem > uiMaxItems:
uiMenuItem=1
uiTimer = 20
print(uiMenuItem)
#Toggle menu visibility
if uiTimer>0:
uiTimer-=1
uiVisible=1
if uiTimer<=0:
uiVisible=0
uiMenuItem=0
#________GUI_________#
if uiVisible==1:
#button controls within menu items
# place text on framebuffer screen
text2 = myfont.render('%d' %(actnum), False, (0, 255, 0))
newtextsurface=pygame.transform.rotate(text2,180)
MAINSURF.blit(newtextsurface,(20,50))
if uiMenuItem==1: #LCD brightness
actnum = dc
#Control lines
pygame.draw.line(MAINSURF, UIGREEN, (10, 10), (100, 10),2)#
pygame.draw.line(MAINSURF, UIWHITE, (10, 20), (100, 20),1)
pygame.draw.line(MAINSURF, UIWHITE, (10, 30), (100, 30),1)
pygame.draw.line(MAINSURF, UIWHITE, (10, 40), (100, 40),1)
pygame.draw.circle(MAINSURF, UIBLUE, (100-dc, 10), 3, 0)#
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl, 20), 2, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl1, 30), 2, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100- int(0.08 * dbgX), 40), 2, 0)
#Handle +/- button clicks
if GPIO.input(fwPin)==0:
uiTimer = 20
dc+=1
if dc>=95:
dc=95
pwm.ChangeDutyCycle(dc)
actnum = dc
elif GPIO.input(revPin)==0:
uiTimer = 20
dc-=1
if dc<=2:
dc=2
pwm.ChangeDutyCycle(dc)
actnum = dc
if uiMenuItem==2:#Low contrast threshold
actnum = scl
pygame.draw.line(MAINSURF, UIWHITE, (10, 10), (100, 10),1)
pygame.draw.line(MAINSURF, UIGREEN, (10, 20), (100, 20),2)
pygame.draw.line(MAINSURF, UIWHITE, (10, 30), (100, 30),1)
pygame.draw.line(MAINSURF, UIWHITE, (10, 40), (100, 40),1)
pygame.draw.circle(MAINSURF, UIWHITE, (100-dc, 10), 2, 0)
pygame.draw.circle(MAINSURF, UIBLUE, (100-scl, 20), 3, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl1,30), 2, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100- int(0.08 * dbgX), 40), 2, 0)
#Handle +/- button clicks
if GPIO.input(fwPin)==0:
uiTimer = 20
scl+=1
if scl>=100:
scl=100
actnum = scl
elif GPIO.input(revPin)==0:
uiTimer = 20
scl-=1
if scl<=1:
scl=1
actnum = scl
if uiMenuItem==3:#High Contrast threshold
actnum = scl1
pygame.draw.line(MAINSURF, UIWHITE, (10, 10), (100, 10),1)
pygame.draw.line(MAINSURF, UIWHITE, (10, 20), (100, 20),1)#
pygame.draw.line(MAINSURF, UIGREEN, (10, 30), (100, 30),2)
pygame.draw.line(MAINSURF, UIWHITE, (10, 40), (100, 40),1)
#pygame.draw.line(DISPLAYSURF, UIWHITE, (10, 40), (110, 40),1)
pygame.draw.circle(MAINSURF, UIWHITE, (100-dc, 10), 2, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl, 20), 2, 0)#
pygame.draw.circle(MAINSURF, UIBLUE, (100-scl1, 30), 3, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100- int(0.08 * dbgX), 40), 2, 0)
#Handle +/- button clicks
if GPIO.input(fwPin)==0:
uiTimer = 20
scl1+=1
if scl1>=100:
scl1=100
actnum = scl1
elif GPIO.input(revPin)==0:
uiTimer = 20
scl1-=1
if scl1<=1:
scl1=1
actnum = scl1
if uiMenuItem==4:#calibration threshold
actnum = dbgX
pygame.draw.line(MAINSURF, UIWHITE, (10, 10), (100, 10),1)
pygame.draw.line(MAINSURF, UIWHITE, (10, 20), (100, 20),1)#
pygame.draw.line(MAINSURF, UIWHITE, (10, 30), (100, 30),1)
pygame.draw.line(MAINSURF, UIGREEN, (10, 40), (100, 40),2)
pygame.draw.circle(MAINSURF, UIWHITE, (100-dc, 10), 2, 0)
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl, 20), 2, 0)#
pygame.draw.circle(MAINSURF, UIWHITE, (100-scl1, 30), 2, 0)
pygame.draw.circle(MAINSURF, UIBLUE, (100- int(0.08 * dbgX), 40), 3, 0)
#Handle +/- button clicks
if GPIO.input(fwPin)==0:
uiTimer = 20
dbgX+=4
if dbgX>=800:
dbgX=800
actnum = dbgX
elif GPIO.input(revPin)==0:
uiTimer = 20
dbgX-=4
if dbgX<=0:
dbgX=0
actnum = dbgX
#Draw crosshair
pygame.draw.line(MAINSURF, UIRED, (58, 55), (62, 55),1)
pygame.draw.line(MAINSURF, UIRED, (60, 53), (60, 57),1)
if killtimer>10:
pygame.draw.circle(MAINSURF, UIGREEN, (60, 55), killtimer, 10)
if killtimer>40:
pygame.draw.circle(MAINSURF,UIRED, (60, 55), killtimer, 10)
#After all, do some display update
pygame.display.update()
#--------------------*************-------------------#
# End of main loop
#--------------------*************-------------------#
def get_cal_image(self,dev):
# Get the first cal image so calImage isn't null
global status, calImage, calImagex, calimgI, imcalib
status = 0
while status != 1:
# Read a raw frame
ret9 = self.read_frame(dev)
status = ret9[20]
status1 = ret9[80]
#print (status , status1)
# Convert the raw 16 bit calibration data to a PIL Image
calimgI = Image.frombytes("F", (208,156), ret9, "raw", "F;16")
#Convert the PIL Image to an unsigned numpy float array
im2arr = numpy.asarray(calimgI)
#clamp values < 2000 to 2000
im2arr = numpy.where(im2arr < 2000, 2000, im2arr)
im2arrF = im2arr.astype('float')
calImage = im2arrF
imcalib=calImage
return
def get_image(self,dev):
global calImage,calimgI, calImagex, status, scl, scl1, pal, snapshot, imcalib, imgain, actnum
global uiMenuItem, uiSelector, uiTimer, uiVisible, uiCrosshair, dbgX, uiColormode
global MAINSURF
status = 0
# Wait for the next image frame, ID = 3 is a Normal frame
while status != 3:
# Read a raw frame
ret9 = self.read_frame(dev)
status = ret9[20]
#print status
# check for a new cal frame, if so update the cal image
if status == 1:
#Convert the raw 16 bit calibration data to a PIL Image
calimgI = Image.frombytes("F", (208,156), ret9, "raw", "F;16")
#Convert the PIL Image to an unsigned numpy float array
im2arr = numpy.asarray(calimgI)
# clamp values < 2000 to 2000
im2arr = numpy.where(im2arr < 2000, 2000, im2arr)
im2arrF = im2arr.astype('float')
# Clamp pixel 40 to 2000 so it doesn't cause havoc as it rises to 65535
im2arrF[0,40] = 2000
#Add the row 207 correction (maybe) >>Looks like it needs to be applied to just the cal frame<<
#self.add_207(im2arrF)
#Zero out column 207
#im2arrF[:,206] = numpy.zeros(156)
#Save the calibration image
calImage = im2arrF
imcalib = calImage
print "calibrated"
#If this is normal image data
#Convert the raw 16 bit thermal data to a PIL Image
#imgx = Image.fromstring("F", (208,156), ret9, "raw", "F;16")
imgx = Image.fromstring("F", (208,156), ret9, "raw", "F;16N")
#Convert the PIL Image to an unsigned numpy float array
im1arr = numpy.asarray(imgx)
#clamp values < 2000 to 2000
im1arr = numpy.where(im1arr < 2000, 2000, im1arr)
im1arrF = im1arr.astype('float')
#Clamp pixel 40 to 2000 so it doesn't cause havoc as it rises to 65535
im1arrF[0,40] = 2000
#Subtract the most recent calibration image from the offset image data
#With both the cal and image as floats, the offset doesn't matter and
#the following image conversion scales the result to display properly
additionF = (im1arrF) + 200 + dbgX - calImage
#Crop or resize image to match display (pygame surface) area
#noiselessF = additionF[14:142, 24:184]
noiselessF = cv2.resize(additionF,(160, 128), interpolation = cv2.INTER_LINEAR)
bottom = scl
top = scl1
display_min = bottom * 4
display_max = top * 16
noiselessF.clip(display_min, display_max, out=noiselessF)
noiselessF -= display_min
noiselessF //= (display_max - display_min + 1) / 256.
#convert and denoise image
noiselessF = noiselessF.astype(numpy.uint8)
noiselessF = ndimage.median_filter(noiselessF, 3)
#convert color and rotate
noiselessF = cv2.cvtColor(noiselessF,cv2.COLOR_GRAY2RGB)
noiselessF = numpy.rot90(noiselessF,1)
noiselessF = numpy.flipud(noiselessF)
#fill image to pygame surface
dst_ary = pygame.surfarray.pixels3d(DISPLAYSURF)
dst_ary[...] = noiselessF
del dst_ary
#DISPLAYSURF.unlock()
MAINSURF.blit(DISPLAYSURF,(0,0))
#count fps
clock.tick()
fps = clock.get_fps()
print fps
# place text on framebuffer screen
text = myfont.render('FPS: %d' %(fps), False, (255, 0, 0))
newtextsurface=pygame.transform.rotate(text,180)
MAINSURF.blit(newtextsurface,(118,110))
App=Thermal()
App.initialize()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment