Seek Compact Pro python script? - Page 1
Forum section: electronics / general

author: pauledd
author_rank: Regular Contributor
timestamp: June 23, 2018, 07:48:36 am
content: Hi thereIs there perhaps someone who has a ready to use python script for talking to theSeek Compact Pro? I just want to output the raw frames, I dont care on palettes norother fancy after effects. There is already such a script for the normal Compact as I foundherebut it seems its is alot different for the initialisation part and image dimension...If not I will try to start step by step converting it here, maybe someone can help me with the stuff I dont understand..

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 07:55:07 am
content: Okay, I just start with the usb stuff and the initialisation routine.for the send/receive order I follow the code used inlibseek-thermal (SeekThermalPro.cpp):Code:import usb.coreimport usb.util#find Seek CompactPro device 289d:0011dev = usb.core.find(idVendor=0x289d, idProduct=0x0011)if not dev: raise ValueError('Device not found')def send_msg(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$# alias method to make code easier to readreceive_msg = dev.ctrl_transferdef deinit():'''Deinit the device'''msg = '\x00\x00'for i in range(3):send_msg(0x41, 0x3C, 0, 0, msg)# set the active configuration. With no arguments, the first configuration will be the active onedev.set_configuration()# get an endpoint instancecfg = dev.get_active_configuration()intf = cfg[(0,0)]custom_match = lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUTep = usb.util.find_descriptor(intf, custom_match=custom_match) # match the first OUT endpointassert ep is not None# Setup devicetry:msg = '\x01'send_msg(0x41, 0x54, 0, 0, msg)print "Setup try"except Exception as e:deinit()msg = '\x01'send_msg(0x41, 0x54, 0, 0, msg)print "Setup except"# SET_OPERATION_MODE = 60; 0x41=send,0x3C=60send_msg(0x41, 0x3C, 0, 0, '\x00\x00')# GET_FIRMWARE_INFO = 78, 0x4E=78ret1 = receive_msg(0xC1, 0x4E, 0, 0, 4)print "GET_FIRMWARE_INFO=",ret1# READ_CHIP_ID = 54, 0x36=54ret2 = receive_msg(0xC1, 0x36, 0, 0, 12)print "READ_CHIP_ID=",ret2# SET_FACTORY_SETTINGS_FEATURES = 86 = 0x56send_msg(0x41, 0x56, 0, 0, '\x06\x00\x08\x00\x00\x00')# GET_FACTORY_SETTINGS = 88 = 0x58ret3 = receive_msg(0xC1, 0x58, 0, 0, 12)print "GET_FACTORY_SETTINGS=",ret3# SET_FIRMWARE_INFO_FEATURES = 85 = 0x55send_msg(0x41, 0x55, 0, 0, '\x17\x00')# GET_FIRMWARE_INFO = 78 = 0x4Eret4 = receive_msg(0xC1, 0x4E, 0, 0, 64)print "GET_FIRMWARE_INFO=",ret4# SET_FACTORY_SETTINGS_FEATURES = 86 = 0x56send_msg(0x41, 0x56, 0, 0, '\x01\x00\x00\x06\x00\x00')# GET_FACTORY_SETTINGSret5 = receive_msg(0xC1, 0x58, 0, 0, 2)print "GET_FACTORY_SETTINGS=",ret5# SET_FACTORY_SETTINGS_FEATURES = 86 = 0x56send_msg(0x41, 0x56, 0, 0, '\x01\x00\x01\x06\x00\x00')# GET_FACTORY_SETTINGSret6 = receive_msg(0xC1, 0x58, 0, 0, 2)print "GET_FACTORY_SETTINGS=",ret6This, so far outputs this and seems to work:Code:Setup tryGET_FIRMWARE_INFO= array('B', [1, 0, 3, 0])READ_CHIP_ID= array('B', [6, 0, 16, 0, 141, 0, 211, 0, 93, 0, 209, 0])GET_FACTORY_SETTINGS= array('B', [48, 54, 49, 48, 65, 49, 74, 85, 76, 68, 53, 52])GET_FIRMWARE_INFO= array('B', [48, 120, 68, 49, 53, 68, 68, 51, 56, 68, 49, 48, 48, 54, 0, 0, 48, 54, 49, 48 65, 49, 74, 85, 76, 68, 53, 52, 0, 0, 0, 0, 121, 5, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])GET_FACTORY_SETTINGS= array('B', [0, 0])GET_FACTORY_SETTINGS= array('B', [1, 0])Next part will be to Send read frame request...In libseek-thermal this code comes next, which I dont fully understand yet:Code:uint16_t addr, addrle;uint8_t *addrle_p = reinterpret_cast<uint8_t*>(&addrle);for (addr=0; addr<2560; addr+=32) {{addrle = htole16(addr); /* mind endianness */std::vector<uint8_t> data = { 0x20, 0x00, addrle_p, addrle_p, 0x00, 0x00 };if (!m_dev.request_set(DeviceCommand::SET_FACTORY_SETTINGS_FEATURES, data))return false;}{std::vector<uint8_t> data(64);if (!m_dev.request_get(DeviceCommand::GET_FACTORY_SETTINGS, data))return false;print_usb_data(data);}}

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 08:23:25 am
content: obviously it loopes 2560 times in 32 step size, which should be 80 times, whilesending SET_FACTORY_SETTINGS_FEATURES requests with custom addrle_p, addrle_p things that have to do with little endian...lets see if one can translate that somehow to python

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 09:41:34 am
content: If my understanding is correct addr, addrle are unsigned int 0-65536. addrle is the little endian valueof addr, which is possibly can be made by this snipped of code:Code:addrle = '\\x' + '\\x'.join(x.encode('hex') for x in struct.pack('<I', addr))Then addrle is c++ reinterpret-casted from uint16 to uint8?Code:uint8_t *addrle_p = reinterpret_cast<uint8_t*>(&addrle);Currently dont know how to do that in python...

author: tonykids
author_rank: Regular Contributor
timestamp: June 24, 2018, 10:33:51 am
content: Do not think that too much,it's simpleJust loop the "m_dev.request_set(DeviceCommand::SET_FACTORY_SETTINGS_FEATURES,data)" 80 timesand for each time thedatabyte array is20 ,00 ,00 ,00 ,00 ,00<--the first time20 ,00 ,20 ,00 ,00 ,00<--the second time20 ,00 ,40 ,00 ,00 ,00..20 ,00 ,e0 ,00 ,00 ,0020 ,00 ,00 ,01 ,00 ,0020 ,00 ,20 ,01 ,00 ,00...20 ,00 ,c0 ,09 ,00 ,0020 ,00 ,e0 ,09 ,00 ,00<--the last timeSeehttps://www.eevblog.com/forum/thermal-imaging/windows-software-for-seek-pro-and-initial-commands/for more information

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 01:49:22 pm
content: thanks a lot,I always tend to think to muchI put it in a dilettante loop...Code:import numpy as npimport structaddrle = np.uint16(0)t= '\\20\\x00'for x in xrange(0,2560,32):addrle = '\\x' + '\\x'.join(x.encode('hex') for x in struct.pack('<I', x))a = t + addrleprint agives meCode:\20\x00\x00\x00\x00\x00\20\x00\x20\x00\x00\x00\20\x00\x40\x00\x00\x00\20\x00\x60\x00\x00\x00\20\x00\x80\x00\x00\x00\20\x00\xa0\x00\x00\x00\20\x00\xc0\x00\x00\x00\20\x00\xe0\x00\x00\x00\20\x00\x00\x01\x00\x00...\20\x00\xe0\x09\x00\x00now lets see if I can put it all together...

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 02:05:07 pm
content: I tried this loop:Code:addrle = np.uint16(0)t= '\\20\\x00'for x in xrange(0,2560,32):addrle = '\\x' + '\\x'.join(x.encode('hex') for x in struct.pack('<I', x))a = t + addrlesend_msg(0x41, 0x56, 0, 0, a)ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)print "receive ok at x", x, " =", aBut I get a pipe error:Code:...File "seek3.py", line 91, in <module>send_msg(0x41, 0x56, 0, 0, a)File "seek3.py", line 11, in send_msgassert (dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) == len(data_or_wLength))File "/usr/lib64/python2.7/site-packages/usb/core.py", line 1043, in ctrl_transferself.__get_timeout(timeout))File "/usr/lib64/python2.7/site-packages/usb/backend/libusb1.py", line 883, in ctrl_transfertimeout))File "/usr/lib64/python2.7/site-packages/usb/backend/libusb1.py", line 595, in _checkraise USBError(_strerror(ret), ret, _libusb_errno)usb.core.USBError: [Errno 32] Pipe errorI suspect I cannot simply add "a" to the end of the send_msg function?EDIT.. forgot one x in "t= '\\20\\x00'", but still get this errorEDIT2.. okay, sending only one "send_msg(0x41, 0x56, 0, 0, '\x20\x00\x00\x00\x00\x00')" succeeded without error...

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 04:47:23 pm
content: I dont get it, I first thought the double backslash would make trouble so I formated those parts as "raw" with that r"\x", then before I do send_msg I print the string and it looks fine again in the output but send_msg then failed... do I miss any timing parameterBecause it says "self.__get_timeout(timeout))"...Code:for x in xrange(0,2560,32):addrle = r"\x" + r"\x".join(x.encode('hex') for x in struct.pack('<I', x))print str(r'\x20\x00') + addrlesend_msg(0x41, 0x56, 0, 0, str(r'\x20\x00') + addrle)Code:\x20\x00\x00\x00\x00\x00Traceback (most recent call last):File "seek3.py", line 92, in <module>send_msg(0x41, 0x56, 0, 0, str(r'\x20\x00') + addrle)File "seek3.py", line 12, in send_msgassert (dev.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout) == len(data_or_wLength))File "/usr/lib64/python2.7/site-packages/usb/core.py", line 1043, in ctrl_transferself.__get_timeout(timeout))File "/usr/lib64/python2.7/site-packages/usb/backend/libusb1.py", line 883, in ctrl_transfertimeout))File "/usr/lib64/python2.7/site-packages/usb/backend/libusb1.py", line 595, in _checkraise USBError(_strerror(ret), ret, _libusb_errno)usb.core.USBError: [Errno 32] Pipe error

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 05:51:48 pm
content: hmm, seems to be a timing issue?If I sendCode:send_msg(0x41, 0x56, 0, 0,' \x20\x00\x00\x00\x00\x00 ')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)send_msg(0x41, 0x56, 0, 0,' \x20\x00\x20\x00\x00\x00 ')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)send_msg(0x41, 0x56, 0, 0,' \x20\x00\x40\x00\x00\x00 ')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)without looping I get no error, BUT just one more send_msg produces the error...So I can send only 3 of those requests

author: pauledd
author_rank: Regular Contributor
timestamp: June 24, 2018, 06:12:18 pm
content: next progress... I removed the whitespaces between the data:Code:send_msg(0x41, 0x56, 0, 0,'\x20\x00\x00\x00\x00\x00')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)send_msg(0x41, 0x56, 0, 0,'\x20\x00\x20\x00\x00\x00')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)send_msg(0x41, 0x56, 0, 0,'\x20\x00\x40\x00\x00\x00')ret7 = receive_msg(0xC1, 0x58, 0, 0, 64)...Now I get no more errors, but without loop, I manually filled the 80 sends... seems to be indeed a formatting problem in the loop...

author: pauledd
author_rank: Regular Contributor
timestamp: June 25, 2018, 06:59:24 am
content: I leave that weired loop and use the 80x send_msg/receive_msg, after that I now continuewith the four remaining commands:Code:# SET_FIRMWARE_INFO_FEATURES = 85 = 0x55send_msg(0x41, 0x55, 0, 0,'\x15\x00')# GET_FIRMWARE_INFO = 78 = 0x4Eret7 = receive_msg(0xC1, 0x4E, 0, 0, 64)# SET_IMAGE_PROCESSING_MODE = 62 = 0x3Esend_msg(0x41, 0x3E, 0, 0,'\x08\x00')# SET_OPERATION_MODE = 60 = 0x3Csend_msg(0x41, 0x3C, 0, 0,'\x01\x00')then the first frame should be taken...

author: pauledd
author_rank: Regular Contributor
timestamp: June 25, 2018, 08:12:00 am
content: Just a little bit offtopic, there seems to be a new Seek Thermal App Version 2.1.2, looks like there is new Firmware in it?

author: pauledd
author_rank: Regular Contributor
timestamp: June 25, 2018, 02:35:46 pm
content: Quote from: pauledd on June 25, 2018, 06:59:24 amAfter the last command "SET_OPERATION_MODE" I send this to start image transfer:Code:# START_GET_IMAGE_TRANSFER = 83 = 0x53send_msg(0x41, 0x53, 0, 0,'\x58\x5B\x01\x00')so far so good...now for receiving the first frame I need to send a bulk transfer command, and thats where my knowlege stops...in the python script for the "Compact"theyuse this command:Code:ret9 = dev.read(0x81, 0x3F60, 1000)ret9 += dev.read(0x81, 0x3F60, 1000)ret9 += dev.read(0x81, 0x3F60, 1000)ret9 += dev.read(0x81, 0x3F60, 1000)I am going to think to much again:I am not yet sure how to translate that to the "CompactPro", because the second parameter (size?) of cause differs for the Compact Pro, and it looks like they assemble the frame by appending 4 readings? 0x3F60 is 16224... 4 times is 64896... the raw size of the "Compact" is 208x156=32448 and that's half of 64896... so 4 readings are two raw frames?Given that the "CompactPro" has 342x260=88920 raw size, half of that would be 44460 (0xADAC). So sending 4x 0xADAC...

author: tonykids
author_rank: Regular Contributor
timestamp: June 26, 2018, 02:42:44 am
content: Hey, it's me again0x3F60 is just because the usb bulktransfer can only transfer a maximum of 16384(0x4000) bytes data every time.So if your data is more than 16384 bytes,you should read in 16384 bytes(or less) several times until you get all your data.

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 06:36:56 am
content: hhmmokaay, thanks. So I will do again some calculations, just to make it clear in my little head:A full raw frame is 342x260=88920 pixels. Each pixel can have a value of 0-65536, 16bit. That is 88920*16=1422720bit or /8 = 177840 bytes per frame. Given your 16384 bytes max packet size I would assume I have to read 177840/16384=10,8 (11) times to get a full frameOkay I will try that and report back...

author: IwuzBornanerd
author_rank: Frequent Contributor
timestamp: June 26, 2018, 07:25:06 am
content: I think the "correct" way, or maybe the generic way is to do the transfers in a loop wherein you start by asking for the full frame & then after each transfer subtract the number of bytes transferred from the number remaining prior to that transfer and ask for the new remainder each time. I saw this in someone else's code and implemented it in my own C code. This works for both the pro & non-pro cameras, but for some reason on the Raspberry Pi the non-pro works better if I ask for a quarter frame each time--otherwise I can get timeout errors on the first request. I get timeouts with the pro regardless (on any request), particularly with the timeout set at less than 1sec (1sec seems ridiculous when expecting 8fps or more). I have not found a "happy" number of bytes to request.The non-pro ALWAYS sends a frame in 4 chunks regardless of how many bytes I ask for . The pro does not seem to care how many bytes are requested either. What I see with my oscilloscope is that with my software the pro sends a frame in 13 or sometimes 14 chunks, but with the Seek apk running  under Lineage OS on my Raspberry Pi 3B the pro sends a frame in 9 or 10 chunks.

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 07:44:52 am
content: hm, okay. I just watched the Seek App (Android) in my emulator with wireshark, I get 13 chunks everytime...

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 07:54:33 am
content: Okay, that makes sense, it receives 13x13744bytes data, but I gess thats with headers...The URB length is 13680 and that is *13 = 177840 = full frame.

author: tonykids
author_rank: Regular Contributor
timestamp: June 26, 2018, 08:23:28 am
content: You are right, that's my fault, I mixed 16384 with 13680And @IwuzBornanerd's suggestion is

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 08:48:24 am
content: No problem,So I looped until my frame list (just leaned that python uses lists) fills to 177840:Code:frame = []while (len(frame) <= 163648):frame += dev.read(0x81, 0x3570, 1000)print "frame len=", len(frame)outputs:Code:frame len= 13168frame len= 26848frame len= 40528frame len= 54208frame len= 67888frame len= 81568frame len= 95248frame len= 108928frame len= 122608frame len= 136288frame len= 149968frame len= 163648frame len= 177328but what next? I printed the first frame just for fun and noticed that all values in it are 0-255. It starts like that:QuoteNow the next thing would be to start looking for the frame_counter/frame_id... for that "CompactPro" that should be in byte 1 and 2. But in the list I only see zero's

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 09:46:22 am
content: I also noticed that I miss12512 bytes from the first bulk return

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 10:32:00 am
content: just digging further. I observed the usb capture while running the libseek-thermal application. It sometimes also receives 512 bytes less in the first package. Then it repeates the initialisation sequence and in the next run it seems to get the full size... I will try to consider that in my code...

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 11:56:11 am
content: I possibly found the source of the missing 512 bytes. I guess this comes from not releasing the usb device claim that has been done at top of the script. So I added the following at the end of my script where I beleave the dispose command is the important one:Code:send_msg(0x41, 0x3C, 0, 0,'\x00\x00')send_msg(0x41, 0x3C, 0, 0,'\x00\x00')send_msg(0x41, 0x3C, 0, 0,'\x00\x00')usb.util.dispose_resources(dev)With that almost all bulk transfers start with the full size, now I can continue to find the frame id/counter position

author: pauledd
author_rank: Regular Contributor
timestamp: June 26, 2018, 01:28:39 pm
content: some progress. I just tried a loop to read a full frame and then print the values at frame and frame.Code:while True:frame = []send_msg(0x41, 0x53, 0, 0,'\x58\x5B\x01\x00')frame = dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)frame += dev.read(0x81, 0x3570, 0x3E8)print frame, frameThats the first time I hear the shutter clickingand I get output with an increasing number that is hopefully indeed the frame counterand another number that first goes stupid but then settles at value 3 and everytime the shutter clicks it changes ..3/6/5/1/3...Code:1 43 95 147 89 711 526 628 530 133 335 337 339 341 343 345 347 349 351 353 355 357 359 361 363 365 367 369 372 674 576 179 381 383 385 3

author: IwuzBornanerd
author_rank: Frequent Contributor
timestamp: June 26, 2018, 08:39:26 pm
content: Quote from: pauledd on June 26, 2018, 07:44:52 amBut your camera is a non-fast frame unit, correct? OR does the Seek app identify it as Seek Compact PRO FF (like it does with my camera)?The first 5 frames, status numbers 4, 9, 14, 8 & 7 seem to be related to calibration. I (and maybe only I) believe that the status 9 frame is the thermistor curve for the sensor; basically a lookup table for determining the temperature of a pixel and that is how I use it in my code.

author: pauledd
author_rank: Regular Contributor
timestamp: June 27, 2018, 06:58:43 am
content: Quote from: IwuzBornanerd on June 26, 2018, 08:39:26 pmYes, its the normal CompactPro (ID 289d:0011)I currently trying to figure out whether my frames are valid ones or whether where to look for the actual image in it. I only concentrate on the nomal frames with id=3.I got afunctionfrom stackoverflow that converts all pairs of uint8 values to into a single uint16 value (I guess this is what happens in the correct_endianness function in the libseek-thermal lib). Of cause the size off the array is then half 177840 = 88920...Code:while (status != 3):frame = []send_msg(0x41, 0x53, 0, 0,'\x58\x5B\x01\x00')frame = dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)status = frameif (status == 3):frame16 = array.array('H', ((j << 8 ) | ifor (i,j)in zip(frame[::2], frame[1::2])))print "frame_uint8=", frame[:32]print "frame_uint16=", frame16[:32]Here I've printed two lines with 32 positions for the same id=3 frame, the first is the unprocessed uint8 frame, the second is the uint16 array with the data from the uint8 array. Now in the uint16 data I can see that the frame_count byte is at index frame16=33, and the frame_id byte is at frame16 = 3, as expected... the index for those two changed to the half number (the frame_id in the uint16 was at frame):Code:frame_uint8= array('B', [121, 5, 33, 0, 3, 0, 0, 0, 33, 0, 31, 25, 152, 58, 0, 0, 0, 0, 0, 0, 48, 0, 37, 55, 0, 0, 0, 0, 0, 0, 0, 0])frame_uint16= array('H', [1401, 33, 3, 0, 33, 6431, 15000, 0, 0, 0, 48, 14117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])I am not shure if I can simply throw the frame16 data into an cv2::Mat/PIL::whatever to display the image or whether I need to crop the raw dimensions to the 320x240, and if so, I am not sure how to do it. The writer ofthis python codejust put the array into "calimg = Image.fromstring("I", (208,156), ret9, "raw", "I;16")" without cropping it to 207x154 for the "Compact"...I know the C++ code in the libseek-thermal lib contains all the steps but I am quite awkward in understanding it. Thats why I take it to pieces here in python

author: IwuzBornanerd
author_rank: Frequent Contributor
timestamp: June 27, 2018, 07:39:21 am
content: Once you have converted to 16 bit numbers you then need to subtract the values in the shutter frame (ID=1) from the frame 3 values. This gets you values that correspond to the difference in temperature between the shutter & the scene. The values can be both negative & positive. That is the first level of correction. You then need to apply gain correction factors to each pixel. You can use the values in the frame with ID=4 by dividing each number by the average of all values and using the results as percentage multipliers for each pixel in the frame 3 minus frame 1 set of values. OR you can make your own correction factors (which are better) by capturing a frame 3 with the camera warmed up & pointed at a uniform ambient temperature surface, subtracting the preceding frame 1 from that and dividing each value by the average. I think I have that right without reviewing my code. You will have to figure out a good way to deal with the +/- values & assigning colors or gray shades. Adding a simple offset is one way. Figuring temperature is another complexity...Is that clear as mud?

author: pauledd
author_rank: Regular Contributor
timestamp: June 28, 2018, 06:47:48 am
content: *to the note, just in case I cant remember again later...I am currently still totally stuck at raw conversionI went some steps backward and simply outputted the raw frames in binary format like that:Code:while status != 3:frame = np.uint16()send_msg(0x41, 0x53, 0, 0,'\x58\x5B\x01\x00')frame = dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)status = frameprint statusthefile = open("frames/frame_id_"+str(status)+".raw", 'w')thefile.write(struct.pack('177840B', *frame))I now have:Code:frame_id_14.rawframe_id_1.rawframe_id_3.rawframe_id_4.rawframe_id_5.rawframe_id_6.rawframe_id_7.rawframe_id_8.rawframe_id_9.rawI then used imagemagik to convert them:Code:for i in $(ls frames/);do convert -depth 16 -endian lsb -size 342x260 gray:$i -auto-level $i.png;doneconversion failed for id: 4,8,9,14 "convert: no images defined"for the rest I got these images:id1id3id5id6id7In id3 I can see the faint thermal object my cam is pionting at... I get closerJust another sidenote:ALLthe camera initialisation steps from:Code:# SET_OPERATION_MODE = 60; 0x41=send,0x3C=60send_msg(0x41, 0x3C, 0, 0, '\x00\x00')...down to:Code:...# GET_FIRMWARE_INFO = 78 = 0x4Eret7 = receive_msg(0xC1, 0x4E, 0, 0, 64)can be deleted. I found out after accidental delete that the cam starts image transfer with just this commands:Code:#SET_IMAGE_PROCESSING_MODE = 62 = 0x3Esend_msg(0x41, 0x3E, 0, 0,'\x08\x00')# SET_OPERATION_MODE = 60 = 0x3Csend_msg(0x41, 0x3C, 0, 0,'\x01\x00')after that you can start the bulk transfer... This seems to work at least for my CompactPro device...I now continue to fiddle with thatarray stuff that my script outputs raw png's...

author: IwuzBornanerd
author_rank: Frequent Contributor
timestamp: June 28, 2018, 08:34:09 am
content: I also found that much of the setup stuff can be eliminated, but I can't verify if it is the same stuff you deleted. I suppose most of it is "defaults"

author: pauledd
author_rank: Regular Contributor
timestamp: June 28, 2018, 05:05:02 pm
content: I made a good step foreward. Afterhoursfiddling with lists/numpy-arrays/opencv/endiannessI managed to get Images from opencv. The problem was simple... I simply did not realize that the data in my lists/numpy-arrays was 1d... for displaying/manipulating images in numpy arrays with opencv I needed the data to be 2d. So one simple numpy function called reshape did the job.And I found a simpler way to convert the raw frame from uint8 LSB to uint16 MSB:Code:...frame = dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)frame += dev.read(0x81, 0x3570, tout)...npframe = np.asarray(frame, dtype='<B')npframe.dtype = np.uint16img = npframe.reshape(260,342)cv2.normalize(img,img, 0,65535,cv2.NORM_MINMAX)cv2.namedWindow("image", cv2.WINDOW_NORMAL)cv2.resizeWindow('image', 600,600)cv2.imshow('image', img)cv2.waitKey(0)cv2.destroyAllWindows()Now I can define an ROI and cut out the actual image from the raw frame and start all the post processing stuff that I want. One thing I found interesting was that the actual image has dimensions wider than those from thespecsI just googled for 324x254 and found that others here had already discovered that. Sofor the CompactPro I get 4-top, 18-right and2-bottom lines that are outside of the image.

author: pauledd
author_rank: Regular Contributor
timestamp: July 05, 2018, 04:39:51 pm
content: I just wonder where this "patent pixels" are in my id3 image above? Are that the total black pixels that cover 2 pixels each? Would be strange if that would be dead pixels, but I would expect the patent to be in a pattern well ordered over the whole image as I saw from the other users here

author: IwuzBornanerd
author_rank: Frequent Contributor
timestamp: July 05, 2018, 07:06:11 pm
content: My Pro does not have "patent pixels"; at least not a noticeable number of regular (or even irregular) zero pixels.