diff --git a/LocCorr/CMakeLists.txt b/LocCorr/CMakeLists.txt index d978d0e..af8cd9b 100644 --- a/LocCorr/CMakeLists.txt +++ b/LocCorr/CMakeLists.txt @@ -8,22 +8,23 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}") project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C) # default flags -set(CMAKE_C_FLAGS "-O3 -std=gnu99 -march=native") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -std=gnu99 -march=native -fdata-sections -ffunction-sections") +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections") set(CMAKE_COLOR_MAKEFILE ON) # here is one of two variants: all .c in directory or .c files in list aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES) +set(CMAKE_VERBOSE_MAKEFILE "ON") + # cmake -DEBUG=1 -> debugging if(DEFINED EBUG AND EBUG EQUAL 1) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W") set(CMAKE_BUILD_TYPE DEBUG) - set(CMAKE_VERBOSE_MAKEFILE "ON") add_definitions(-DEBUG) else() set(CMAKE_BUILD_TYPE RELEASE) - set(CMAKE_VERBOSE_MAKEFILE "ON") endif() ###### pkgconfig ###### @@ -34,6 +35,7 @@ set(MODULES usefull_macros cfitsio) SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}) find_package(PkgConfig REQUIRED) find_package(FLYCAP REQUIRED) +find_package(BASLER REQUIRED) pkg_check_modules(${PROJ} REQUIRED ${MODULES}) include(FindOpenMP) @@ -60,9 +62,9 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}") # exe file add_executable(${PROJ} ${SOURCES}) # -I -include_directories(${${PROJ}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS}) +include_directories(${${PROJ}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS}) # -L -link_directories(${${PROJ}_LIBRARY_DIRS}) +link_directories(${${PROJ}_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS}) # -D add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" -DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\" @@ -70,7 +72,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\" -DMAJOR_VERSION=\"${MAJOR_VESION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT}) # -l -target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${FLYCAP_LIBRARIES} -lm) +target_link_libraries(${PROJ} ${${PROJ}_LIBRARIES} ${FLYCAP_LIBRARIES} ${BASLER_LIBRARIES} -lm) # Installation of the program INSTALL(TARGETS ${PROJ} DESTINATION "bin") diff --git a/LocCorr/FindBASLER.cmake b/LocCorr/FindBASLER.cmake new file mode 100644 index 0000000..5324401 --- /dev/null +++ b/LocCorr/FindBASLER.cmake @@ -0,0 +1,36 @@ +# - Try to find libpylonc.so +# Once done this will define +# +# BASLER_FOUND - system has lib +# BASLER_INCLUDE_DIR - include directory +# BASLER_LIBRARIES - Link these to use lib + +# Copyright (c) 2021, Edward V. Emelianov +# +# Redistribution and use is allowed according to the terms of the GPLv2/GPLv3. + +include(GNUInstallDirs) + +find_path(BASLER_INCLUDE_DIR pylonc/PylonC.h + PATHS /usr/pylon/include /opt/pylon/include /usr/local/pylon/include /usr/include /usr/local/include /opt/include /opt/local/include +) +find_path(BASLER_LIBRARY_DIR libpylonc.so + PATHS /usr/pylon/lib /opt/pylon/lib /usr/local/pylon/lib /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) +find_library(BASLER_LIBRARY NAMES pylonc + PATHS /opt/pylon/lib /usr/pylon/lib /usr/local/pylon/lib /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) + +find_package_handle_standard_args(BASLER DEFAULT_MSG BASLER_INCLUDE_DIR BASLER_LIBRARY BASLER_LIBRARY_DIR) + +if(BASLER_FOUND) + set(BASLER_INCLUDE_DIRS ${BASLER_INCLUDE_DIR}) + set(BASLER_LIBRARIES ${BASLER_LIBRARY}) + set(BASLER_LIBRARY_DIRS ${BASLER_LIBRARY_DIR}) +# message("BASLER include dir = ${BASLER_INCLUDE_DIRS}") +# message("BASLER lib = ${BASLER_LIBRARIES}") +# message("BASLER libdir = ${BASLER_LIBRARY_DIRS}") + mark_as_advanced(BASLER_INCLUDE_DIRS BASLER_LIBRARIES BASLER_LIBRARY_DIRS) +else() + message("BASLER not found") +endif(BASLER_FOUND) diff --git a/LocCorr/FindFLYCAP.cmake b/LocCorr/FindFLYCAP.cmake index 6eafdc1..db436f9 100644 --- a/LocCorr/FindFLYCAP.cmake +++ b/LocCorr/FindFLYCAP.cmake @@ -15,9 +15,9 @@ find_path(FLYCAP_INCLUDE_DIR FlyCapture2.h PATH_SUFFIXES libflycapture flycapture PATHS /usr/include /usr/local/include /opt/include /opt/local/include ) -#find_path(FLYCAP_LIBRARY_DIR libflycapture.so -# PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 -#) +find_path(FLYCAP_LIBRARY_DIR libflycapture.so + PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 +) find_library(FLYCAP_LIBRARY NAMES flycapture PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 ) @@ -25,12 +25,12 @@ find_library(FLYCAP_LIBRARYC NAMES flycapture-c PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64 ) -find_package_handle_standard_args(FLYCAP DEFAULT_MSG FLYCAP_INCLUDE_DIR FLYCAP_LIBRARY FLYCAP_LIBRARYC) +find_package_handle_standard_args(FLYCAP DEFAULT_MSG FLYCAP_INCLUDE_DIR FLYCAP_LIBRARY FLYCAP_LIBRARYC FLYCAP_LIBRARY_DIR) if(FLYCAP_FOUND) set(FLYCAP_INCLUDE_DIRS ${FLYCAP_INCLUDE_DIR}) set(FLYCAP_LIBRARIES ${FLYCAP_LIBRARY} ${FLYCAP_LIBRARYC}) -## set(FLYCAP_LIBRARY_DIRS ${FLYCAP_LIBRARY_DIR}) + set(FLYCAP_LIBRARY_DIRS ${FLYCAP_LIBRARY_DIR}) # message("FLYCAP include dir = ${FLYCAP_INCLUDE_DIRS}") # message("FLYCAP lib = ${FLYCAP_LIBRARIES}") # message("FLYCAP libdir = ${FLYCAP_LIBRARY_DIRS}") diff --git a/LocCorr/basler.c b/LocCorr/basler.c new file mode 100644 index 0000000..e47369f --- /dev/null +++ b/LocCorr/basler.c @@ -0,0 +1,334 @@ +/* + * This file is part of the loccorr project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include + +#include "basler.h" +#include "imagefile.h" + +static PYLON_DEVICE_HANDLE hDev; +static int isopened = FALSE; +static size_t payloadsize = 0; // size of imgBuf +static unsigned char *imgBuf = NULL; +static float expostime = 0.; // current exposition time +static PYLON_DEVICECALLBACK_HANDLE hCb; + +typedef struct{ + int64_t min; + int64_t max; + int64_t incr; + int64_t val; +} int64_values; + +typedef struct{ + double min; + double max; + double val; +} float_values; + +static char* describeError(GENAPIC_RESULT reserr){ + static char buf[1024]; + char* errMsg, *bptr = buf; + size_t length, l = 1023; + GenApiGetLastErrorMessage(NULL, &length); + errMsg = MALLOC(char, length); + GenApiGetLastErrorMessage(errMsg, &length); + size_t ll = snprintf(bptr, l, "%s (%d); ", errMsg, (int)reserr); + if(ll > 0){l -= ll; bptr += ll;} + FREE(errMsg); + GenApiGetLastErrorDetail(NULL, &length); + errMsg = MALLOC(char, length); + GenApiGetLastErrorDetail(errMsg, &length); + snprintf(bptr, l, "%s", errMsg); + FREE(errMsg); + return buf; +} + +#define PYLONFN(fn, ...) do{register GENAPIC_RESULT reserr; if(GENAPI_E_OK != (reserr=fn(__VA_ARGS__))){ \ + WARNX(#fn "(): %s", describeError(reserr)); return FALSE;}}while(0) + +static void disconnect(){ + FNAME(); + if(!isopened) return; + FREE(imgBuf); + PylonDeviceDeregisterRemovalCallback(hDev, hCb); + PylonDeviceClose(hDev); + PylonDestroyDevice(hDev); + PylonTerminate(); + isopened = FALSE; +} + +// get node & check it for read/write +/** + * @brief chkNode - get node & check it for read/write + * @param phNode (io) - pointer to node + * @param nodeType - type of node + * @param wr - ==TRUE if need to check for writeable + * @return TRUE if node found & checked + */ +static int chkNode(NODE_HANDLE *phNode, const char *featureName, EGenApiNodeType nodeType, int wr){ + if(!isopened || !phNode || !featureName) return FALSE; + NODEMAP_HANDLE hNodeMap; + EGenApiNodeType nt; + _Bool bv; + PYLONFN(PylonDeviceGetNodeMap, hDev, &hNodeMap); + PYLONFN(GenApiNodeMapGetNode, hNodeMap, featureName, phNode); + if(*phNode == GENAPIC_INVALID_HANDLE) return FALSE; + PYLONFN(GenApiNodeGetType, *phNode, &nt); + if(nodeType != nt) return FALSE; + PYLONFN(GenApiNodeIsReadable, *phNode, &bv); + if(!bv) return FALSE; // not readable + if(wr){ + PYLONFN(GenApiNodeIsWritable, *phNode, &bv); + if(!bv) return FALSE; // not writeable + } + return TRUE; +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +// getters of different types of data +static int getBoolean(const char *featureName, _Bool *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, BooleanNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiBooleanGetValue, hNode, val); + DBG("Get boolean: %s = %s", featureName, val ? "true" : "false"); + return TRUE; +} +static int getInt(char *featureName, int64_values *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, IntegerNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiIntegerGetMin, hNode, &val->min); + PYLONFN(GenApiIntegerGetMax, hNode, &val->max); + PYLONFN(GenApiIntegerGetInc, hNode, &val->incr); + PYLONFN(GenApiIntegerGetValue, hNode, &val->val); + DBG("Get integer %s = %ld: min = %ld, max = %ld, incr = %ld", featureName, val->val, val->min, val->max, val->incr); + return TRUE; +} +static int getFloat(char *featureName, float_values *val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, FloatNode, FALSE)) return FALSE; + if(!val) return TRUE; + PYLONFN(GenApiFloatGetMin, hNode, &val->min); + PYLONFN(GenApiFloatGetMax, hNode, &val->max); + PYLONFN(GenApiFloatGetValue, hNode, &val->val); + DBG("Get float %s = %g: min = %g, max = %g", featureName, val->val, val->min, val->max); + return TRUE; +} + +// setters of different types of data +static int setBoolean(char *featureName, _Bool val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, BooleanNode, TRUE)) return FALSE; + PYLONFN(GenApiBooleanSetValue, hNode, val); +} +static int setInt(char *featureName, int64_t val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, IntegerNode, TRUE)) return FALSE; + PYLONFN(GenApiIntegerSetValue, hNode, val); + return TRUE; +} +static int setFloat(char *featureName, float val){ + if(!isopened || !featureName) return FALSE; + NODE_HANDLE hNode; + if(!chkNode(&hNode, featureName, FloatNode, TRUE)) return FALSE; + PYLONFN(GenApiFloatSetValue, hNode, val); + return TRUE; +} +#pragma GCC diagnostic pop + +static void disableauto(){ + if(!isopened) return; + const char *features[] = {"EnumEntry_TriggerSelector_AcquisitionStart", + "EnumEntry_TriggerSelector_FrameBurstStart", + "EnumEntry_TriggerSelector_FrameStart"}; + const char *triggers[] = {"AcquisitionStart", "FrameBurstStart", "FrameStart"}; + for(int i = 0; i < 3; ++i){ + if(PylonDeviceFeatureIsAvailable(hDev, features[i])){ + PylonDeviceFeatureFromString(hDev, "TriggerSelector", triggers[i]); + PylonDeviceFeatureFromString(hDev, "TriggerMode", "Off"); + } + } + PylonDeviceFeatureFromString(hDev, "GainAuto", "Off"); + PylonDeviceFeatureFromString(hDev, "ExposureAuto", "Off"); + PylonDeviceFeatureFromString(hDev, "ExposureMode", "Timed"); + PylonDeviceFeatureFromString(hDev, "SequencerMode", "Off"); +} + +static void GENAPIC_CC removalCallbackFunction(_U_ PYLON_DEVICE_HANDLE hDevice){ + disconnect(); +} + +static int connect(){ + FNAME(); + size_t numDevices; + disconnect(); + PylonInitialize(); + PYLONFN(PylonEnumerateDevices, &numDevices); + if(!numDevices){ + WARNX("No cameras found"); + return FALSE; + } + PYLONFN(PylonCreateDeviceByIndex, 0, &hDev); + isopened = TRUE; + PYLONFN(PylonDeviceOpen, hDev, PYLONC_ACCESS_MODE_CONTROL | PYLONC_ACCESS_MODE_STREAM | PYLONC_ACCESS_MODE_EXCLUSIVE); + disableauto(); + PYLONFN(PylonDeviceFeatureFromString, hDev, "PixelFormat", "Mono8"); + PYLONFN(PylonDeviceFeatureFromString, hDev, "CameraOperationMode", "LongExposure"); + PYLONFN(PylonDeviceFeatureFromString, hDev, "UserSetSelector", "HighGain"); // set high gain selector + PYLONFN(PylonDeviceFeatureFromString, hDev, "AcquisitionMode", "SingleFrame"); + PYLONFN(PylonDeviceExecuteCommandFeature, hDev, "UserSetLoad"); // load high gain mode + PYLON_STREAMGRABBER_HANDLE hGrabber; + PYLONFN(PylonDeviceGetStreamGrabber, hDev, 0, &hGrabber); + PYLONFN(PylonStreamGrabberOpen, hGrabber); +// PYLON_WAITOBJECT_HANDLE hWaitStream; +// PYLONFN(PylonStreamGrabberGetWaitObject, hStreamGrabber, &hWaitStream); + PYLONFN(PylonStreamGrabberGetPayloadSize, hDev, hGrabber, &payloadsize); + PylonStreamGrabberClose(hGrabber); + PylonDeviceRegisterRemovalCallback(hDev, removalCallbackFunction, &hCb); + imgBuf = MALLOC(unsigned char, payloadsize); + //PYLONFN(PylonDeviceExecuteCommandFeature, hDev, "AcquisitionStart"); + return TRUE; +} + +static Image *capture(){ + FNAME(); + static int toohot = FALSE; + if(!isopened || !imgBuf) return NULL; + float_values f; + if(!getFloat("DeviceTemperature", &f)) WARNX("Can't get temperature"); + else{ + DBG("Temperature: %.1f", f.val); + if(f.val > 80.){ + WARNX("Device too hot"); + if(!toohot){ + LOGWARN("Device too hot"); + toohot = TRUE; + } + }else if(toohot && f.val < 75.){ + LOGMSG("Device temperature is normal"); + toohot = FALSE; + } + } + PylonGrabResult_t grabResult; + _Bool bufferReady; + GENAPIC_RESULT res = PylonDeviceGrabSingleFrame(hDev, 0, imgBuf, payloadsize, + &grabResult, &bufferReady, 500 + (uint32_t)expostime); + if(res != GENAPI_E_OK || !bufferReady){ + WARNX("res != GENAPI_E_OK || !bufferReady"); + return NULL; + } + if(grabResult.Status != Grabbed){ + WARNX("grabResult.Status != Grabbed"); + return NULL; + } + Image *oIma = u8toImage(imgBuf, grabResult.SizeX, grabResult.SizeY, grabResult.SizeX + grabResult.PaddingX); + return oIma; +} + +// Basler have no "brightness" parameter +static int setbrightness(_U_ float b){ + FNAME(); + return TRUE; +} + +static int setexp(float e){ + FNAME(); + if(!isopened) return FALSE; + e *= 1000.; + if(!setFloat("ExposureTime", e)){ + LOGWARN("Can't set expose time %g", e); + WARNX("Can't set expose time %g", e); + return FALSE; + } + float_values f; + if(!getFloat("ExposureTime", &f)) return FALSE; + expostime = (float)f.val / 1000.; + return TRUE; +} + +static int setgain(_U_ float e){ + FNAME(); + if(!isopened) return FALSE; + if(!setFloat("Gain", e)){ + LOGWARN("Can't set gain %g", e); + WARNX("Can't set gain %g", e); + return FALSE; + } + return TRUE; +} + +static int changeformat(frameformat *fmt){ + FNAME(); + if(!isopened) return FALSE; + if(!setInt("Width", fmt->w)) return FALSE; + if(!setInt("Height", fmt->h)) return FALSE; + if(!setInt("OffsetX", fmt->xoff)) return FALSE; + if(!setInt("OffsetY", fmt->yoff)) return FALSE; + int64_values i; + if(getInt("Width", &i)) fmt->w = i.val; + if(getInt("Height", &i)) fmt->h = i.val; + if(getInt("OffsetX", &i)) fmt->xoff = i.val; + if(getInt("OffsetY", &i)) fmt->yoff = i.val; + return TRUE; +} + +static int geometrylimits(frameformat *max, frameformat *step){ + FNAME(); + if(!isopened || !max || !step) return FALSE; + int64_values i; + if(!getInt("Width", &i)) return FALSE; + max->w = i.max; step->w = i.incr; + if(!getInt("Height", &i)) return FALSE; + max->h = i.max; step->h = i.incr; + if(!getInt("OffsetX", &i)) return FALSE; + max->xoff = i.max; step->xoff = i.incr; + if(!getInt("OffsetY", &i)) return FALSE; + max->yoff = i.max; step->yoff = i.incr; + return TRUE; +} + +static float gainmax(){ + FNAME(); + float_values v; + if(!getFloat("Gain", &v)) return 0.; + return (float)v.max; +} + +// exported object +camera Basler = { + .disconnect = disconnect, + .connect = connect, + .capture = capture, + .setbrightness = setbrightness, + .setexp = setexp, + .setgain = setgain, + .setgeometry = changeformat, + .getgeomlimits = geometrylimits, + .getmaxgain = gainmax, +}; + diff --git a/LocCorr/basler.h b/LocCorr/basler.h new file mode 100644 index 0000000..42e2f4d --- /dev/null +++ b/LocCorr/basler.h @@ -0,0 +1,27 @@ +/* + * This file is part of the loccorr project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ +#pragma once +#ifndef BASLER_H__ +#define BASLER_H__ + +#include "cameracapture.h" // `camera` +#define BASLER_CAPT_NAME "basler" + +extern camera Basler; + +#endif // BASLER_H__ diff --git a/LocCorr/cameracapture.c b/LocCorr/cameracapture.c new file mode 100644 index 0000000..9159118 --- /dev/null +++ b/LocCorr/cameracapture.c @@ -0,0 +1,261 @@ +/* + * This file is part of the loccorr project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#include // FLT_EPSILON +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "config.h" +#include "fits.h" +#include "grasshopper.h" +#include "imagefile.h" +#include "improc.h" + +// pointer to selected camera +static camera *theCam = NULL; + +static float gain = 0., gainmax = 0.; +static float exptime = 100.; +static float brightness = 0.; +static int connected = FALSE; + +static frameformat curformat; +static frameformat maxformat; +static frameformat stepformat; + +static void changeformat(){ + if(!theCam) return; + if(maxformat.h < 1 || maxformat.w < 1){ + WARNX("Bad max format data"); + LOGWARN("Bad max format data"); + return; + } + if(stepformat.h < 1 || stepformat.w < 1){ + WARNX("Bad step format data"); + LOGWARN("Bad step format data"); + return; + } + if(stepformat.xoff < 1) stepformat.xoff = 1; + if(stepformat.yoff < 1) stepformat.yoff = 1; + curformat.h = (theconf.height < maxformat.h) ? theconf.height : maxformat.h; + curformat.h -= curformat.h % stepformat.h; + curformat.w = (theconf.width < maxformat.w) ? theconf.width : maxformat.w; + curformat.w -= curformat.w % stepformat.w; + curformat.xoff = (theconf.xoff + curformat.w <= maxformat.w) ? theconf.xoff : maxformat.w - curformat.w; + curformat.xoff -= curformat.xoff % stepformat.xoff; + curformat.yoff = (theconf.yoff + curformat.h <= maxformat.h) ? theconf.yoff : maxformat.h - curformat.h; + curformat.yoff -= curformat.yoff % stepformat.yoff; + if(theCam->setgeometry(&curformat)){ // now we can change config values to real + theconf.height = curformat.h; + theconf.width = curformat.w; + theconf.xoff = curformat.xoff; + theconf.yoff = curformat.yoff; + } +} + +/** + * @brief setCamera - set active camera & initialize it + * @param cptr - pointer to new camera + * @return FALSE if failed + */ +int setCamera(camera *cptr){ + camdisconnect(); + theCam = cptr; + connected = theCam->connect(); + if(!connected) return FALSE; + gainmax = theCam->getmaxgain(); + gain = theconf.gain; + brightness = theconf.brightness; + if(!theCam->getgeomlimits(&maxformat, &stepformat)){ + WARNX("Can't detect camera format limits"); + LOGWARN("Can't detect camera format limits"); + return TRUE; + } + changeformat(); + LOGMSG("Camera connected, max gain: %.1f, max (W,H): (%d,%d)", gainmax, maxformat.w, maxformat.h); + return TRUE; +} + +void camdisconnect(){ + if(!connected) return; + connected = FALSE; + if(theCam) theCam->disconnect(); +} + +static void calcexpgain(float newexp){ + DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, newexp); + if(newexp*1.25 > theconf.minexp){ + if(gain < gainmax - 1.){ // increase gain first + gain += 1.; + newexp /= 1.25; + } + }else{ // make gain lower + if(1.25*newexp < theconf.maxexp && gain > 1.){ + gain -= 1.; + newexp *= 1.25; + } + } + if(newexp < theconf.minexp) newexp = theconf.minexp; + else if(newexp > theconf.maxexp) newexp = theconf.maxexp; + exptime = newexp; + DBG("New values: exp=%g, gain=%g", exptime, gain); +} + +//convertedImage.pData, convertedImage.cols, convertedImage.rows, convertedImage.stride +static void recalcexp(Image *I){ + // check if user changed exposition values + if(exptime < theconf.minexp){ + exptime = theconf.minexp; + return; + } + else if(exptime > theconf.maxexp){ + exptime = theconf.maxexp; + return; + } + if(I->minval < 0. || I->maxval > 255.1){ + DBG("Bad image data: min=%g, max=%g", I->minval, I->maxval); + return; + } + int wh = I->width * I->height; + int histogram[256] = {0}; + // algorythm works only with 8bit images! + #pragma omp parallel + { + int histogram_private[256] = {0}; + #pragma omp for nowait + for(int i = 0; i < wh; ++i){ + ++histogram_private[(int)I->data[i]]; + } + #pragma omp critical + { + for(int i=0; i<256; ++i) histogram[i] += histogram_private[i]; + } + } + int idx100, sum100 = 0; + for(idx100 = 255; idx100 >= 0; --idx100){ + sum100 += histogram[idx100]; + if(sum100 > 100) break; + } + DBG("Sum100=%d, idx100=%d", sum100, idx100); + if(idx100 > 200 && idx100 < 250) return; // good values + if(idx100 > 250){ // exposure too long + calcexpgain(0.7*exptime); + }else{ // exposure too short + if(idx100 > 5) + calcexpgain(exptime * 210. / (float)idx100); + else + calcexpgain(exptime * 50.); + } +} + +int camcapture(void (*process)(Image*)){ + FNAME(); + static float oldexptime = 0.; + static float oldgain = -1.; + static float oldbrightness = -1.; + Image *oIma = NULL; + while(1){ + if(stopwork){ + DBG("STOP"); + break; + } + if(!theCam){ // wait until camera be powered on + LOGERR("camcapture(): camera not initialized"); + ERRX("Not initialized"); + } + if(!connected){ + DBG("Disconnected"); + connected = theCam->connect(); + sleep(1); + continue; + } + if(fabsf(oldbrightness - brightness) > FLT_EPSILON){ // new brightness + DBG("Change brightness to %g", brightness); + if(theCam->setbrightness(brightness)){ + oldbrightness = brightness; + }else{ + WARNX("Can't change brightness to %g", brightness); + } + } + if(exptime > theconf.maxexp) exptime = theconf.maxexp; + else if(exptime < theconf.minexp) exptime = theconf.minexp; + if(fabsf(oldexptime - exptime) > FLT_EPSILON){ // new exsposition value + DBG("Change exptime to %.2fms\n", exptime); + if(theCam->setexp(exptime)){ + oldexptime = exptime; + }else{ + WARNX("Can't change exposition time to %gms", exptime); + } + } + if(gain > gainmax) gain = gainmax; + if(fabsf(oldgain - gain) > FLT_EPSILON){ // change gain + DBG("Change gain to %g\n", gain); + if(theCam->setgain(gain)){ + oldgain = gain; + }else{ + WARNX("Can't change gain to %g", gain); + } + } + // change format + if(abs(curformat.h - theconf.height) || abs(curformat.w - theconf.width) || abs(curformat.xoff - theconf.xoff) || abs(curformat.yoff - theconf.yoff)){ + changeformat(); + } + if(!(oIma = theCam->capture())){ + WARNX("Can't grab image"); + camdisconnect(); + continue; + } + if(theconf.expmethod == EXPAUTO) recalcexp(oIma); + else{ + if(fabs(theconf.fixedexp - exptime) > FLT_EPSILON) + exptime = theconf.fixedexp; + if(fabs(theconf.gain - gain) > FLT_EPSILON) + gain = theconf.gain; + if(fabs(theconf.brightness - brightness) > FLT_EPSILON) + brightness = theconf.brightness; + } + if(process){ + process(oIma); + } + FREE(oIma->data); + FREE(oIma); + } + camdisconnect(); + DBG("CAMCAPTURE: out"); + return 1; +} + +// return JSON with image status +char *camstatus(const char *messageid, char *buf, int buflen){ + static char *impath = NULL; + if(!impath){ + if(!(impath = realpath(GP->outputjpg, impath))){ + WARN("realpath() (%s)", impath); + impath = strdup(GP->outputjpg); + } + DBG("path: %s", impath); + } + snprintf(buf, buflen, "{ \"%s\": \"%s\", \"camstatus\": \"%sconnected\", \"impath\": \"%s\", \"imctr\": %llu, " + "\"fps\": %.3f, \"expmethod\": \"%s\", \"exposition\": %g, \"gain\": %g, \"brightness\": %g }\n", + MESSAGEID, messageid, connected ? "" : "dis", impath, ImNumber, getFramesPerS(), + (theconf.expmethod == EXPAUTO) ? "auto" : "manual", exptime, gain, brightness); + return buf; +} diff --git a/LocCorr/cameracapture.h b/LocCorr/cameracapture.h new file mode 100644 index 0000000..f58cd37 --- /dev/null +++ b/LocCorr/cameracapture.h @@ -0,0 +1,52 @@ +/* + * This file is part of the loccorr project. + * Copyright 2021 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once +#ifndef CAMERACAPTURE_H__ +#define CAMERACAPTURE_H__ +#include "fits.h" // Image* + +// format of single frame +typedef struct{ + int w; int h; // width & height + int xoff; int yoff; // X and Y offset +} frameformat; + +typedef struct{ + void (*disconnect)(); // disconnect & cleanup + int (*connect)(); // connect & init + Image* (*capture)(); // capture an image + // setters: brightness, exptime, gain + int (*setbrightness)(float b); + int (*setexp)(float e); + int (*setgain)(float g); + float (*getmaxgain)(); // get max available gain value + // geometry (if TRUE, all args are changed to suitable values) + int (*setgeometry)(frameformat *fmt); + // get limits of geometry: maximal values and steps + int (*getgeomlimits)(frameformat *max, frameformat *step); + //int (*getgeometry)(frameformat *fmt); +} camera; + +int setCamera(camera *cptr); +void camdisconnect(); +int camcapture(void (*process)(Image *)); +char *camstatus(const char *messageid, char *buf, int buflen); + + +#endif // CAMERACAPTURE_H__ diff --git a/LocCorr/config.c b/LocCorr/config.c index 4590b46..be2350d 100644 --- a/LocCorr/config.c +++ b/LocCorr/config.c @@ -217,11 +217,11 @@ confparam *chk_keyval(const char *key, const char *val, key_value *result){ confparam *par = parvals; while(par->name){ if(strcmp(key, par->name) == 0){ - DBG("key='%s', par->name='%s'", key, par->name); + //DBG("key='%s', par->name='%s'", key, par->name); result->type = par->type; switch(par->type){ case PAR_INT: - DBG("INTEGER"); + //DBG("INTEGER"); if(!str2int(&result->val.intval, val)){ WARNX("Wrong integer value '%s' of parameter '%s'", val, key); return NULL; @@ -232,12 +232,12 @@ confparam *chk_keyval(const char *key, const char *val, key_value *result){ result->val.intval, par->name, par->minval, par->maxval); break; case PAR_DOUBLE: - DBG("DOUBLE"); + //DBG("DOUBLE"); if(!str2double(&result->val.dblval, val)){ WARNX("Wrong double value '%s' of parameter '%s'", val, key); return NULL; } - DBG("val: %g, min: %g, max: %g", result->val.dblval, par->minval, par->maxval); + //DBG("val: %g, min: %g, max: %g", result->val.dblval, par->minval, par->maxval); if(result->val.dblval > par->minval && result->val.dblval < par->maxval) return par; else WARNX("Value (%g) of parameter %s out of range %g..%g", @@ -300,7 +300,7 @@ int chkconfig(const char *confname){ int found = 0; par = parvals; while(par->name){ - DBG("parvals[]={%s, %d, %d(%g), %d}", par->name, par->type, *((int*)par->ptr), *((double*)par->ptr), par->got); + //DBG("parvals[]={%s, %d, %d(%g), %d}", par->name, par->type, *((int*)par->ptr), *((double*)par->ptr), par->got); int k = par->got; if(!k){ ++par; diff --git a/LocCorr/config.h b/LocCorr/config.h index 765a28e..3cc86d4 100644 --- a/LocCorr/config.h +++ b/LocCorr/config.h @@ -38,9 +38,9 @@ #define EXPOS_MIN (0.1) #define EXPOS_MAX (4001.) #define GAIN_MIN (0.) -#define GAIN_MAX (33.) +#define GAIN_MAX (100.) #define BRIGHT_MIN (0.) -#define BRIGHT_MAX (1000.) +#define BRIGHT_MAX (10.) // max average images counter #define NAVER_MAX (50) // coefficients to convert dx,dy to du,dv diff --git a/LocCorr/grasshopper.c b/LocCorr/grasshopper.c index c4b53ae..70dfdd3 100644 --- a/LocCorr/grasshopper.c +++ b/LocCorr/grasshopper.c @@ -24,22 +24,20 @@ #include #include -#include "cmdlnopts.h" #include "config.h" -#include "fits.h" #include "grasshopper.h" #include "imagefile.h" -#include "improc.h" static fc2Context context; static fc2PGRGuid guid; static fc2Error err = FC2_ERROR_OK; -static float gain = 20.; -static float exptime = 100.; -static float brightness = 0.; #define FC2FN(fn, ...) do{err = FC2_ERROR_OK; if(FC2_ERROR_OK != (err=fn(context __VA_OPT__(,) __VA_ARGS__))){ \ - WARNX(#fn "(): %s", fc2ErrorToDescription(err)); return 0;}}while(0) + WARNX(#fn "(): %s", fc2ErrorToDescription(err)); return FALSE;}}while(0) + +static void disconnect(){ + fc2DestroyContext(context); +} /** * @brief setfloat - set absolute property value (float) @@ -58,21 +56,21 @@ static int setfloat(fc2PropertyType t, float f){ if(prop.autoManualMode){ if(!i.manualSupported){ WARNX("Can't set auto-only property"); - return 0; + return FALSE; } prop.autoManualMode = false; } if(!prop.absControl){ if(!i.absValSupported){ WARNX("Can't set non-absolute property to absolute value"); - return 0; + return FALSE; } prop.absControl = true; } if(!prop.onOff){ if(!i.onOffSupported){ WARNX("Can't set property ON"); - return 0; + return FALSE; } prop.onOff = true; } @@ -84,12 +82,24 @@ static int setfloat(fc2PropertyType t, float f){ FC2FN(fc2GetProperty, &prop); if(fabsf(prop.absValue - f) > 0.02f){ WARNX("Can't set property! Got %g instead of %g.", prop.absValue, f); - return 0; + return FALSE; } - return 1; + return TRUE; } -int propOnOff(fc2PropertyType t, BOOL onOff){ +static int setbrightness(float b){ + return setfloat(FC2_BRIGHTNESS, b); +} +static int setexp(float e){ + return setfloat(FC2_SHUTTER, e); +} +static int setgain(float e){ + return setfloat(FC2_GAIN, e); +} +// TODO: +set saturation, hue, sharpness ? + + +static int propOnOff(fc2PropertyType t, BOOL onOff){ fc2Property prop = {0}; prop.type = t; fc2PropertyInfo i = {0}; @@ -118,56 +128,63 @@ int propOnOff(fc2PropertyType t, BOOL onOff){ #define trigModeOff() propOnOff(FC2_TRIGGER_MODE, false) #define trigDelayOff() propOnOff(FC2_TRIGGER_DELAY, false) #define frameRateOff() propOnOff(FC2_FRAME_RATE, false) -// +set: saturation, hue, sharpness -#define setbrightness(b) setfloat(FC2_BRIGHTNESS, b) -#define setexp(e) setfloat(FC2_SHUTTER, e) -#define setgain(g) setfloat(FC2_GAIN, g) -static int connected = 0; -void disconnectGrasshopper(){ - if(!connected) return; - connected = 0; - fc2DestroyContext(context); +static int geometrylimits(frameformat *max, frameformat *step){ + fc2Format7Info f = {.mode = FC2_MODE_0}; + BOOL b; + fc2Format7Info i = {0}; + FC2FN(fc2GetFormat7Info, &i, &b); + if(!b || !max || !step) return FALSE; + max->h = f.maxHeight; max->w = f.maxWidth; + max->xoff = f.maxWidth - f.offsetHStepSize; + max->yoff = f.maxHeight - f.offsetVStepSize; + step->w = f.imageHStepSize; + step->h = f.imageVStepSize; + step->xoff = f.offsetHStepSize; + step->yoff = f.offsetVStepSize; + return TRUE; } -static int changeformat(){ +static int getformat(frameformat *fmt){ + unsigned int packsz; float pc; + fc2Format7ImageSettings f7; + FC2FN(fc2GetFormat7Configuration, &f7, &packsz, &pc); + fmt->h = f7.height; fmt->w = f7.width; + fmt->xoff = f7.offsetX; fmt->yoff = f7.offsetY; + return TRUE; +} + +static int changeformat(frameformat *fmt){ FNAME(); BOOL b; - fc2Format7Info f7i = {.mode = FC2_MODE_0}; - FC2FN(fc2GetFormat7Info, &f7i, &b); - if(!b) return 0; fc2Format7ImageSettings f7; -/* unsigned int packSz; float perc; - FC2FN(fc2GetFormat7Configuration, &f7, &packSz, &perc); - DBG("packsz=%u, perc=%f, off=%d/%d, HW=%d/%d", packSz, perc, f7.offsetX, f7.offsetY, f7.height, f7.width); - */ f7.mode = FC2_MODE_0; - f7.offsetX = (theconf.xoff < (int)f7i.maxWidth && theconf.xoff > -1) ? theconf.xoff : 0; - f7.offsetY = (theconf.yoff < (int)f7i.maxHeight && theconf.yoff > -1) ? theconf.yoff : 0; - f7.width = (f7.offsetX+theconf.width <= f7i.maxWidth && theconf.width > 1) ? (unsigned int)theconf.width : f7i.maxWidth - f7.offsetX; - f7.height = (f7.offsetY+theconf.height <= f7i.maxHeight && theconf.height > 1) ? (unsigned int)theconf.height : f7i.maxHeight - f7.offsetY; + f7.offsetX = fmt->xoff; + f7.offsetY = fmt->yoff; + f7.width = fmt->w; + f7.height = fmt->h; DBG("offx=%d, offy=%d, w=%d, h=%d ", f7.offsetX, f7.offsetY, f7.width, f7.height); f7.pixelFormat = FC2_PIXEL_FORMAT_MONO8; fc2Format7PacketInfo f7p; FC2FN(fc2ValidateFormat7Settings, &f7, &b, &f7p); - if(!b) return 0; // invalid + if(!b) return FALSE; // invalid FC2FN(fc2SetFormat7Configuration, &f7, f7p.recommendedBytesPerPacket); - return 1; + getformat(fmt); // change values to currently used + return TRUE; } static int connect(){ - if(connected) return 1; FNAME(); unsigned int numCameras = 0; if(FC2_ERROR_OK != (err = fc2CreateContext(&context))){ WARNX("fc2CreateContext(): %s", fc2ErrorToDescription(err)); - return 0; + return FALSE; } FC2FN(fc2GetNumOfCameras, &numCameras); if(numCameras == 0){ WARNX("No cameras detected!"); - disconnectGrasshopper(); - return 0; + camdisconnect(); + return FALSE; } DBG("Found %d camera[s]", numCameras); if(numCameras > 1){ @@ -182,14 +199,12 @@ static int connect(){ trigModeOff(); trigDelayOff(); frameRateOff(); - if(!changeformat()) WARNX("Can't change camera format"); - connected = 1; - return 1; + return TRUE; } static int GrabImage(fc2Image *convertedImage){ //FNAME(); - int ret = 0; + int ret = FALSE; fc2Image rawImage; // start capture FC2FN(fc2StartCapture); @@ -210,166 +225,45 @@ static int GrabImage(fc2Image *convertedImage){ WARNX("Error in fc2ConvertImageTo: %s", fc2ErrorToDescription(err)); goto rtn; } - ret = 1; + ret = TRUE; rtn: fc2StopCapture(context); fc2DestroyImage(&rawImage); return ret; } -static void calcexpgain(float newexp){ - DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, newexp); - if(newexp < exptime){ // first we should make gain lower - if(10.*newexp < theconf.maxexp){ - if(gain > 10.){ - gain = 10.; - newexp *= 10.; - }else if(gain > 0.){ - gain = 0.; - newexp *= 10.; - } - } - }else{ // if new exposition too large, increase gain - if(newexp > theconf.maxexp){ - if(gain < 19.){ - gain += 10.; - newexp /= 10.; - } - } - } - if(newexp < theconf.minexp) newexp = theconf.minexp; - else if(newexp > theconf.maxexp) newexp = theconf.maxexp; - exptime = newexp; - DBG("New values: exp=%g, gain=%g", exptime, gain); -} - -//convertedImage.pData, convertedImage.cols, convertedImage.rows, convertedImage.stride -static void recalcexp(fc2Image *img){ - // check if user changed exposition values - if(exptime < theconf.minexp){ - exptime = theconf.minexp; - return; - } - else if(exptime > theconf.maxexp){ - exptime = theconf.maxexp; - return; - } - uint8_t *data = img->pData; - int W = img->cols, H = img->rows, S = img->stride; - int histogram[256] = {0}; - DBG("W=%d, H=%d, S=%d", W, H, S); - for(int y = 0; y < H; ++y){ - uint8_t *ptr = &data[y*S]; - for(int x = 0; x < W; ++x, ++ptr) - ++histogram[*ptr]; - } - int idx100, sum100 = 0; - for(idx100 = 255; idx100 >= 0; --idx100){ - sum100 += histogram[idx100]; - if(sum100 > 100) break; - } - DBG("Sum100=%d, idx100=%d", sum100, idx100); - if(idx100 > 200 && idx100 < 250) return; // good values - if(idx100 > 250){ // exposure too long - calcexpgain(0.7*exptime); - }else{ // exposure too short - if(idx100 > 5) - calcexpgain(exptime * 210. / (float)idx100); - else - calcexpgain(exptime * 50.); - } -} - -int capture_grasshopper(void (*process)(Image*)){ +// capture image, memory allocated HERE +static Image *capture(){ FNAME(); - static float oldexptime = 0.; - static float oldgain = -1.; - static float oldbrightness = -1.; Image *oIma = NULL; fc2Image convertedImage; err = fc2CreateImage(&convertedImage); if(err != FC2_ERROR_OK){ WARNX("capture_grasshopper(): can't create image, %s", fc2ErrorToDescription(err)); - disconnectGrasshopper(); - return 0; + return NULL; } - while(1){ - if(stopwork){ - DBG("STOP"); - break; - } - if(!connect()){ // wait until camera be powered on - DBG("Disconnected"); - sleep(1); - continue; - } - if(fabsf(oldbrightness - brightness) > FLT_EPSILON){ // new brightness - DBG("Change brightness to %g", brightness); - if(setbrightness(brightness)){ - oldbrightness = brightness; - }else{ - WARNX("Can't change brightness to %g", brightness); - } - } - if(fabsf(oldexptime - exptime) > FLT_EPSILON){ // new exsposition value - DBG("Change exptime to %.2fms\n", exptime); - if(setexp(exptime)){ - oldexptime = exptime; - }else{ - WARNX("Can't change exposition time to %gms", exptime); - } - } - if(fabsf(oldgain - gain) > FLT_EPSILON){ // change gain - DBG("Change gain to %g\n", gain); - if(setgain(gain)){ - oldgain = gain; - }else{ - WARNX("Can't change gain to %g", gain); - } - } - if(!GrabImage(&convertedImage)){ - WARNX("Can't grab image"); - disconnectGrasshopper(); - continue; - } - if(theconf.expmethod == EXPAUTO) recalcexp(&convertedImage); - else{ - if(fabs(theconf.fixedexp - exptime) > FLT_EPSILON) - exptime = theconf.fixedexp; - if(fabs(theconf.gain - gain) > FLT_EPSILON) - gain = theconf.gain; - if(fabs(theconf.brightness - brightness) > FLT_EPSILON) - brightness = theconf.brightness; - } - if(!process){ - continue; - } - oIma = u8toImage(convertedImage.pData, convertedImage.cols, convertedImage.rows, convertedImage.stride); - if(oIma){ - process(oIma); - FREE(oIma->data); - FREE(oIma); - } + if(!GrabImage(&convertedImage)){ + WARNX("Can't grab image"); + return NULL; } + oIma = u8toImage(convertedImage.pData, convertedImage.cols, convertedImage.rows, convertedImage.stride); fc2DestroyImage(&convertedImage); - disconnectGrasshopper(); - DBG("GRASSHOPPER: out"); - return 1; + return oIma; } -// return JSON with image status -char *gsimagestatus(const char *messageid, char *buf, int buflen){ - static char *impath = NULL; - if(!impath){ - if(!(impath = realpath(GP->outputjpg, impath))){ - WARN("realpath() (%s)", impath); - impath = strdup(GP->outputjpg); - } - DBG("path: %s", impath); - } - snprintf(buf, buflen, "{ \"%s\": \"%s\", \"camstatus\": \"%sconnected\", \"impath\": \"%s\", \"imctr\": %llu, " - "\"fps\": %.3f, \"expmethod\": \"%s\", \"exposition\": %g, \"gain\": %g, \"brightness\": %g }\n", - MESSAGEID, messageid, connected ? "" : "dis", impath, ImNumber, getFramesPerS(), - (theconf.expmethod == EXPAUTO) ? "auto" : "manual", exptime, gain, brightness); - return buf; +static float maxgain(){ + return GAIN_MAX; } + +// exported object +camera GrassHopper = { + .disconnect = disconnect, + .connect = connect, + .capture = capture, + .setbrightness = setbrightness, + .setexp = setexp, + .setgain = setgain, + .setgeometry = changeformat, + .getgeomlimits = geometrylimits, + .getmaxgain = maxgain, +}; diff --git a/LocCorr/grasshopper.h b/LocCorr/grasshopper.h index 5f7386b..30edfb4 100644 --- a/LocCorr/grasshopper.h +++ b/LocCorr/grasshopper.h @@ -19,12 +19,10 @@ #ifndef GRASSHOPPER_H__ #define GRASSHOPPER_H__ -#include "fits.h" // Image* +#include "cameracapture.h" // `camera` #define GRASSHOPPER_CAPT_NAME "grasshopper" -void disconnectGrasshopper(); -int capture_grasshopper(void (*process)(Image *)); -char *gsimagestatus(const char *messageid, char *buf, int buflen); +extern camera GrassHopper; #endif // GRASSHOPPER_H__ diff --git a/LocCorr/imagefile.c b/LocCorr/imagefile.c index 8825ce6..4579925 100644 --- a/LocCorr/imagefile.c +++ b/LocCorr/imagefile.c @@ -27,6 +27,8 @@ #include #include +#include "basler.h" +#include "cameracapture.h" #include "cmdlnopts.h" #include "config.h" #include "draw.h" @@ -96,6 +98,7 @@ static InputType imtype(FILE *f){ InputType chkinput(const char *name){ DBG("input name: %s", name); if(0 == strcmp(name, GRASSHOPPER_CAPT_NAME)) return T_CAPT_GRASSHOPPER; + else if(0 == strcmp(name, BASLER_CAPT_NAME)) return T_CAPT_BASLER; struct stat fd_stat; stat(name, &fd_stat); if(S_ISDIR(fd_stat.st_mode)){ @@ -120,6 +123,7 @@ InputType chkinput(const char *name){ } Image *u8toImage(uint8_t *data, int width, int height, int stride){ + FNAME(); Image *outp = MALLOC(Image, 1); outp->width = width; outp->height = height; diff --git a/LocCorr/imagefile.h b/LocCorr/imagefile.h index 07c3696..3670cba 100644 --- a/LocCorr/imagefile.h +++ b/LocCorr/imagefile.h @@ -33,7 +33,8 @@ typedef enum{ T_GIF, T_JPEG, T_PNG, - T_CAPT_GRASSHOPPER // capture grasshopper + T_CAPT_GRASSHOPPER, // capture grasshopper + T_CAPT_BASLER } InputType; void Image_minmax(Image *I); diff --git a/LocCorr/improc.c b/LocCorr/improc.c index ac6b68d..bd057fa 100644 --- a/LocCorr/improc.c +++ b/LocCorr/improc.c @@ -23,7 +23,9 @@ #include #include +#include "basler.h" #include "binmorph.h" +#include "cameracapture.h" #include "cmdlnopts.h" #include "config.h" #include "draw.h" @@ -163,6 +165,7 @@ process_corrections: } void process_file(Image *I){ + FNAME(); static double lastTproc = 0.; /* #ifdef EBUG @@ -187,7 +190,7 @@ void process_file(Image *I){ //DELTA("Save original"); Imtype bk; if(calc_background(I, &bk)){ - //DBG("backgr = %g", bk); + DBG("backgr = %g", bk); DELTA("Got background"); uint8_t *ibin = Im2bin(I, bk); DELTA("Made binary"); @@ -290,40 +293,17 @@ void process_file(Image *I){ } } FREE(tmpnm); - ++ImNumber; - if(lastTproc > 1.) FPS = 1. / (dtime() - lastTproc); - lastTproc = dtime(); FREE(outp); } FREE(Objects); - /* - Image *c = ST2Im(S, W, H); - DELTA("conv size_t -> Ima"); - save_fits(c, "size_t.fits"); - Image_free(&c); - DELTA("Save size_t"); - */ - /* - Image *obj = Image_sim(I); - OMP_FOR() - for(int y = 0; y < H; ++y){ - size_t idx = y*W; - Imtype *optr = &obj->data[idx]; - Imtype *iptr = &I->data[idx]; - size_t *mask = &S[idx]; - for(int x = 0; x < W; ++x, ++mask, ++iptr, ++optr){ - if(*mask) *optr = *iptr - bk; - } - } - Image_minmax(obj); - save_fits(obj, "object.fits"); - Image_free(&obj); - */ }else Image_write_jpg(I, GP->outputjpg, theconf.equalize); FREE(S); FREE(cc); } - } + }else Image_write_jpg(I, GP->outputjpg, theconf.equalize); + ++ImNumber; + if(lastTproc > 1.) FPS = 1. / (dtime() - lastTproc); + lastTproc = dtime(); DELTA("End"); } @@ -344,13 +324,19 @@ static char *watchfl(const char *messageid, char *buf, int buflen){ } int process_input(InputType tp, char *name){ - //DBG("process_input(%d, %s)", tp, name); + DBG("process_input(%d, %s)", tp, name); if(tp == T_DIRECTORY){ imagedata = watchdr; return watch_directory(name, process_file); - }else if(tp == T_CAPT_GRASSHOPPER){ - imagedata = gsimagestatus; - return capture_grasshopper(process_file); + }else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER){ + camera *cam = &GrassHopper; + if(tp == T_CAPT_BASLER) cam = &Basler; + if(!setCamera(cam)){ + WARNX("The camera disconnected"); + LOGWARN("The camera disconnected"); + } + imagedata = camstatus; + return camcapture(process_file); } imagedata = watchfl; return watch_file(name, process_file); diff --git a/LocCorr/main.c b/LocCorr/main.c index 9ea8e50..83d1935 100644 --- a/LocCorr/main.c +++ b/LocCorr/main.c @@ -58,7 +58,7 @@ void iffound_default(pid_t pid){ ERRX("Another copy of this process found, pid=%d. Exit.", pid); } -void *procinp_thread(_U_ void* arg){ +static void *procinp_thread(_U_ void* arg){ int p = process_input(tp, GP->inputname); LOGDBG("process_input=%d", p); return NULL; @@ -94,6 +94,9 @@ static InputType chk_inp(const char *name){ case T_CAPT_GRASSHOPPER: printf("capture grasshopper camera"); break; + case T_CAPT_BASLER: + printf("capture basler camera"); + break; default: printf("Unsupported type\n"); return T_WRONG; diff --git a/LocCorr/median.c b/LocCorr/median.c index 1dbf11e..be63702 100644 --- a/LocCorr/median.c +++ b/LocCorr/median.c @@ -444,9 +444,10 @@ int get_stat(const Image *in, int seed, Image **mean, Image **std){ * @return 0 if error */ int calc_background(Image *img, Imtype *bk){ + //DBG("image: min=%g, max=%g", img->minval, img->maxval); if(img->maxval - img->minval < DBL_EPSILON){ WARNX("Zero image!"); - return 0; + return FALSE; } int w = img->width, h = img->height, wh = w*h; Imtype min = img->minval, ampl = img->maxval - min; @@ -472,24 +473,13 @@ int calc_background(Image *img, Imtype *bk){ modeidx = i; } //DBG("Mode=%g @ idx%d (N=%d)", ((Imtype)modeidx / 255.)*ampl, modeidx, modeval); -/* - int diff[256] = {0}; - for(int i = 1; i < 255; ++i) diff[i] = (histogram[i+1]-histogram[i-1])/2; - if(modeidx == 0) modeidx = 1; - if(modeidx > 253) return NULL; // very bad image: overilluminated - int borderidx = modeidx; - green("1\n"); - for(int i = modeidx; i < 255; ++i){ // search bend-point by first derivate - printf("%d: %d, %d\n", i, diff[i], diff[i+1]); - if(diff[i] >= 0 && diff[i+1] >=0){ - borderidx = i; break; - } - } -*/ int diff2[256] = {0}; for(int i = 2; i < 254; ++i) diff2[i] = (histogram[i+2]+histogram[i-2]-2*histogram[i])/4; if(modeidx < 2) modeidx = 2; - if(modeidx > 253) return 0; // very bad image: overilluminated + if(modeidx > 253){ + WARNX("Overilluminated image"); + return FALSE; // very bad image: overilluminated + } int borderidx = modeidx; // green("2\n"); for(int i = modeidx; i < 254; ++i){ // search bend-point by second derivate @@ -502,8 +492,8 @@ int calc_background(Image *img, Imtype *bk){ //borderidx = (borderidx + modeidx) / 2; Imtype borderval = ((Imtype)borderidx / 255.)*ampl + min; if(bk) *bk = borderval; - //green("HISTO:\n"); - //for(int i = 0; i < 256; ++i) printf("%d:\t%d\t%d\n", i, histogram[i], diff2[i]); +// green("HISTO:\n"); +// for(int i = 0; i < 256; ++i) printf("%d:\t%d\t%d\n", i, histogram[i], diff2[i]); // calculate values of upper 2% border #if 0 Image *out = Image_sim(img); @@ -515,5 +505,5 @@ int calc_background(Image *img, Imtype *bk){ } Image_minmax(out); #endif - return 1; + return TRUE; }