Compare commits

..

4 Commits

Author SHA1 Message Date
21ebc6bc61 Add toupcam support 2026-02-18 17:15:42 +03:00
7769391186 little fixes 2026-01-29 14:37:42 +03:00
b9084907a3 forgotten fixes 2025-06-12 14:31:51 +03:00
3f7b5081ea add more statistics 2025-05-14 09:04:11 +03:00
22 changed files with 712 additions and 225 deletions

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
# Object files # Object files
*.o *.o
*/mk/*
# Libraries # Libraries
*.lib *.lib

View File

@@ -19,14 +19,15 @@ option(DEBUG "Compile in debug mode" OFF)
option(BASLER "Add Basler cameras support" OFF) option(BASLER "Add Basler cameras support" OFF)
option(GRASSHOPPER "Add GrassHopper cameras support" OFF) option(GRASSHOPPER "Add GrassHopper cameras support" OFF)
option(HIKROBOT "Add HikRobot cameras support" OFF) option(HIKROBOT "Add HikRobot cameras support" OFF)
option(TOUPCAM "Add Toupcam CMOS support" OFF)
# default flags # default flags (c17 because MVS code have `typedef char bool`)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -W -Wextra -Werror -std=c17")
set(CMAKE_COLOR_MAKEFILE ON) set(CMAKE_COLOR_MAKEFILE ON)
# cmake -DDEBUG=yes -> debugging # cmake -DDEBUG=yes -> debugging
if(DEBUG) if(DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen -Werror") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Og -g3 -ggdb -fno-builtin-strlen")
add_definitions(-DEBUG) add_definitions(-DEBUG)
set(CMAKE_BUILD_TYPE DEBUG) set(CMAKE_BUILD_TYPE DEBUG)
set(CMAKE_VERBOSE_MAKEFILE "ON") set(CMAKE_VERBOSE_MAKEFILE "ON")
@@ -63,6 +64,13 @@ if(HIKROBOT)
else() else()
list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/hikrobot.c") list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/hikrobot.c")
endif() endif()
if(TOUPCAM)
pkg_check_modules(TOUPCAM REQUIRED toupcam)
add_definitions("-DTOUPCAM_FOUND=1")
else()
list(REMOVE_ITEM SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/toupcam.c")
endif()
pkg_check_modules(MODULES REQUIRED ${MODULES}) pkg_check_modules(MODULES REQUIRED ${MODULES})
include(FindOpenMP) include(FindOpenMP)
@@ -89,18 +97,18 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
# exe file # exe file
add_executable(${PROJ} ${SOURCES}) add_executable(${PROJ} ${SOURCES})
# -I # -I
target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS} ${MVS_INCLUDE_DIRS}) target_include_directories(${PROJ} PUBLIC ${MODULES_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS} ${MVS_INCLUDE_DIRS} ${TOUPCAM_INCLUDE_DIRS})
# -L # -L
target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS} ${MVS_LIBRARY_DIRS}) target_link_directories(${PROJ} PUBLIC ${MODULES_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS} ${MVS_LIBRARY_DIRS} ${TOUPCAM_LIBRARY_DIRS})
message("MOD: ${MODULES_LIBRARY_DIRS}, FC: ${FLYCAP_LIBRARY_DIRS}, MVS: ${MVS_LIBRARY_DIRS}") message("MOD: ${MODULES_LIBRARY_DIRS}, FC: ${FLYCAP_LIBRARY_DIRS}, MVS: ${MVS_LIBRARY_DIRS}, TOUPCAM: ${TOUPCAM_LIBRARY_DIRS}")
# -D # -D
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" add_definitions(${CFLAGS} -D_XOPEN_SOURCE=666 -D_POSIX_C_SOURCE=600700 -D_DEFAULT_SOURCE -DLOCALEDIR=\"${LOCALEDIR}\"
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
-DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\" -DMINOR_VERSION=\"${MINOR_VERSION}\" -DMID_VERSION=\"${MID_VERSION}\"
-DMAJOR_VERSION=\"${MAJOR_VERSION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT}) -DMAJOR_VERSION=\"${MAJOR_VERSION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT})
# -l # -l
target_link_libraries(${PROJ} ${MODULES_LIBRARIES} ${FLYCAP_LIBRARIES} ${BASLER_LIBRARIES} ${MVS_LIBRARIES} -lm) target_link_libraries(${PROJ} ${MODULES_LIBRARIES} ${FLYCAP_LIBRARIES} ${BASLER_LIBRARIES} ${MVS_LIBRARIES} ${TOUPCAM_LIBRARIES} -lm)
# Installation of the program # Installation of the program
INSTALL(TARGETS ${PROJ} DESTINATION "bin") INSTALL(TARGETS ${PROJ} DESTINATION "bin")

View File

@@ -1,3 +1,3 @@
Mon Apr 28 13:05:48 MSK 2025 Mon Apr 28 13:05:48 MSK 2025
Move to libusefull_macros v0.3.2. Move to libusefull_macros v0.3.2.
Fixed some troubles with `inotifying` log files. Fixed some troubles with `inotifying` log files.

28
LocCorr_new/Toupcam.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* This file is part of the loccorr project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifdef TOUPCAM_FOUND
#include "cameracapture.h" // `camera`
#define TOUPCAM_CAPT_NAME "toupcam"
extern camera Toupcam;
#endif

View File

@@ -436,30 +436,30 @@ size_t *il_cclabel4(uint8_t *Img, int W, int H, il_ConnComps **CC){
l_boxes[i].ymin = H; l_boxes[i].ymin = H;
} }
#pragma omp for nowait #pragma omp for nowait
for(int y = 0; y < H; ++y){ for(int y = 0; y < H; ++y){
size_t *lptr = &labels[y*W]; size_t *lptr = &labels[y*W];
for(int x = 0; x < W; ++x, ++lptr){ for(int x = 0; x < W; ++x, ++lptr){
if(!*lptr) continue; if(!*lptr) continue;
register size_t mark = indexes[*lptr]; register size_t mark = indexes[*lptr];
*lptr = mark; *lptr = mark;
il_Box *b = &l_boxes[mark]; il_Box *b = &l_boxes[mark];
++b->area; ++b->area;
if(b->xmax < x) b->xmax = x; if(b->xmax < x) b->xmax = x;
if(b->xmin > x) b->xmin = x; if(b->xmin > x) b->xmin = x;
if(b->ymax < y) b->ymax = y; if(b->ymax < y) b->ymax = y;
if(b->ymin > y) b->ymin = y; if(b->ymin > y) b->ymin = y;
} }
} }
#pragma omp critical #pragma omp critical
for(size_t i = 1; i < cidx; ++i){ for(size_t i = 1; i < cidx; ++i){
il_Box *ob = &boxes[i], *ib = &l_boxes[i]; il_Box *ob = &boxes[i], *ib = &l_boxes[i];
if(ob->xmax < ib->xmax) ob->xmax = ib->xmax; if(ob->xmax < ib->xmax) ob->xmax = ib->xmax;
if(ob->xmin > ib->xmin) ob->xmin = ib->xmin; if(ob->xmin > ib->xmin) ob->xmin = ib->xmin;
if(ob->ymax < ib->ymax) ob->ymax = ib->ymax; if(ob->ymax < ib->ymax) ob->ymax = ib->ymax;
if(ob->ymin > ib->ymin) ob->ymin = ib->ymin; if(ob->ymin > ib->ymin) ob->ymin = ib->ymin;
ob->area += ib->area; ob->area += ib->area;
} }
FREE(l_boxes); FREE(l_boxes);
} }
FREE(assoc); FREE(assoc);
FREE(indexes); FREE(indexes);

View File

@@ -21,6 +21,7 @@
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include "cameracapture.h" #include "cameracapture.h"
#include "cmdlnopts.h" #include "cmdlnopts.h"
@@ -34,7 +35,7 @@
static camera *theCam = NULL; static camera *theCam = NULL;
static float gain = 0., gainmax = 0.; static float gain = 0., gainmax = 0.;
static float exptime = 100.; static float exptime = -1.;
static float brightness = 0.; static float brightness = 0.;
static int connected = FALSE; static int connected = FALSE;
@@ -42,6 +43,15 @@ static frameformat curformat;
static frameformat maxformat; static frameformat maxformat;
static frameformat stepformat; static frameformat stepformat;
// statistics of last image
typedef struct{
Imtype minval, maxval, bkg;
float avg, xc, yc;
ptstat_t stat;
} imdata_t;
static imdata_t lastimdata = {0};
static void changeformat(){ static void changeformat(){
if(!theCam) return; if(!theCam) return;
if(maxformat.h < 1 || maxformat.w < 1){ if(maxformat.h < 1 || maxformat.w < 1){
@@ -190,7 +200,7 @@ static int needs_exposure_adjustment(const Image *I, float curr_x, float curr_y)
float avg = I->avg_intensity; float avg = I->avg_intensity;
float dx = fabsf(curr_x - last_centroid_x); float dx = fabsf(curr_x - last_centroid_x);
float dy = fabsf(curr_y - last_centroid_y); float dy = fabsf(curr_y - last_centroid_y);
LOGDBG("avg: %g, curr_x: %g, curr_y: %g", avg, curr_x, curr_y); //LOGDBG("avg: %g, curr_x: %g, curr_y: %g", avg, curr_x, curr_y);
// don't change brightness if average value in 5..50 // don't change brightness if average value in 5..50
if(avg > 5.f && avg < 50.f){ if(avg > 5.f && avg < 50.f){
last_avg_intensity = avg; last_avg_intensity = avg;
@@ -229,42 +239,50 @@ static void *procthread(void* v){
void (*process)(Image*) = (procfn_t)v; void (*process)(Image*) = (procfn_t)v;
#ifdef EBUG #ifdef EBUG
double t0 = sl_dtime(); double t0 = sl_dtime();
int imno = 0;
#endif #endif
while(!stopwork){ while(!stopwork){
while(iCaptured < 0) usleep(1000);
pthread_mutex_lock(&capt_mutex); pthread_mutex_lock(&capt_mutex);
if(Icap[iCaptured]){ if(Icap[iCaptured]){
DBG("---- got image #%d @ %g", iCaptured, sl_dtime() - t0); DBG("===== got image #%d, iCaptured=#%d @ %g", imno++, iCaptured, sl_dtime() - t0);
Image *oIma = Icap[iCaptured]; // take image here and free buffer Image *oIma = Icap[iCaptured]; // take image here and free buffer
Icap[iCaptured] = NULL; Icap[iCaptured] = NULL;
pthread_mutex_unlock(&capt_mutex); pthread_mutex_unlock(&capt_mutex);
if(process){
if(theconf.medfilt){
Image *X = get_median(oIma, theconf.medseed);
if(X){
Image_free(&oIma);
oIma = X;
}
}
process(oIma);
lastimdata.avg = oIma->avg_intensity;
lastimdata.bkg = oIma->background;
lastimdata.minval = oIma->minval;
lastimdata.maxval = oIma->maxval;
lastimdata.stat = oIma->stat;
getcenter(&lastimdata.xc, &lastimdata.yc);
}
if(theconf.expmethod == EXPAUTO){ if(theconf.expmethod == EXPAUTO){
float xc, yc; if(needs_exposure_adjustment(oIma, lastimdata.xc, lastimdata.yc)) recalcexp(oIma);
getcenter(&xc, &yc);
if(needs_exposure_adjustment(oIma, xc, yc)) recalcexp(oIma);
}else{ }else{
if(fabs(theconf.fixedexp - exptime) > FLT_EPSILON) if(fabs(theconf.exptime - exptime) > FLT_EPSILON)
exptime = theconf.fixedexp; exptime = theconf.exptime;
if(fabs(theconf.gain - gain) > FLT_EPSILON) if(fabs(theconf.gain - gain) > FLT_EPSILON)
gain = theconf.gain; gain = theconf.gain;
if(fabs(theconf.brightness - brightness) > FLT_EPSILON) if(fabs(theconf.brightness - brightness) > FLT_EPSILON)
brightness = theconf.brightness; brightness = theconf.brightness;
} }
if(process){ //Icap[iCaptured] = NULL;
if(theconf.medfilt){ //pthread_mutex_unlock(&capt_mutex);
Image *X = get_median(oIma, theconf.medseed); Image_free(&oIma);
if(X){ DBG("===== cleared image data @ %g", sl_dtime() - t0);
FREE(oIma->data); }else{
FREE(oIma); //DBG("===== NO image data");
oIma = X; pthread_mutex_unlock(&capt_mutex);
} }
} //DBG("===== NEXT!");
process(oIma);
}
FREE(oIma->data);
FREE(oIma);
DBG("---- cleared image data @ %g", sl_dtime() - t0);
}else pthread_mutex_unlock(&capt_mutex);
usleep(1000); usleep(1000);
} }
return NULL; return NULL;
@@ -274,14 +292,21 @@ int camcapture(void (*process)(Image*)){
FNAME(); FNAME();
static float oldexptime = 0.; static float oldexptime = 0.;
static float oldgain = -1.; static float oldgain = -1.;
static float oldbrightness = -1.; static float oldbrightness = 0.;
Image *oIma = NULL; Image *oIma = NULL;
pthread_t proc_thread; pthread_t proc_thread;
if(pthread_create(&proc_thread, NULL, procthread, (void*)process)){ if(pthread_create(&proc_thread, NULL, procthread, (void*)process)){
LOGERR("pthread_create() for image processing failed"); LOGERR("pthread_create() for image processing failed");
ERR("pthread_create()"); ERR("pthread_create()");
} }
exptime = theconf.exptime;
#ifdef EBUG
static int imno = 0;
#endif
while(1){ while(1){
#ifdef EBUG
double t0 = sl_dtime();
#endif
if(stopwork){ if(stopwork){
DBG("STOP"); DBG("STOP");
break; break;
@@ -290,13 +315,15 @@ int camcapture(void (*process)(Image*)){
LOGERR("camcapture(): camera not initialized"); LOGERR("camcapture(): camera not initialized");
ERRX("Not initialized"); ERRX("Not initialized");
} }
DBG("T=%g", sl_dtime() - t0);
if(!connected){ if(!connected){
DBG("Disconnected"); DBG("Disconnected, try to connect");
connected = theCam->connect(); connected = theCam->connect();
sleep(1); sleep(1);
changeformat(); changeformat();
continue; continue;
} }
DBG("T=%g", sl_dtime() - t0);
if(fabsf(oldbrightness - brightness) > FLT_EPSILON){ // new brightness if(fabsf(oldbrightness - brightness) > FLT_EPSILON){ // new brightness
DBG("Change brightness to %g", brightness); DBG("Change brightness to %g", brightness);
if(theCam->setbrightness(brightness)){ if(theCam->setbrightness(brightness)){
@@ -305,38 +332,54 @@ int camcapture(void (*process)(Image*)){
WARNX("Can't change brightness to %g", brightness); WARNX("Can't change brightness to %g", brightness);
} }
} }
DBG("T=%g", sl_dtime() - t0);
if(exptime > theconf.maxexp) exptime = theconf.maxexp; if(exptime > theconf.maxexp) exptime = theconf.maxexp;
else if(exptime < theconf.minexp) exptime = theconf.minexp; else if(exptime < theconf.minexp) exptime = theconf.minexp;
if(fabsf(oldexptime - exptime) > FLT_EPSILON){ // new exsposition value if(fabsf(oldexptime - exptime) > FLT_EPSILON){ // new exsposition value
DBG("Change exptime to %.2fms\n", exptime); DBG("Change exptime to %.2fms\n", exptime);
if(theCam->setexp(exptime)){ if(theCam->setexp(exptime)){
oldexptime = exptime; oldexptime = exptime;
theconf.exptime = exptime;
}else{ }else{
WARNX("Can't change exposition time to %gms", exptime); WARNX("Can't change exposition time to %gms", exptime);
} }
} }
DBG("T=%g", sl_dtime() - t0);
if(gain > gainmax) gain = gainmax; if(gain > gainmax) gain = gainmax;
if(fabsf(oldgain - gain) > FLT_EPSILON){ // change gain if(fabsf(oldgain - gain) > FLT_EPSILON){ // change gain
DBG("Change gain to %g\n", gain); DBG("Change gain to %g\n", gain);
LOGDBG("Change gain to %g", gain);
if(theCam->setgain(gain)){ if(theCam->setgain(gain)){
oldgain = gain; oldgain = gain;
theconf.gain = gain;
}else{ }else{
WARNX("Can't change gain to %g", gain); WARNX("Can't change gain to %g", gain);
LOGWARN("Can't change gain to %g", gain);
gain = oldgain;
} }
} }
DBG("T=%g", sl_dtime() - t0);
// change format // change format
if(abs(curformat.h - theconf.height) || abs(curformat.w - theconf.width) || abs(curformat.xoff - theconf.xoff) || abs(curformat.yoff - theconf.yoff)){ if(abs(curformat.h - theconf.height) || abs(curformat.w - theconf.width) || abs(curformat.xoff - theconf.xoff) || abs(curformat.yoff - theconf.yoff)){
changeformat(); changeformat();
} }
DBG("Try to grab (T=%g)", sl_dtime() - t0);
static int errctr = 0;
if(!(oIma = theCam->capture())){ if(!(oIma = theCam->capture())){
WARNX("Can't grab image"); WARNX("---- Can't grab image");
camdisconnect(); if(++errctr > MAX_CAPT_ERRORS){
LOGERR("camcapture(): too much capture errors; reconnect camera");
camdisconnect();
errctr = 0;
}
continue; continue;
} }else errctr = 0;
DBG("---- Grabbed #%d @ %g", imno++, sl_dtime() - t0);
pthread_mutex_lock(&capt_mutex); pthread_mutex_lock(&capt_mutex);
if(iCaptured < 0) iCaptured = 0; if(iCaptured < 0) iCaptured = 0;
else iCaptured = !iCaptured; else iCaptured = !iCaptured;
if(Icap[iCaptured]){ // try current value if previous is still busy if(Icap[iCaptured]){ // try current value if previous is still busy
DBG("---- iCap=%d busy!", iCaptured);
iCaptured = !iCaptured; iCaptured = !iCaptured;
} }
if(!Icap[iCaptured]){ // previous buffer is free if(!Icap[iCaptured]){ // previous buffer is free
@@ -344,22 +387,16 @@ int camcapture(void (*process)(Image*)){
Icap[iCaptured] = oIma; Icap[iCaptured] = oIma;
oIma = NULL; oIma = NULL;
}else{ // clear our image - there's no empty buffers }else{ // clear our image - there's no empty buffers
DBG("---- no free buffers"); DBG("---- no free buffers for iCap=%d", iCaptured);
FREE(oIma->data); Image_free(&oIma);
FREE(oIma);
} }
pthread_mutex_unlock(&capt_mutex); pthread_mutex_unlock(&capt_mutex);
DBG("T=%g", sl_dtime() - t0);
} }
pthread_cancel(proc_thread); pthread_cancel(proc_thread);
if(oIma){ if(oIma) Image_free(&oIma);
FREE(oIma->data);
FREE(oIma);
}
for(int i = 0; i < 2; ++i){ for(int i = 0; i < 2; ++i){
if(Icap[i]){ if(Icap[i]) Image_free(&Icap[i]);
FREE(Icap[i]->data);
FREE(Icap[i]);
}
} }
camdisconnect(); camdisconnect();
DBG("CAMCAPTURE: out"); DBG("CAMCAPTURE: out");
@@ -388,10 +425,13 @@ char *camstatus(const char *messageid, char *buf, int buflen){
float xc, yc; float xc, yc;
getcenter(&xc, &yc); getcenter(&xc, &yc);
snprintf(buf, buflen, "{ \"%s\": \"%s\", \"camstatus\": \"%sconnected\", \"impath\": \"%s\", \"imctr\": %llu, " snprintf(buf, buflen, "{ \"%s\": \"%s\", \"camstatus\": \"%sconnected\", \"impath\": \"%s\", \"imctr\": %llu, "
"\"fps\": %.3f, \"expmethod\": \"%s\", \"exposition\": %g, \"gain\": %g, \"brightness\": %g, " "\"fps\": %.3f, \"expmethod\": \"%s\", \"exptime\": %g, \"gain\": %g, \"maxgain\": %g, \"brightness\": %g, "
"\"xcenter\": %.1f, \"ycenter\": %.1f }\n", "\"xcenter\": %.1f, \"ycenter\": %.1f , \"minval\": %d, \"maxval\": %d, \"background\": %d, "
MESSAGEID, messageid, connected ? "" : "dis", impath, ImNumber, getFramesPerS(), "\"average\": %.1f, \"xc\": %.1f, \"yc\": %.1f, \"xsigma\": %.1f, \"ysigma\": %.1f, \"area\": %d }\n",
(theconf.expmethod == EXPAUTO) ? "auto" : "manual", exptime, gain, brightness, MESSAGEID, messageid, connected ? "" : "dis", impath, ImNumber, getFramesPerS(),
xc, yc); (theconf.expmethod == EXPAUTO) ? "auto" : "manual", exptime, gain, gainmax, brightness,
xc, yc, lastimdata.minval, lastimdata.maxval, lastimdata.bkg, lastimdata.avg,
lastimdata.stat.xc, lastimdata.stat.yc, lastimdata.stat.xsigma, lastimdata.stat.ysigma,
lastimdata.stat.area);
return buf; return buf;
} }

View File

@@ -21,6 +21,8 @@
#include "imagefile.h" // Image #include "imagefile.h" // Image
// max capture errors contract to make reconnection
#define MAX_CAPT_ERRORS (10)
// format of single frame // format of single frame
typedef struct{ typedef struct{

View File

@@ -70,7 +70,7 @@ static sl_option_t cmdlnopts[] = {
{"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")}, {"logfile", NEED_ARG, NULL, 'l', arg_string, APTR(&G.logfile), _("file to save logs (default: none)")},
{"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")}, {"pidfile", NEED_ARG, NULL, 'P', arg_string, APTR(&G.pidfile), _("pidfile (default: " DEFAULT_PIDFILE ")")},
{"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), _("increase verbosity level of log file (each -v increased by 1)")}, {"verbose", NO_ARGS, NULL, 'v', arg_none, APTR(&G.verb), _("increase verbosity level of log file (each -v increased by 1)")},
{"input", NEED_ARG, NULL, 'i', arg_string, APTR(&G.inputname), _("file or directory name for monitoring (or grasshopper/basler/hikrobot for capturing)")}, {"input", NEED_ARG, NULL, 'i', arg_string, APTR(&G.inputname), _("file or directory name for monitoring (or grasshopper/basler/hikrobot/toupcam for capturing)")},
{"blackp", NEED_ARG, NULL, 'b', arg_double, APTR(&G.throwpart), _("fraction of black pixels to throw away when make histogram eq")}, {"blackp", NEED_ARG, NULL, 'b', arg_double, APTR(&G.throwpart), _("fraction of black pixels to throw away when make histogram eq")},
// {"radius", NEED_ARG, NULL, 'r', arg_int, APTR(&G.medradius), _("radius of median filter (r=1 -> 3x3, r=2 -> 5x5 etc.)")}, // {"radius", NEED_ARG, NULL, 'r', arg_int, APTR(&G.medradius), _("radius of median filter (r=1 -> 3x3, r=2 -> 5x5 etc.)")},
{"equalize", NO_ARGS, NULL, 'e', arg_int, APTR(&G.equalize), _("make historam equalization of saved jpeg")}, {"equalize", NO_ARGS, NULL, 'e', arg_int, APTR(&G.equalize), _("make historam equalization of saved jpeg")},

View File

@@ -62,7 +62,7 @@ configuration theconf = {
.throwpart=DEFAULT_THROWPART, .throwpart=DEFAULT_THROWPART,
.maxexp=EXPOS_MAX - 1., .maxexp=EXPOS_MAX - 1.,
.minexp=EXPOS_MIN, .minexp=EXPOS_MIN,
.fixedexp=EXPOS_MIN*2, .exptime=EXPOS_MIN*2,
.gain=20., .gain=20.,
.intensthres=DEFAULT_INTENSTHRES, .intensthres=DEFAULT_INTENSTHRES,
.medseed=MIN_MEDIAN_SEED, .medseed=MIN_MEDIAN_SEED,
@@ -74,6 +74,7 @@ static int compConfVals(const void *_1st, const void *_2nd){
return strcmp(a->name, b->name); return strcmp(a->name, b->name);
} }
// could be in unsorted order as whould be sorted at first help call
// {"", PAR_DOUBLE, (void*)&theconf., 0}, // {"", PAR_DOUBLE, (void*)&theconf., 0},
static confparam parvals[] = { static confparam parvals[] = {
{"maxarea", PAR_INT, (void*)&theconf.maxarea, 0, MINAREA, MAXAREA, {"maxarea", PAR_INT, (void*)&theconf.maxarea, 0, MINAREA, MAXAREA,
@@ -99,7 +100,7 @@ static confparam parvals[] = {
{"equalize", PAR_INT, (void*)&theconf.equalize, 0, 0., 1., {"equalize", PAR_INT, (void*)&theconf.equalize, 0, 0., 1.,
"make histogram equalization"}, "make histogram equalization"},
{"expmethod", PAR_INT, (void*)&theconf.expmethod, 0, 0., 1., {"expmethod", PAR_INT, (void*)&theconf.expmethod, 0, 0., 1.,
"exposition method: 0 - auto, 1 - fixed"}, "0 - automatic calculation of gain and exptime, 1 - use fixed values"},
{"naverage", PAR_INT, (void*)&theconf.naverage, 0, 1., NAVER_MAX, {"naverage", PAR_INT, (void*)&theconf.naverage, 0, 1., NAVER_MAX,
"calculate mean position by N images"}, "calculate mean position by N images"},
{"umax", PAR_INT, (void*)&theconf.maxUpos, 0, -MAXSTEPS, MAXSTEPS, {"umax", PAR_INT, (void*)&theconf.maxUpos, 0, -MAXSTEPS, MAXSTEPS,
@@ -146,8 +147,8 @@ static confparam parvals[] = {
"minimal exposition time"}, "minimal exposition time"},
{"maxexp", PAR_DOUBLE, (void*)&theconf.maxexp, 0, 0., EXPOS_MAX, {"maxexp", PAR_DOUBLE, (void*)&theconf.maxexp, 0, 0., EXPOS_MAX,
"maximal exposition time"}, "maximal exposition time"},
{"fixedexp", PAR_DOUBLE, (void*)&theconf.fixedexp, 0, EXPOS_MIN, EXPOS_MAX, {"exptime", PAR_DOUBLE, (void*)&theconf.exptime, 0, EXPOS_MIN, EXPOS_MAX,
"fixed (in manual mode) exposition time"}, "exposition time (you can change it only when expmethod==1)"},
{"intensthres", PAR_DOUBLE, (void*)&theconf.intensthres, 0, 0., 1., {"intensthres", PAR_DOUBLE, (void*)&theconf.intensthres, 0, 0., 1.,
"threshold by total object intensity when sorting = |I1-I2|/(I1+I2)"}, "threshold by total object intensity when sorting = |I1-I2|/(I1+I2)"},
{"gain", PAR_DOUBLE, (void*)&theconf.gain, 0, GAIN_MIN, GAIN_MAX, {"gain", PAR_DOUBLE, (void*)&theconf.gain, 0, GAIN_MIN, GAIN_MAX,
@@ -162,8 +163,10 @@ static confparam parvals[] = {
"median filter radius"}, "median filter radius"},
{"fixedbg", PAR_INT, (void*)&theconf.fixedbkg, 0, 0., 1., {"fixedbg", PAR_INT, (void*)&theconf.fixedbkg, 0, 0., 1.,
"don't calculate background, use fixed value instead"}, "don't calculate background, use fixed value instead"},
{"fbglevel", PAR_INT, (void*)&theconf.fixedbkgval, 0, FIXED_BK_MIN, FIXED_BK_MAX, {"background", PAR_INT, (void*)&theconf.background, 0, FIXED_BK_MIN, FIXED_BK_MAX,
"fixed background level"}, "fixed background level"},
{"writedi", PAR_INT, (void*)&theconf.writedebugimgs, 0, 0., 1.,
"write debug images (binary/erosion/opening)"},
{NULL, 0, NULL, 0, 0., 0., NULL} {NULL, 0, NULL, 0, 0., 0., NULL}
}; };

View File

@@ -34,12 +34,12 @@
#define MAX_THROWPART (0.9) #define MAX_THROWPART (0.9)
#define MAX_OFFSET (10000) #define MAX_OFFSET (10000)
// min/max exposition in ms // min/max exposition in ms
#define EXPOS_MIN (0.1) #define EXPOS_MIN (1e-9)
#define EXPOS_MAX (4001.) #define EXPOS_MAX (4001.)
#define GAIN_MIN (0.) #define GAIN_MIN (0.)
#define GAIN_MAX (100.) #define GAIN_MAX (256.)
#define BRIGHT_MIN (0.) #define BRIGHT_MIN (0.)
#define BRIGHT_MAX (10.) #define BRIGHT_MAX (100.)
// max average images counter // max average images counter
#define NAVER_MAX (25) #define NAVER_MAX (25)
// coefficients to convert dx,dy to du,dv // coefficients to convert dx,dy to du,dv
@@ -52,15 +52,15 @@
#define MAX_MEDIAN_SEED (7) #define MAX_MEDIAN_SEED (7)
// fixed background // fixed background
#define FIXED_BK_MIN (0) #define FIXED_BK_MIN (0)
#define FIXED_BK_MAX (250) #define FIXED_BK_MAX (255)
// exposition methods: 0 - auto, 1 - fixed // exposition methods: 0 - auto, 1 - fixed
#define EXPAUTO (0) #define EXPAUTO (0)
#define EXPMANUAL (1) #define EXPMANUAL (1)
// roundness parameter // roundness parameter
#define MINWH (0.3) #define MINWH (0.01)
#define MAXWH (3.) #define MAXWH (100.)
// PID limits // PID limits
#define PID_P_MIN (0.1) #define PID_P_MIN (0.1)
@@ -99,7 +99,8 @@ typedef struct{
int medfilt; // == 1 to make median filter before calculations int medfilt; // == 1 to make median filter before calculations
int medseed; // median seed int medseed; // median seed
int fixedbkg; // don't calculate background, use fixed value instead int fixedbkg; // don't calculate background, use fixed value instead
int fixedbkgval; // value of bk int background; // value of background
int writedebugimgs; // write debugging images: binary/erosion/opening
// dU = Kxu*dX + Kyu*dY; dV = Kxv*dX + Kyv*dY // dU = Kxu*dX + Kyu*dY; dV = Kxv*dX + Kyv*dY
double Kxu; double Kyu; double Kxu; double Kyu;
double Kxv; double Kyv; double Kxv; double Kyv;
@@ -109,7 +110,7 @@ typedef struct{
double throwpart; // part of values to throw avay @ histogram equalisation double throwpart; // part of values to throw avay @ histogram equalisation
double maxexp; // minimal and maximal exposition (in ms) double maxexp; // minimal and maximal exposition (in ms)
double minexp; double minexp;
double fixedexp; // exptime in manual mode double exptime; // exposure time
double gain; // gain value in manual mode double gain; // gain value in manual mode
double brightness; // brightness @camera double brightness; // brightness @camera
double intensthres; // threshold for stars intensity comparison: fabs(Ia-Ib)/(Ia+Ib) > thres -> stars differs double intensthres; // threshold for stars intensity comparison: fabs(Ia-Ib)/(Ia+Ib) > thres -> stars differs

View File

@@ -47,11 +47,6 @@ do{ fitsstatus = 0; \
if(status) fits_report_error(stderr, status);\ if(status) fits_report_error(stderr, status);\
}while(0) }while(0)
void Image_free(Image **img){
FREE((*img)->data);
FREE(*img);
}
// I->data should be allocated!!! // I->data should be allocated!!!
static inline void convflt2ima(float *f, Image *I){ static inline void convflt2ima(float *f, Image *I){
if(!I || !I->data || !f) return; if(!I || !I->data || !f) return;

View File

@@ -26,7 +26,6 @@
#include "imagefile.h" #include "imagefile.h"
void Image_free(Image **ima);
bool FITS_read(const char *filename, Image **fits); bool FITS_read(const char *filename, Image **fits);
#endif // FITS_H__ #endif // FITS_H__

View File

@@ -279,7 +279,7 @@ static int cam_connect(){
WARNX("Can't turn off triggered mode"); WARNX("Can't turn off triggered mode");
return FALSE; return FALSE;
} }
if(!changeenum("AcquisitionMode", MV_ACQ_MODE_SINGLE)){ if(!changeenum("AcquisitionMode", /*MV_ACQ_MODE_SINGLE*/ MV_ACQ_MODE_CONTINUOUS)){
WARNX("Can't set acquisition mode to single"); WARNX("Can't set acquisition mode to single");
return FALSE; return FALSE;
} }
@@ -356,46 +356,69 @@ static int changeformat(frameformat *f){
return TRUE; return TRUE;
} }
// exptime - in milliseconds!
static int setexp(float e){ static int setexp(float e){
if(!handle) return FALSE; if(!handle){
float eS = e / 1e3; WARNX("NO HANDLE");
if(eS > extrvalues.maxexp || eS < extrvalues.minexp){ return FALSE;
WARNX("Wrong exposure time: %fs (should be [%fs..%fs])", eS, }
if(e > extrvalues.maxexp || e < extrvalues.minexp){
WARNX("Wrong exposure time: %fs (should be [%fs..%fs])", e,
extrvalues.minexp, extrvalues.maxexp); extrvalues.minexp, extrvalues.maxexp);
return FALSE; return FALSE;
} }
if(!changefloat("ExposureTime", e * 1e3)) return FALSE; if(!changefloat("ExposureTime", e * 1e6)){
exptime = eS; WARNX("Can't set exptime %g", e);
return FALSE;
}
exptime = e;
return TRUE; return TRUE;
} }
static int cam_startexp(){ static int cam_startexp(){
if(!handle || !pdata) return FALSE; if(!handle || !pdata) return FALSE;
DBG("Start exposition"); DBG("+++++ Start exposition for %gs", exptime);
MV_CC_StopGrabbing(handle); MV_CC_StopGrabbing(handle);
TRY(StartGrabbing); TRY(StartGrabbing);
ONERR() return FALSE; ONERR(){
DBG("+++++ Ooops! Can't start grabbing: try another time!");
MV_CC_StopGrabbing(handle);
TRY(StartGrabbing);
ONERR(){
DBG("+++++ ERR!");
return FALSE;
}
}
DBG("+++++ OK, started");
return TRUE; return TRUE;
} }
static Image* capture(){ static Image* capture(){
if(!cam_startexp()) return NULL;
MV_FRAME_OUT_INFO_EX stImageInfo = {0}; // last image info
double starttime = sl_dtime(); double starttime = sl_dtime();
static int isstarted = 0;
if(!isstarted){
if(!cam_startexp()) return NULL;
isstarted = 1;
}
MV_FRAME_OUT_INFO_EX stImageInfo = {0}; // last image info
DBG("^^^^^^^^^^^^^^^^^^^^ Started capt @ %g", sl_dtime() - starttime);
do{ do{
usleep(100); usleep(100);
double diff = exptime - (sl_dtime() - starttime); double diff = exptime - (sl_dtime() - starttime);
if(diff > 0.) continue; // wait until exposure ends if(diff > 0.) continue; // wait until exposure ends
DBG("diff = %g", diff); DBG("diff = %g", diff);
if(diff < -5.0){ // wait much longer than exp lasts if(diff < -MAX_READOUT_TM){ // wait much longer than exp lasts
DBG("^^^^^^^^^^^^^^^^^^^^ OOps, time limit");
MV_CC_StopGrabbing(handle); MV_CC_StopGrabbing(handle);
DBG("Restart grabbing");
if(!cam_startexp()) isstarted = 0;
return NULL; return NULL;
} }
TRY(GetOneFrameTimeout, pdata, pdatasz, &stImageInfo, 10); TRY(GetOneFrameTimeout, pdata, pdatasz, &stImageInfo, 100);
ONOK() break; ONOK() break;
}while(1); }while(1);
DBG("^^^^^^^^^^^^^^^^^^^^ Tcapt=%g, exptime=%g", sl_dtime() - starttime, exptime);
Image *captIma = u8toImage(pdata, stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nWidth); Image *captIma = u8toImage(pdata, stImageInfo.nWidth, stImageInfo.nHeight, stImageInfo.nWidth);
DBG("^^^^^^^^^^^^^^^^^^^^ return @ %g", sl_dtime() - starttime);
return captIma; return captIma;
} }

View File

@@ -23,8 +23,11 @@
#define HIKROBOT_CAPT_NAME "hikrobot" #define HIKROBOT_CAPT_NAME "hikrobot"
// maximal readout time, seconds
#define MAX_READOUT_TM (0.3)
// tolerance of float values // tolerance of float values
#define HR_FLOAT_TOLERANCE (0.005) #define HR_FLOAT_TOLERANCE (1.1)
extern camera Hikrobot; extern camera Hikrobot;
#endif #endif

View File

@@ -37,9 +37,10 @@
#include "hikrobot.h" #include "hikrobot.h"
#include "imagefile.h" #include "imagefile.h"
#include "median.h" #include "median.h"
#include "Toupcam.h"
typedef struct{ typedef struct{
const char signature[8]; const uint8_t signature[8];
uint8_t len; uint8_t len;
InputType it; InputType it;
} imsign; } imsign;
@@ -112,6 +113,9 @@ InputType chkinput(const char *name){
#endif #endif
#ifdef MVS_FOUND #ifdef MVS_FOUND
if(0 == strcmp(name, HIKROBOT_CAPT_NAME)) return T_CAPT_HIKROBOT; if(0 == strcmp(name, HIKROBOT_CAPT_NAME)) return T_CAPT_HIKROBOT;
#endif
#ifdef TOUPCAM_FOUND
if(0 == strcmp(name, TOUPCAM_CAPT_NAME)) return T_CAPT_TOUPCAM;
#endif #endif
struct stat fd_stat; struct stat fd_stat;
stat(name, &fd_stat); stat(name, &fd_stat);
@@ -204,14 +208,24 @@ Image *Image_read(const char *name){
* @return data allocated here * @return data allocated here
*/ */
Image *Image_new(int w, int h){ Image *Image_new(int w, int h){
static uint64_t cnt = 0;
if(w < 1 || h < 1) return NULL; if(w < 1 || h < 1) return NULL;
DBGLOG("Image_new(%d, #%u)", w*h, cnt);
Image *outp = MALLOC(Image, 1); Image *outp = MALLOC(Image, 1);
outp->width = w; outp->width = w;
outp->height = h; outp->height = h;
outp->counter = cnt++;
outp->data = MALLOC(Imtype, w*h); outp->data = MALLOC(Imtype, w*h);
return outp; return outp;
} }
void Image_free(Image **I){
if(!I || !*I) return;
DBGLOG("Image_free(%d, #%d)", (*I)->height * (*I)->width, (*I)->counter);
FREE((*I)->data);
FREE(*I);
}
/** /**
* @brief Image_sim - allocate memory for new empty Image with similar size & data type * @brief Image_sim - allocate memory for new empty Image with similar size & data type
* @param i - sample image * @param i - sample image
@@ -257,6 +271,7 @@ int get_histogram(const Image *I, size_t histo[HISTOSZ]){
*/ */
int calc_background(Image *img){ int calc_background(Image *img){
if(!img || !img->data) return FALSE; if(!img || !img->data) return FALSE;
DBG("image min/max=%d/%d", img->minval, img->maxval);
if(img->maxval == img->minval){ if(img->maxval == img->minval){
WARNX("Zero or overilluminated image!"); WARNX("Zero or overilluminated image!");
return FALSE; return FALSE;
@@ -266,7 +281,7 @@ int calc_background(Image *img){
WARNX("Image values too small"); WARNX("Image values too small");
return FALSE; return FALSE;
} }
img->background = theconf.fixedbkg; img->background = theconf.background;
return TRUE; return TRUE;
} }
size_t histogram[HISTOSZ]; size_t histogram[HISTOSZ];

View File

@@ -32,6 +32,11 @@ typedef uint8_t Imtype;
// WARNING! Check code if you change Imtype: e.g. recalcexp() and other // WARNING! Check code if you change Imtype: e.g. recalcexp() and other
#define HISTOSZ (256) #define HISTOSZ (256)
typedef struct{ // statistics: mean, RMS, area
float xc; float yc; float xsigma; float ysigma;
int area;
} ptstat_t;
typedef struct{ typedef struct{
int width; // width int width; // width
int height; // height int height; // height
@@ -40,6 +45,8 @@ typedef struct{
Imtype maxval; Imtype maxval;
float avg_intensity; float avg_intensity;
Imtype background; // background value Imtype background; // background value
ptstat_t stat; // image statistics
uint64_t counter; // image counter
} Image; } Image;
// input file/directory type // input file/directory type
@@ -54,7 +61,8 @@ typedef enum{
T_PNG, T_PNG,
T_CAPT_GRASSHOPPER, // capture grasshopper T_CAPT_GRASSHOPPER, // capture grasshopper
T_CAPT_BASLER, T_CAPT_BASLER,
T_CAPT_HIKROBOT T_CAPT_HIKROBOT,
T_CAPT_TOUPCAM,
} InputType; } InputType;
void Image_minmax(Image *I); void Image_minmax(Image *I);

View File

@@ -31,9 +31,11 @@
#include "draw.h" #include "draw.h"
#include "grasshopper.h" #include "grasshopper.h"
#include "hikrobot.h" #include "hikrobot.h"
#include "imagefile.h"
#include "improc.h" #include "improc.h"
#include "inotify.h" #include "inotify.h"
#include "steppers.h" #include "steppers.h"
#include "Toupcam.h"
volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images volatile atomic_ullong ImNumber = 0; // GLOBAL: counter of processed images
volatile atomic_bool stopwork = FALSE; // GLOBAL: suicide volatile atomic_bool stopwork = FALSE; // GLOBAL: suicide
@@ -132,7 +134,7 @@ static void getDeviation(object *curobj){
averflag = 1; averflag = 1;
if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy); if(fXYlog) fprintf(fXYlog, "%.1f\t%.1f\t%.1f\t%.1f", xx, yy, Sx, Sy);
process_corrections: process_corrections:
LOGDBG("here"); //LOGDBG("here");
if(theSteppers){ if(theSteppers){
DBG("Process corrections"); DBG("Process corrections");
if(theSteppers->proc_corr && averflag){ if(theSteppers->proc_corr && averflag){
@@ -145,14 +147,10 @@ process_corrections:
LOGERR("Lost connection with stepper server"); LOGERR("Lost connection with stepper server");
WARNX("Lost connection with stepper server"); WARNX("Lost connection with stepper server");
} }
LOGDBG("And there"); //LOGDBG("And there");
XYnewline(); XYnewline();
} }
typedef struct{ // statistics: mean and RMS
float xc; float yc; float xsigma; float ysigma;
} ptstat_t;
/** /**
* @brief sumAndStat - calculate statistics in region of interest * @brief sumAndStat - calculate statistics in region of interest
* @param I - image (with background calculated) * @param I - image (with background calculated)
@@ -206,8 +204,6 @@ void process_file(Image *I){
static int prev_x = -1, prev_y = -1; static int prev_x = -1, prev_y = -1;
static object *Objects = NULL; static object *Objects = NULL;
static size_t Nallocated = 0; static size_t Nallocated = 0;
il_ConnComps *cc = NULL;
size_t *S = NULL;
#ifdef EBUG #ifdef EBUG
double t0 = sl_dtime(), tlast = t0; double t0 = sl_dtime(), tlast = t0;
#define DELTA(p) do{double t = sl_dtime(); DBG("---> %s @ %gms (delta: %gms)", p, (t-t0)*1e3, (t-tlast)*1e3); tlast = t;}while(0) #define DELTA(p) do{double t = sl_dtime(); DBG("---> %s @ %gms (delta: %gms)", p, (t-t0)*1e3, (t-tlast)*1e3); tlast = t;}while(0)
@@ -228,6 +224,7 @@ void process_file(Image *I){
//DELTA("Save original"); //DELTA("Save original");
if(calc_background(I)){ if(calc_background(I)){
DBG("backgr = %d", I->background); DBG("backgr = %d", I->background);
theconf.background = I->background;
DELTA("Got background"); DELTA("Got background");
int objctr = 0; int objctr = 0;
if(prev_x > 0 && prev_y > 0){ if(prev_x > 0 && prev_y > 0){
@@ -241,6 +238,7 @@ void process_file(Image *I){
DBG("Get sum and stat for simplest centroid"); DBG("Get sum and stat for simplest centroid");
double sum = sumAndStat(I, NULL, 0, &roi, &stat); double sum = sumAndStat(I, NULL, 0, &roi, &stat);
if(sum > 0.){ if(sum > 0.){
I->stat = stat;
if( fabsf(stat.xc - prev_x) > XY_TOLERANCE || if( fabsf(stat.xc - prev_x) > XY_TOLERANCE ||
fabsf(stat.yc - prev_y) > XY_TOLERANCE){ fabsf(stat.yc - prev_y) > XY_TOLERANCE){
DBG("Bad: was x=%d, y=%d; become x=%g, y=%g ==> need fine calculations", prev_x, prev_y, xc, yc); DBG("Bad: was x=%d, y=%d; become x=%g, y=%g ==> need fine calculations", prev_x, prev_y, xc, yc);
@@ -248,6 +246,7 @@ void process_file(Image *I){
double WdH = stat.xsigma/stat.ysigma; double WdH = stat.xsigma/stat.ysigma;
// wery approximate area inside sigmax*sigmay // wery approximate area inside sigmax*sigmay
double area = .4 * stat.xsigma * stat.ysigma; double area = .4 * stat.xsigma * stat.ysigma;
I->stat.area = (int)area;
if(!isnan(WdH) && !isinf(WdH) && // if W/H is a number if(!isnan(WdH) && !isinf(WdH) && // if W/H is a number
WdH > theconf.minwh && WdH < theconf.maxwh && // if W/H near circle WdH > theconf.minwh && WdH < theconf.maxwh && // if W/H near circle
area > theconf.minarea && area < theconf.maxarea){ // if star area is in range area > theconf.minarea && area < theconf.maxarea){ // if star area is in range
@@ -274,31 +273,45 @@ void process_file(Image *I){
uint8_t *ibin = Im2bin(I, I->background); uint8_t *ibin = Im2bin(I, I->background);
DELTA("Made binary"); DELTA("Made binary");
if(ibin){ if(ibin){
//savebin(ibin, W, H, "binary.fits"); if(theconf.writedebugimgs){
//DELTA("save binary.fits"); Image *Itmp = bin2Im(ibin, I->width, I->height);
Image_write_jpg(Itmp, "binary.jpg", 1);
Image_free(&Itmp);
DELTA("save binary");
}
uint8_t *er = il_erosionN(ibin, W, H, theconf.Nerosions); uint8_t *er = il_erosionN(ibin, W, H, theconf.Nerosions);
FREE(ibin); FREE(ibin);
DELTA("Erosion"); DELTA("Erosion");
//savebin(er, W, H, "erosion.fits"); if(theconf.writedebugimgs){
//DELTA("Save erosion"); Image *Itmp = bin2Im(er, I->width, I->height);
Image_write_jpg(Itmp, "erosion.jpg", 1);
Image_free(&Itmp);
DELTA("Save erosion");
}
uint8_t *opn = il_dilationN(er, W, H, theconf.Ndilations); uint8_t *opn = il_dilationN(er, W, H, theconf.Ndilations);
FREE(er); FREE(er);
DELTA("Opening"); DELTA("Opening");
//savebin(opn, W, H, "opening.fits"); if(theconf.writedebugimgs){
//DELTA("Save opening"); Image *Itmp = bin2Im(opn, I->width, I->height);
S = il_cclabel4(opn, W, H, &cc); Image_write_jpg(Itmp, "opening.jpg", 1);
Image_free(&Itmp);
DELTA("Save opening");
}
il_ConnComps *cc = NULL;
size_t *S = il_cclabel4(opn, W, H, &cc);
FREE(opn); FREE(opn);
DBG("Nobj=%zd", cc->Nobj-1); if(S && cc) DBG("Nobj=%zd", cc->Nobj-1);
if(S && cc && cc->Nobj > 1){ // Nobj = amount of objects + 1 if(S && cc && cc->Nobj > 1){ // Nobj = amount of objects + 1
DBGLOG("Nobj=%zd", cc->Nobj-1); DBGLOG("Nobj=%zd", cc->Nobj-1);
if(Nallocated < cc->Nobj-1){ if(Nallocated < cc->Nobj-1){
Nallocated = cc->Nobj-1; Nallocated = cc->Nobj-1;
Objects = realloc(Objects, Nallocated*sizeof(object)); Objects = realloc(Objects, Nallocated*sizeof(object));
} }
I->stat.area = cc->boxes[1].area;
for(size_t i = 1; i < cc->Nobj; ++i){ for(size_t i = 1; i < cc->Nobj; ++i){
il_Box *b = &cc->boxes[i]; il_Box *b = &cc->boxes[i];
double wh = ((double)b->xmax - b->xmin)/(b->ymax - b->ymin); double wh = ((double)b->xmax - b->xmin)/(b->ymax - b->ymin);
//DBG("Obj# %zd: wh=%g, area=%d", i, wh, b->area); DBG("Obj# %zd: wh=%g, area=%d", i, wh, b->area);
if(wh < theconf.minwh || wh > theconf.maxwh) continue; if(wh < theconf.minwh || wh > theconf.maxwh) continue;
if((int)b->area < theconf.minarea || (int)b->area > theconf.maxarea) continue; if((int)b->area < theconf.minarea || (int)b->area > theconf.maxarea) continue;
ptstat_t stat; ptstat_t stat;
@@ -323,68 +336,68 @@ void process_file(Image *I){
else else
qsort(Objects, objctr, sizeof(object), compDist); qsort(Objects, objctr, sizeof(object), compDist);
} }
SKIP_FULL_PROCESS:
DBGLOG("T%.2f, N=%d\n", sl_dtime(), objctr);
DELTA("Calculate deviations");
if(objctr){
#ifdef EBUG
object *o = Objects;
green("%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%8s\n",
"N", "Area", "Mv", "W/H", "Xc", "Yc", "Sx", "Sy", "Area/r^2");
for(int i = 0; i < objctr; ++i, ++o){
// 1.0857 = 2.5/ln(10)
printf("%6d\t%6d\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%8.1f\n",
i, o->area, 20.-1.0857*log(o->Isum), o->WdivH, o->xc, o->yc,
o->xsigma, o->ysigma, o->area/o->xsigma/o->ysigma);
}
#endif
getDeviation(Objects); // calculate dX/dY and process corrections
}
DELTA("prepare image");
{ // prepare image and save jpeg
uint8_t *outp = NULL;
if(theconf.equalize)
outp = equalize(I, 3, theconf.throwpart);
else
outp = linear(I, 3);
static il_Pattern *cross = NULL, *crossL = NULL;
if(!cross) cross = il_Pattern_xcross(33, 33);
if(!crossL) crossL = il_Pattern_xcross(51, 51);
il_Img3 i3 = {.data = outp, .w = I->width, .h = H};
DELTA("Draw crosses");
// draw fiber center position
il_Pattern_draw3(&i3, crossL, theconf.xtarget-theconf.xoff, H-(theconf.ytarget-theconf.yoff), C_R);
if(objctr){ // draw crosses @ objects' centers
int H = I->height;
// draw current star centroid
il_Pattern_draw3(&i3, cross, Objects[0].xc, H-Objects[0].yc, C_G);
// add offset to show in target system
xc = Objects[0].xc + theconf.xoff;
yc = Objects[0].yc + theconf.yoff;
// draw other centroids
for(int i = 1; i < objctr; ++i)
il_Pattern_draw3(&i3, cross, Objects[i].xc, H-Objects[i].yc, C_B);
}else{xc = -1.; yc = -1.;}
char tmpnm[FILENAME_MAX+5];
sprintf(tmpnm, "%s-tmp", GP->outputjpg);
if(stbi_write_jpg(tmpnm, I->width, I->height, 3, outp, 95)){
if(rename(tmpnm, GP->outputjpg)){
WARN("rename()");
LOGWARN("can't save %s", GP->outputjpg);
}
}
DELTA("Written");
FREE(outp);
}
}else{
xc = -1.; yc = -1.;
Image_write_jpg(I, GP->outputjpg, theconf.equalize);
} }
DBGLOG("Image saved");
FREE(S); FREE(S);
FREE(cc); FREE(cc);
} }
}else Image_write_jpg(I, GP->outputjpg, theconf.equalize); SKIP_FULL_PROCESS:
DBGLOG("T%.2f, N=%d\n", sl_dtime(), objctr);
DELTA("Calculate deviations");
if(objctr){
#ifdef EBUG
object *o = Objects;
green("%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%6s\t%8s\n",
"N", "Area", "Mv", "W/H", "Xc", "Yc", "Sx", "Sy", "Area/r^2");
for(int i = 0; i < objctr; ++i, ++o){
// 1.0857 = 2.5/ln(10)
printf("%6d\t%6d\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%6.1f\t%8.1f\n",
i, o->area, 20.-1.0857*log(o->Isum), o->WdivH, o->xc, o->yc,
o->xsigma, o->ysigma, o->area/o->xsigma/o->ysigma);
}
#endif
getDeviation(Objects); // calculate dX/dY and process corrections
}
DELTA("prepare image");
{ // prepare image and save jpeg
uint8_t *outp = NULL;
if(theconf.equalize)
outp = equalize(I, 3, theconf.throwpart);
else
outp = linear(I, 3);
static il_Pattern *cross = NULL, *crossL = NULL;
if(!cross) cross = il_Pattern_xcross(33, 33);
if(!crossL) crossL = il_Pattern_xcross(51, 51);
il_Img3 i3 = {.data = outp, .w = I->width, .h = H};
DELTA("Draw crosses");
// draw fiber center position
il_Pattern_draw3(&i3, crossL, theconf.xtarget-theconf.xoff, H-(theconf.ytarget-theconf.yoff), C_R);
if(objctr){ // draw crosses @ objects' centers
int H = I->height;
// draw current star centroid
il_Pattern_draw3(&i3, cross, Objects[0].xc, H-Objects[0].yc, C_G);
// add offset to show in target system
xc = Objects[0].xc + theconf.xoff;
yc = Objects[0].yc + theconf.yoff;
// draw other centroids
for(int i = 1; i < objctr; ++i)
il_Pattern_draw3(&i3, cross, Objects[i].xc, H-Objects[i].yc, C_B);
}else{xc = -1.; yc = -1.;}
char tmpnm[FILENAME_MAX+5];
sprintf(tmpnm, "%s-tmp", GP->outputjpg);
if(stbi_write_jpg(tmpnm, I->width, I->height, 3, outp, 95)){
if(rename(tmpnm, GP->outputjpg)){
WARN("rename()");
LOGWARN("can't save %s", GP->outputjpg);
}
}
DELTA("Written");
FREE(outp);
}
}else{
xc = -1.; yc = -1.;
Image_write_jpg(I, GP->outputjpg, theconf.equalize);
}
DBGLOG("Image saved");
++ImNumber; ++ImNumber;
if(lastTproc > 1.) FPS = 1. / (sl_dtime() - lastTproc); if(lastTproc > 1.) FPS = 1. / (sl_dtime() - lastTproc);
lastTproc = sl_dtime(); lastTproc = sl_dtime();
@@ -412,7 +425,7 @@ int process_input(InputType tp, char *name){
if(tp == T_DIRECTORY){ if(tp == T_DIRECTORY){
imagedata = watchdr; imagedata = watchdr;
return watch_directory(name, process_file); return watch_directory(name, process_file);
}else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER || tp == T_CAPT_HIKROBOT){ }else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER || tp == T_CAPT_HIKROBOT || tp == T_CAPT_TOUPCAM){
camera *cam = NULL; camera *cam = NULL;
switch(tp){ switch(tp){
case T_CAPT_GRASSHOPPER: case T_CAPT_GRASSHOPPER:
@@ -428,6 +441,11 @@ int process_input(InputType tp, char *name){
case T_CAPT_HIKROBOT: case T_CAPT_HIKROBOT:
#ifdef MVS_FOUND #ifdef MVS_FOUND
cam = &Hikrobot; cam = &Hikrobot;
#endif
break;
case T_CAPT_TOUPCAM:
#ifdef TOUPCAM_FOUND
cam = &Toupcam;
#endif #endif
break; break;
default: return FALSE; default: return FALSE;

View File

@@ -76,7 +76,8 @@ static InputType chk_inp(const char *name){
if(!name) ERRX("Point file or directory name to monitor"); if(!name) ERRX("Point file or directory name to monitor");
InputType itp = chkinput(name); InputType itp = chkinput(name);
if(T_WRONG == itp) return T_WRONG; if(T_WRONG == itp) return T_WRONG;
green("\n%s is a ", name); green("\n%s", name);
printf(" is a ");
switch(itp){ switch(itp){
case T_DIRECTORY: case T_DIRECTORY:
printf("directory"); printf("directory");
@@ -108,6 +109,9 @@ static InputType chk_inp(const char *name){
case T_CAPT_HIKROBOT: case T_CAPT_HIKROBOT:
printf("hikrobot camera capture"); printf("hikrobot camera capture");
break; break;
case T_CAPT_TOUPCAM:
printf("toupcam camera capture");
break;
default: default:
printf("unsupported type\n"); printf("unsupported type\n");
return T_WRONG; return T_WRONG;

View File

@@ -67,6 +67,7 @@ typedef struct{
static char *helpmsg(const char *messageid, char *buf, int buflen); static char *helpmsg(const char *messageid, char *buf, int buflen);
static char *stepperstatus(const char *messageid, char *buf, int buflen); static char *stepperstatus(const char *messageid, char *buf, int buflen);
static char *getimagedata(const char *messageid, char *buf, int buflen); static char *getimagedata(const char *messageid, char *buf, int buflen);
// should be in sorted order
static getter getterHandlers[] = { static getter getterHandlers[] = {
{"help", helpmsg, "List avaiable commands"}, {"help", helpmsg, "List avaiable commands"},
{"imdata", getimagedata, "Get image data (status, path, FPS, counter)"}, {"imdata", getimagedata, "Get image data (status, path, FPS, counter)"},
@@ -80,7 +81,7 @@ static char *setfocusstate(const char *state, char *buf, int buflen);
static char *moveU(const char *val, char *buf, int buflen); static char *moveU(const char *val, char *buf, int buflen);
static char *moveV(const char *val, char *buf, int buflen); static char *moveV(const char *val, char *buf, int buflen);
static char *addcmnt(const char *cmnt, char *buf, int buflen); static char *addcmnt(const char *cmnt, char *buf, int buflen);
// should be in sorted order
static setter setterHandlers[] = { static setter setterHandlers[] = {
{"comment", addcmnt, "Add comment to XY log file"}, {"comment", addcmnt, "Add comment to XY log file"},
{"focus", setfocusstate, "Move focus to given value"}, {"focus", setfocusstate, "Move focus to given value"},

View File

@@ -86,8 +86,8 @@ steppersproc *theSteppers = NULL;
// stepper numbers // stepper numbers
typedef enum{ typedef enum{
Ustepper = 0, Ustepper = 0,
Vstepper = 2, Vstepper = 1,
Fstepper = 1, Fstepper = 2,
} stepperno; } stepperno;
const char *motornames[NMOTORS] = { const char *motornames[NMOTORS] = {
@@ -200,6 +200,7 @@ static void stp_disc(){
static void stp_disconnect(){ static void stp_disconnect(){
DBG("Try to disconnect"); DBG("Try to disconnect");
LOGDBG("Try to disconnect");
if(serialsock){ if(serialsock){
DBG("Close socket"); DBG("Close socket");
sl_sock_delete(&serialsock); sl_sock_delete(&serialsock);
@@ -292,9 +293,8 @@ static void parse_msg(char *msg){
for(int idx = 0; idx < CMD_AMOUNT; ++idx){ for(int idx = 0; idx < CMD_AMOUNT; ++idx){
// search index in commands // search index in commands
if(0 == strcmp(stp_commands[idx], key)){ // found our if(0 == strcmp(stp_commands[idx], key)){ // found our
free(key);
//LOGDBG("OK, idx=%d, cmd=%s", idx, stp_commands[idx]); //LOGDBG("OK, idx=%d, cmd=%s", idx, stp_commands[idx]);
parser(idx, parno, ival); LastErr = parser(idx, parno, ival);
break; break;
} }
} }
@@ -323,14 +323,19 @@ static errcodes send_message(steppercmd idx, char *msg){
if(!msg) msglen = snprintf(buf, 255, "%s\n", stp_commands[idx]); if(!msg) msglen = snprintf(buf, 255, "%s\n", stp_commands[idx]);
else msglen = snprintf(buf, 255, "%s%s\n", stp_commands[idx], msg); else msglen = snprintf(buf, 255, "%s%s\n", stp_commands[idx], msg);
//DBG("Send message '%s', len %zd", buf, msglen); //DBG("Send message '%s', len %zd", buf, msglen);
LastErr = ERR_AMOUNT;
if(sl_sock_sendstrmessage(serialsock, buf) != (ssize_t)msglen){ if(sl_sock_sendstrmessage(serialsock, buf) != (ssize_t)msglen){
WARN("send()"); WARN("send()");
LOGWARN("send_message(): send() failed"); LOGWARN("send_message(): send() failed");
return ERR_WRONGLEN; return ERR_WRONGLEN;
} }
LOGDBG("Message '%s' sent", buf); //LOGDBG("send_message(): message '%s' sent", buf);
;;; // wait for errcode double t0 = sl_dtime();
return ERR_OK; while(sl_dtime() - t0 < STEPPERS_NOANS_TIMEOUT){
if(LastErr != ERR_AMOUNT) return LastErr;
}
LOGWARN("send_message(): got NO answer for %s%s", stp_commands[idx], msg);
return ERR_CANTRUN;
} }
// send command cmd to n'th motor with param p, @return FALSE if failed // send command cmd to n'th motor with param p, @return FALSE if failed
@@ -392,20 +397,24 @@ void *clientproc(void _U_ *par){
FNAME(); FNAME();
char rbuf[BUFSIZ]; char rbuf[BUFSIZ];
if(!serialsock) return NULL; if(!serialsock) return NULL;
double t0 = sl_dtime();
do{ do{
ssize_t got = sl_sock_readline(serialsock, rbuf, BUFSIZ); ssize_t got = sl_sock_readline(serialsock, rbuf, BUFSIZ);
//LOGDBG("got=%zd", got); //LOGDBG("got=%zd", got);
if(got < 0){ // disconnected if(got < 0 || sl_dtime() - t0 > STEPPERS_NOANS_TIMEOUT){ // disconnected
WARNX("Serial server disconnected"); WARNX("Serial server disconnected");
LOGERR("Serial server disconnected (timeout reached)");
if(serialsock) sl_sock_delete(&serialsock); if(serialsock) sl_sock_delete(&serialsock);
state = STP_DISCONN;
return NULL; return NULL;
}else if(got == 0){ // nothing to read from serial port }else if(got == 0){ // nothing to read from serial port
usleep(1000); usleep(1000);
continue; continue;
} }
//LOGDBG("clientproc(): got '%s'", rbuf);
// process data // process data
DBG("GOT: %s", rbuf);
parse_msg(rbuf); parse_msg(rbuf);
t0 = sl_dtime();
} while(serialsock && serialsock->connected); } while(serialsock && serialsock->connected);
WARNX("disconnected"); WARNX("disconnected");
if(serialsock) sl_sock_delete(&serialsock); if(serialsock) sl_sock_delete(&serialsock);
@@ -458,9 +467,9 @@ static void process_movetomiddle_stage(){
case SETUP_WAITUV0: // wait for all coordinates moving to zero case SETUP_WAITUV0: // wait for all coordinates moving to zero
if(!relaxed(Ustepper) || !relaxed(Vstepper) || !relaxed(Fstepper)) break; // didn't reach yet if(!relaxed(Ustepper) || !relaxed(Vstepper) || !relaxed(Fstepper)) break; // didn't reach yet
// now all motors are stopped -> send positions to zero // now all motors are stopped -> send positions to zero
if( !nth_motor_setter(CMD_ABSPOS, Ustepper, 1) || if( !nth_motor_setter(CMD_ABSPOS, Ustepper, 0) ||
!nth_motor_setter(CMD_ABSPOS, Vstepper, 1) || !nth_motor_setter(CMD_ABSPOS, Vstepper, 0) ||
!nth_motor_setter(CMD_ABSPOS, Fstepper, 1)) break; !nth_motor_setter(CMD_ABSPOS, Fstepper, 0)) break;
DBG("Reached UVF0!"); DBG("Reached UVF0!");
// goto // goto
if(nth_motor_setter(CMD_GOTO, Ustepper, (theconf.maxUpos + theconf.minUpos)/2) && if(nth_motor_setter(CMD_GOTO, Ustepper, (theconf.maxUpos + theconf.minUpos)/2) &&
@@ -508,8 +517,8 @@ static void process_setup_stage(){
case SETUP_WAITUV0: // wait for both coordinates moving to zero case SETUP_WAITUV0: // wait for both coordinates moving to zero
if(!relaxed(Ustepper) || !relaxed(Vstepper)) break; if(!relaxed(Ustepper) || !relaxed(Vstepper)) break;
// set current position to 0 // set current position to 0
if( !nth_motor_setter(CMD_ABSPOS, Ustepper, 1) || if( !nth_motor_setter(CMD_ABSPOS, Ustepper, 0) ||
!nth_motor_setter(CMD_ABSPOS, Vstepper, 1)) break; !nth_motor_setter(CMD_ABSPOS, Vstepper, 0)) break;
DBG("ZERO border reached"); DBG("ZERO border reached");
// goto middle // goto middle
if(nth_motor_setter(CMD_GOTO, Ustepper, (theconf.maxUpos+theconf.minUpos)/2) && if(nth_motor_setter(CMD_GOTO, Ustepper, (theconf.maxUpos+theconf.minUpos)/2) &&
@@ -901,30 +910,38 @@ static void *stp_process_states(_U_ void *arg){
stp_connect_server(); stp_connect_server();
continue; continue;
} }
// check request to change focus
if(chfocus){
DBG("Try to move F to %d", newfocpos);
if(nth_motor_setter(CMD_GOTO, Fstepper, newfocpos)) chfocus = FALSE;
}
if(dUmove){
DBG("Try to move U by %d", dUmove);
if(nth_motor_setter(CMD_RELPOS, Ustepper, dUmove)) dUmove = 0;
}
if(dVmove){
DBG("Try to move V by %d", dVmove);
if(nth_motor_setter(CMD_RELPOS, Vstepper, dVmove)) dVmove = 0;
}
static double t0 = -1.; static double t0 = -1.;
if(t0 < 0.) t0 = sl_dtime(); if(t0 < 0.) t0 = sl_dtime();
if(state != STP_DISCONN){ if(state != STP_DISCONN){
if(sl_dtime() - t0 >= 0.1){ // each 0.1s check state if steppers aren't disconnected if(sl_dtime() - t0 >= 0.1){ // each 0.1s check state if steppers aren't disconnected
t0 = sl_dtime(); t0 = sl_dtime();
chkall(); chkall();
} if(!relaxed(Ustepper) && !relaxed(Vstepper)) continue;
if(!relaxed(Ustepper) && !relaxed(Vstepper)) continue; first = TRUE;
first = TRUE; }else continue;
} }
// if we are here, all U/V moving is finished // if we are here, all U/V moving is finished
// check request to change focus
if(chfocus){
DBG("Try to move F to %d", newfocpos);
if(nth_motor_setter(CMD_GOTO, Fstepper, newfocpos)){
chfocus = FALSE;
}
}
if(dUmove){
DBG("Try to move U by %d", dUmove);
if(nth_motor_setter(CMD_RELPOS, Ustepper, dUmove)){
dUmove = 0;
continue;
}
}
if(dVmove){
DBG("Try to move V by %d", dVmove);
if(nth_motor_setter(CMD_RELPOS, Vstepper, dVmove)){
dVmove = 0;
continue;
}
}
switch(state){ // steppers state machine switch(state){ // steppers state machine
case STP_DISCONN: case STP_DISCONN:
if(!stp_connect_server()){ if(!stp_connect_server()){
@@ -933,7 +950,7 @@ static void *stp_process_states(_U_ void *arg){
LOGWARN("Can't reconnect"); LOGWARN("Can't reconnect");
first = FALSE; first = FALSE;
} }
sleep(5); sleep(1);
} }
break; break;
case STP_SETUP: // setup axes (before this state set Xtarget/Ytarget in improc.c) case STP_SETUP: // setup axes (before this state set Xtarget/Ytarget in improc.c)

View File

@@ -18,6 +18,9 @@
#pragma once #pragma once
// timeout for "no answer from motors", seconds
#define STEPPERS_NOANS_TIMEOUT (5.)
// set state to `disconnect` after this amount of errors in `moving_finished` // set state to `disconnect` after this amount of errors in `moving_finished`
#define MAX_ERR_CTR (15) #define MAX_ERR_CTR (15)

318
LocCorr_new/toupcam.c Normal file
View File

@@ -0,0 +1,318 @@
/*
* This file is part of the loccorr project.
* Copyright 2026 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <float.h>
#include <pthread.h>
#include <string.h>
#include <toupcam.h>
#include <usefull_macros.h>
#include "Toupcam.h"
// flags for image processing
typedef enum{
IM_SLEEP,
IM_STARTED,
IM_READY,
IM_ERROR
} imstate_t;
// devices
static ToupcamDeviceV2 g_dev[TOUPCAM_MAX] = {0};
static struct{
ToupcamDeviceV2* dev; // device
HToupcam hcam; // hcam for all functions
unsigned long long flags; // flags (read on connect)
pthread_mutex_t mutex; // lock mutex for `data` writing/reading
void* data; // image data
size_t imsz; // size of current image in bytes
imstate_t state; // current state
uint64_t imseqno; // number of image from connection
uint64_t lastcapno; // last captured image number
} toupcam = {0};
// array - max ROI; geometry - current ROI
static frameformat array = {.xoff=0, .yoff = 0, .w = 800, .h = 600}, geometry = {0};
// exptime (in seconds!!!) and starting of exposition
static double exptimeS = 0.1, starttime = 0.;
#define TCHECK() do{if(!toupcam.hcam) return FALSE;}while(0)
// return constant string with error code description
static const char *errcode(int ecode){
switch(ecode){
case 0: return "S_OK";
case 1: return "S_FALSE";
case 0x8000ffff: return "E_UNEXPECTED";
case 0x80004001: return "E_NOTIMPL";
case 0x80004002: return "E_NOINTERFACE";
case 0x80070005: return "E_ACCESSDENIED";
case 0x8007000e: return "E_OUTOFMEMORY";
case 0x80070057: return "E_INVALIDARG";
case 0x80004003: return "E_POINTER";
case 0x80004005: return "E_FAIL";
case 0x8001010e: return "E_WRONG_THREAD";
case 0x8007001f: return "E_GEN_FAILURE";
case 0x800700aa: return "E_BUSY";
case 0x8000000a: return "E_PENDING";
case 0x8001011f: return "E_TIMEOUT";
case 0x80072743: return "E_UNREACH";
default: return "Unknown error";
}
}
static void camcancel(){
FNAME();
if(!toupcam.hcam) return;
int e = Toupcam_Trigger(toupcam.hcam, 0); // stop triggering
if(e < 0) WARNX("Can't trigger 0: %s", errcode(e));
e = Toupcam_Stop(toupcam.hcam);
if(e < 0) WARNX("Can't stop: %s", errcode(e));
toupcam.state = IM_SLEEP;
}
static void Tdisconnect(){
FNAME();
camcancel();
if(toupcam.hcam){
DBG("Close camera");
Toupcam_Close(toupcam.hcam);
toupcam.hcam = NULL;
}
if(toupcam.data){
DBG("Free image data");
free(toupcam.data);
toupcam.data = NULL;
}
}
static void EventCallback(unsigned nEvent, void _U_ *pCallbackCtx){
FNAME();
DBG("CALLBACK with evt %d", nEvent);
if(!toupcam.hcam || !toupcam.data){ DBG("NO data!"); return; }
if(nEvent != TOUPCAM_EVENT_IMAGE){ DBG("Not image event"); return; }
ToupcamFrameInfoV4 info = {0};
pthread_mutex_lock(&toupcam.mutex);
if(Toupcam_PullImageV4(toupcam.hcam, toupcam.data, 0, 0, 0, &info) < 0){
DBG("Error pulling image");
toupcam.state = IM_ERROR;
}else{
++toupcam.imseqno;
DBG("Image %lu (%dx%d) ready!", toupcam.imseqno, info.v3.width, info.v3.height);
toupcam.state = IM_READY;
toupcam.imsz = info.v3.height * info.v3.width;
geometry.h = info.v3.height;
geometry.w = info.v3.width;
}
pthread_mutex_unlock(&toupcam.mutex);
}
static int startexp(){
FNAME();
TCHECK();
if(toupcam.state == IM_SLEEP){
DBG("Sleeping -> start pull mode");
if(Toupcam_StartPullModeWithCallback(toupcam.hcam, EventCallback, NULL) < 0){
WARNX("Can't run PullMode with Callback!");
return FALSE;
}
}
// Ask to trigger for several images (maximal speed available)
DBG("Trigger images");
int e = Toupcam_Trigger(toupcam.hcam, 100);
if(e < 0){
DBG("Can't ask for images stream: %s; try 1", errcode(e));
e = Toupcam_Trigger(toupcam.hcam, 1);
if(e < 0){
WARNX("Can't ask for next image: %s", errcode(e));
return FALSE;
}
}
toupcam.state = IM_STARTED;
starttime = sl_dtime();
return TRUE;
}
static int Texp(float t){ // t - in milliseconds!!!
FNAME();
TCHECK();
if(t < FLT_EPSILON) return FALSE;
unsigned int microseconds = (unsigned)(t * 1e3f);
DBG("Set exptime to %dus", microseconds);
camcancel();
int e = Toupcam_put_ExpoTime(toupcam.hcam, microseconds);
if(e < 0){
WARNX("Can't set exp: %s", errcode(e));
//startexp();
return FALSE;
}
DBG("OK");
if(Toupcam_get_ExpoTime(toupcam.hcam, &microseconds) < 0) exptimeS = (double) t / 1e3;
else exptimeS = (float)microseconds / 1e6f;
DBG("Real exptime: %.4fs", exptimeS);
//startexp();
return TRUE;
}
static int Tconnect(){
FNAME();
Tdisconnect();
unsigned N = Toupcam_EnumV2(g_dev);
if(0 == N){
DBG("Found 0 toupcams");
return FALSE;
}
toupcam.dev = &g_dev[0];
toupcam.hcam = Toupcam_Open(g_dev[0].id);
if(!toupcam.hcam){
WARN("Can't open toupcam camera");
return FALSE;
}
DBG("Opened %s", toupcam.dev->displayname);
DBG("Clear ROI");
Toupcam_put_Roi(toupcam.hcam, 0, 0, 0, 0); // clear ROI
// now fill camera geometry
unsigned int xoff, yoff, h, w;
DBG("Get ROI");
Toupcam_get_Roi(toupcam.hcam, &xoff, &yoff, &w, &h);
DBG("off (x/y): %d/%d; wxh: %dx%d", xoff, yoff, w, h);
geometry.xoff = xoff; geometry.yoff = yoff; geometry.w = w; geometry.h = h;
toupcam.flags = Toupcam_query_Model(toupcam.hcam)->flag;
DBG("flags: 0x%llx", toupcam.flags);
DBG("Allocate data (%d bytes: 2*%d*%d)", 2 * array.w * array.h, array.w, array.h);
toupcam.data = calloc(array.w * array.h, 1);
#define OPT(opt, val, comment) do{DBG(comment); if(Toupcam_put_Option(toupcam.hcam, opt, val) < 0){ DBG("Can't put this option"); }}while(0)
// 12 frames/sec
OPT(TOUPCAM_OPTION_TRIGGER, 1, "Software/simulated trigger mode");
OPT(TOUPCAM_OPTION_RAW, 1, "Put to RAW mode");
OPT(TOUPCAM_OPTION_BINNING, 1, "Set binning to 1x1");
#undef OPT
toupcam.state = IM_SLEEP;
toupcam.imseqno = toupcam.lastcapno = 0;
// 8bit
if(Toupcam_put_Option(toupcam.hcam, TOUPCAM_OPTION_BITDEPTH, 0) < 0) WARNX("Cant set bitdepth");
if(Toupcam_put_Option(toupcam.hcam, TOUPCAM_OPTION_PIXEL_FORMAT, TOUPCAM_PIXELFORMAT_RAW8) < 0){
WARNX("Cannot init 8bit mode!");
Tdisconnect();
return FALSE;
}
pthread_mutex_init(&toupcam.mutex, NULL);
if(!Texp(0.1)){ WARNX("Can't set default exptime"); }
return TRUE;
}
static Image *Tcapture(){
FNAME();
if(!toupcam.hcam || !toupcam.data) return NULL;
if(!startexp()){
WARNX("Can't start exposition");
return NULL;
}
DBG("here, exptime=%gs, dstart=%g", exptimeS, (sl_dtime() - starttime));
double tremain = 0.;
while(toupcam.state != IM_READY){
tremain = exptimeS - (sl_dtime() - starttime);
if(tremain < -2.0){
WARNX("Timeout - failed");
camcancel();
return NULL;
}
usleep(100);
}
if(toupcam.state != IM_READY){
WARNX("State=%d, not ready", toupcam.state);
return NULL;
}
pthread_mutex_lock(&toupcam.mutex);
Image *o = u8toImage(toupcam.data, geometry.w, geometry.h, geometry.w);
toupcam.lastcapno = toupcam.imseqno;
pthread_mutex_unlock(&toupcam.mutex);
return o;
}
static int Tbright(float b){
FNAME();
TCHECK();
if(b < -255.f || b > 255.f){
WARNX("Available brightness: -255..255");
return FALSE;
}
int br = (int) b;
DBG("Try to set brightness to %d", br);
camcancel();
int e = Toupcam_put_Brightness(toupcam.hcam, br);
//startexp();
if(e < 0){
WARNX("Can't set brightness: %s", errcode(e));
return FALSE;
}
DBG("OK");
return TRUE;
}
static int Tgain(float g){
FNAME();
TCHECK();
unsigned short G = (unsigned short)(100.f * g);
int ret = FALSE;
camcancel();
if(Toupcam_put_ExpoAGain(toupcam.hcam, G) < 0){
WARNX("Gain out of range: 1..8");
}else{ DBG("GAIN is %d", G); ret = TRUE;}
//startexp();
return ret;
}
static float Tmaxgain(){
FNAME();
return 8.f; // toupcam SDK returns wrong value: 16.
}
static int Tgeometry(frameformat *f){
FNAME();
TCHECK();
int ret = FALSE;
camcancel();
if(Toupcam_put_Roi(toupcam.hcam, (unsigned) f->xoff, (unsigned) f->yoff, (unsigned) f->w, (unsigned) f->h) >= 0){
geometry = *f;
ret = TRUE;
}
//startexp();
return ret;
}
static int Tglimits(frameformat *max, frameformat *step){
FNAME();
TCHECK();
if(max) *max = array;
if(step) *step = (frameformat){.w = 2, .h = 2, .xoff = 2, .yoff = 2};
return TRUE;
}
camera Toupcam = {
.disconnect = Tdisconnect,
.connect = Tconnect,
.capture = Tcapture,
.setbrightness = Tbright,
.setexp = Texp,
.setgain = Tgain,
.getmaxgain = Tmaxgain,
.setgeometry = Tgeometry,
.getgeomlimits = Tglimits,
};