mirror of
https://github.com/eddyem/astrovideoguide_v3.git
synced 2025-12-06 10:45:10 +03:00
add Basler CMOS support
This commit is contained in:
parent
40f437b6a0
commit
9d76c96602
@ -8,22 +8,23 @@ set(VERSION "${MAJOR_VERSION}.${MID_VERSION}.${MINOR_VERSION}")
|
|||||||
project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C)
|
project(${PROJ} VERSION ${PROJ_VERSION} LANGUAGES C)
|
||||||
|
|
||||||
# default flags
|
# 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)
|
set(CMAKE_COLOR_MAKEFILE ON)
|
||||||
|
|
||||||
# here is one of two variants: all .c in directory or .c files in list
|
# here is one of two variants: all .c in directory or .c files in list
|
||||||
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR} SOURCES)
|
||||||
|
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
||||||
|
|
||||||
# cmake -DEBUG=1 -> debugging
|
# cmake -DEBUG=1 -> debugging
|
||||||
if(DEFINED EBUG AND EBUG EQUAL 1)
|
if(DEFINED EBUG AND EBUG EQUAL 1)
|
||||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra -Wall -Werror -W")
|
||||||
set(CMAKE_BUILD_TYPE DEBUG)
|
set(CMAKE_BUILD_TYPE DEBUG)
|
||||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
|
||||||
add_definitions(-DEBUG)
|
add_definitions(-DEBUG)
|
||||||
else()
|
else()
|
||||||
set(CMAKE_BUILD_TYPE RELEASE)
|
set(CMAKE_BUILD_TYPE RELEASE)
|
||||||
set(CMAKE_VERBOSE_MAKEFILE "ON")
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
###### pkgconfig ######
|
###### pkgconfig ######
|
||||||
@ -34,6 +35,7 @@ set(MODULES usefull_macros cfitsio)
|
|||||||
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
find_package(FLYCAP REQUIRED)
|
find_package(FLYCAP REQUIRED)
|
||||||
|
find_package(BASLER REQUIRED)
|
||||||
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
pkg_check_modules(${PROJ} REQUIRED ${MODULES})
|
||||||
|
|
||||||
include(FindOpenMP)
|
include(FindOpenMP)
|
||||||
@ -60,9 +62,9 @@ message("Install dir prefix: ${CMAKE_INSTALL_PREFIX}")
|
|||||||
# exe file
|
# exe file
|
||||||
add_executable(${PROJ} ${SOURCES})
|
add_executable(${PROJ} ${SOURCES})
|
||||||
# -I
|
# -I
|
||||||
include_directories(${${PROJ}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS})
|
include_directories(${${PROJ}_INCLUDE_DIRS} ${FLYCAP_INCLUDE_DIRS} ${BASLER_INCLUDE_DIRS})
|
||||||
# -L
|
# -L
|
||||||
link_directories(${${PROJ}_LIBRARY_DIRS})
|
link_directories(${${PROJ}_LIBRARY_DIRS} ${FLYCAP_LIBRARY_DIRS} ${BASLER_LIBRARY_DIRS})
|
||||||
# -D
|
# -D
|
||||||
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
||||||
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
-DPACKAGE_VERSION=\"${VERSION}\" -DGETTEXT_PACKAGE=\"${PROJ}\"
|
||||||
@ -70,7 +72,7 @@ add_definitions(${CFLAGS} -DLOCALEDIR=\"${LOCALEDIR}\"
|
|||||||
-DMAJOR_VERSION=\"${MAJOR_VESION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT})
|
-DMAJOR_VERSION=\"${MAJOR_VESION}\" -DTHREAD_NUMBER=${PROCESSOR_COUNT})
|
||||||
|
|
||||||
# -l
|
# -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
|
# Installation of the program
|
||||||
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
INSTALL(TARGETS ${PROJ} DESTINATION "bin")
|
||||||
|
|||||||
36
LocCorr/FindBASLER.cmake
Normal file
36
LocCorr/FindBASLER.cmake
Normal file
@ -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 <edward.emelianoff@gmail.com>
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
@ -15,9 +15,9 @@ find_path(FLYCAP_INCLUDE_DIR FlyCapture2.h
|
|||||||
PATH_SUFFIXES libflycapture flycapture
|
PATH_SUFFIXES libflycapture flycapture
|
||||||
PATHS /usr/include /usr/local/include /opt/include /opt/local/include
|
PATHS /usr/include /usr/local/include /opt/include /opt/local/include
|
||||||
)
|
)
|
||||||
#find_path(FLYCAP_LIBRARY_DIR libflycapture.so
|
find_path(FLYCAP_LIBRARY_DIR libflycapture.so
|
||||||
# PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64
|
PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64
|
||||||
#)
|
)
|
||||||
find_library(FLYCAP_LIBRARY NAMES flycapture
|
find_library(FLYCAP_LIBRARY NAMES flycapture
|
||||||
PATHS /lib /lib64 /usr/lib /usr/lib64 /opt/lib /opt/lib64 /usr/local/lib /usr/local/lib64
|
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
|
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)
|
if(FLYCAP_FOUND)
|
||||||
set(FLYCAP_INCLUDE_DIRS ${FLYCAP_INCLUDE_DIR})
|
set(FLYCAP_INCLUDE_DIRS ${FLYCAP_INCLUDE_DIR})
|
||||||
set(FLYCAP_LIBRARIES ${FLYCAP_LIBRARY} ${FLYCAP_LIBRARYC})
|
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 include dir = ${FLYCAP_INCLUDE_DIRS}")
|
||||||
# message("FLYCAP lib = ${FLYCAP_LIBRARIES}")
|
# message("FLYCAP lib = ${FLYCAP_LIBRARIES}")
|
||||||
# message("FLYCAP libdir = ${FLYCAP_LIBRARY_DIRS}")
|
# message("FLYCAP libdir = ${FLYCAP_LIBRARY_DIRS}")
|
||||||
|
|||||||
334
LocCorr/basler.c
Normal file
334
LocCorr/basler.c
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the loccorr project.
|
||||||
|
* Copyright 2021 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 <usefull_macros.h>
|
||||||
|
#include <pylonc/PylonC.h>
|
||||||
|
|
||||||
|
#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,
|
||||||
|
};
|
||||||
|
|
||||||
27
LocCorr/basler.h
Normal file
27
LocCorr/basler.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the loccorr project.
|
||||||
|
* Copyright 2021 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
|
||||||
|
#ifndef BASLER_H__
|
||||||
|
#define BASLER_H__
|
||||||
|
|
||||||
|
#include "cameracapture.h" // `camera`
|
||||||
|
#define BASLER_CAPT_NAME "basler"
|
||||||
|
|
||||||
|
extern camera Basler;
|
||||||
|
|
||||||
|
#endif // BASLER_H__
|
||||||
261
LocCorr/cameracapture.c
Normal file
261
LocCorr/cameracapture.c
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the loccorr project.
|
||||||
|
* Copyright 2021 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> // FLT_EPSILON
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
52
LocCorr/cameracapture.h
Normal file
52
LocCorr/cameracapture.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the loccorr project.
|
||||||
|
* Copyright 2021 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
|
||||||
|
#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__
|
||||||
@ -217,11 +217,11 @@ confparam *chk_keyval(const char *key, const char *val, key_value *result){
|
|||||||
confparam *par = parvals;
|
confparam *par = parvals;
|
||||||
while(par->name){
|
while(par->name){
|
||||||
if(strcmp(key, par->name) == 0){
|
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;
|
result->type = par->type;
|
||||||
switch(par->type){
|
switch(par->type){
|
||||||
case PAR_INT:
|
case PAR_INT:
|
||||||
DBG("INTEGER");
|
//DBG("INTEGER");
|
||||||
if(!str2int(&result->val.intval, val)){
|
if(!str2int(&result->val.intval, val)){
|
||||||
WARNX("Wrong integer value '%s' of parameter '%s'", val, key);
|
WARNX("Wrong integer value '%s' of parameter '%s'", val, key);
|
||||||
return NULL;
|
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);
|
result->val.intval, par->name, par->minval, par->maxval);
|
||||||
break;
|
break;
|
||||||
case PAR_DOUBLE:
|
case PAR_DOUBLE:
|
||||||
DBG("DOUBLE");
|
//DBG("DOUBLE");
|
||||||
if(!str2double(&result->val.dblval, val)){
|
if(!str2double(&result->val.dblval, val)){
|
||||||
WARNX("Wrong double value '%s' of parameter '%s'", val, key);
|
WARNX("Wrong double value '%s' of parameter '%s'", val, key);
|
||||||
return NULL;
|
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)
|
if(result->val.dblval > par->minval && result->val.dblval < par->maxval)
|
||||||
return par;
|
return par;
|
||||||
else WARNX("Value (%g) of parameter %s out of range %g..%g",
|
else WARNX("Value (%g) of parameter %s out of range %g..%g",
|
||||||
@ -300,7 +300,7 @@ int chkconfig(const char *confname){
|
|||||||
int found = 0;
|
int found = 0;
|
||||||
par = parvals;
|
par = parvals;
|
||||||
while(par->name){
|
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;
|
int k = par->got;
|
||||||
if(!k){
|
if(!k){
|
||||||
++par;
|
++par;
|
||||||
|
|||||||
@ -38,9 +38,9 @@
|
|||||||
#define EXPOS_MIN (0.1)
|
#define EXPOS_MIN (0.1)
|
||||||
#define EXPOS_MAX (4001.)
|
#define EXPOS_MAX (4001.)
|
||||||
#define GAIN_MIN (0.)
|
#define GAIN_MIN (0.)
|
||||||
#define GAIN_MAX (33.)
|
#define GAIN_MAX (100.)
|
||||||
#define BRIGHT_MIN (0.)
|
#define BRIGHT_MIN (0.)
|
||||||
#define BRIGHT_MAX (1000.)
|
#define BRIGHT_MAX (10.)
|
||||||
// max average images counter
|
// max average images counter
|
||||||
#define NAVER_MAX (50)
|
#define NAVER_MAX (50)
|
||||||
// coefficients to convert dx,dy to du,dv
|
// coefficients to convert dx,dy to du,dv
|
||||||
|
|||||||
@ -24,22 +24,20 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <usefull_macros.h>
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
#include "cmdlnopts.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "fits.h"
|
|
||||||
#include "grasshopper.h"
|
#include "grasshopper.h"
|
||||||
#include "imagefile.h"
|
#include "imagefile.h"
|
||||||
#include "improc.h"
|
|
||||||
|
|
||||||
static fc2Context context;
|
static fc2Context context;
|
||||||
static fc2PGRGuid guid;
|
static fc2PGRGuid guid;
|
||||||
static fc2Error err = FC2_ERROR_OK;
|
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__))){ \
|
#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)
|
* @brief setfloat - set absolute property value (float)
|
||||||
@ -58,21 +56,21 @@ static int setfloat(fc2PropertyType t, float f){
|
|||||||
if(prop.autoManualMode){
|
if(prop.autoManualMode){
|
||||||
if(!i.manualSupported){
|
if(!i.manualSupported){
|
||||||
WARNX("Can't set auto-only property");
|
WARNX("Can't set auto-only property");
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
prop.autoManualMode = false;
|
prop.autoManualMode = false;
|
||||||
}
|
}
|
||||||
if(!prop.absControl){
|
if(!prop.absControl){
|
||||||
if(!i.absValSupported){
|
if(!i.absValSupported){
|
||||||
WARNX("Can't set non-absolute property to absolute value");
|
WARNX("Can't set non-absolute property to absolute value");
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
prop.absControl = true;
|
prop.absControl = true;
|
||||||
}
|
}
|
||||||
if(!prop.onOff){
|
if(!prop.onOff){
|
||||||
if(!i.onOffSupported){
|
if(!i.onOffSupported){
|
||||||
WARNX("Can't set property ON");
|
WARNX("Can't set property ON");
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
prop.onOff = true;
|
prop.onOff = true;
|
||||||
}
|
}
|
||||||
@ -84,12 +82,24 @@ static int setfloat(fc2PropertyType t, float f){
|
|||||||
FC2FN(fc2GetProperty, &prop);
|
FC2FN(fc2GetProperty, &prop);
|
||||||
if(fabsf(prop.absValue - f) > 0.02f){
|
if(fabsf(prop.absValue - f) > 0.02f){
|
||||||
WARNX("Can't set property! Got %g instead of %g.", prop.absValue, f);
|
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};
|
fc2Property prop = {0};
|
||||||
prop.type = t;
|
prop.type = t;
|
||||||
fc2PropertyInfo i = {0};
|
fc2PropertyInfo i = {0};
|
||||||
@ -118,56 +128,63 @@ int propOnOff(fc2PropertyType t, BOOL onOff){
|
|||||||
#define trigModeOff() propOnOff(FC2_TRIGGER_MODE, false)
|
#define trigModeOff() propOnOff(FC2_TRIGGER_MODE, false)
|
||||||
#define trigDelayOff() propOnOff(FC2_TRIGGER_DELAY, false)
|
#define trigDelayOff() propOnOff(FC2_TRIGGER_DELAY, false)
|
||||||
#define frameRateOff() propOnOff(FC2_FRAME_RATE, 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;
|
static int geometrylimits(frameformat *max, frameformat *step){
|
||||||
void disconnectGrasshopper(){
|
fc2Format7Info f = {.mode = FC2_MODE_0};
|
||||||
if(!connected) return;
|
BOOL b;
|
||||||
connected = 0;
|
fc2Format7Info i = {0};
|
||||||
fc2DestroyContext(context);
|
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();
|
FNAME();
|
||||||
BOOL b;
|
BOOL b;
|
||||||
fc2Format7Info f7i = {.mode = FC2_MODE_0};
|
|
||||||
FC2FN(fc2GetFormat7Info, &f7i, &b);
|
|
||||||
if(!b) return 0;
|
|
||||||
fc2Format7ImageSettings f7;
|
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.mode = FC2_MODE_0;
|
||||||
f7.offsetX = (theconf.xoff < (int)f7i.maxWidth && theconf.xoff > -1) ? theconf.xoff : 0;
|
f7.offsetX = fmt->xoff;
|
||||||
f7.offsetY = (theconf.yoff < (int)f7i.maxHeight && theconf.yoff > -1) ? theconf.yoff : 0;
|
f7.offsetY = fmt->yoff;
|
||||||
f7.width = (f7.offsetX+theconf.width <= f7i.maxWidth && theconf.width > 1) ? (unsigned int)theconf.width : f7i.maxWidth - f7.offsetX;
|
f7.width = fmt->w;
|
||||||
f7.height = (f7.offsetY+theconf.height <= f7i.maxHeight && theconf.height > 1) ? (unsigned int)theconf.height : f7i.maxHeight - f7.offsetY;
|
f7.height = fmt->h;
|
||||||
DBG("offx=%d, offy=%d, w=%d, h=%d ", f7.offsetX, f7.offsetY, f7.width, f7.height);
|
DBG("offx=%d, offy=%d, w=%d, h=%d ", f7.offsetX, f7.offsetY, f7.width, f7.height);
|
||||||
f7.pixelFormat = FC2_PIXEL_FORMAT_MONO8;
|
f7.pixelFormat = FC2_PIXEL_FORMAT_MONO8;
|
||||||
fc2Format7PacketInfo f7p;
|
fc2Format7PacketInfo f7p;
|
||||||
FC2FN(fc2ValidateFormat7Settings, &f7, &b, &f7p);
|
FC2FN(fc2ValidateFormat7Settings, &f7, &b, &f7p);
|
||||||
if(!b) return 0; // invalid
|
if(!b) return FALSE; // invalid
|
||||||
FC2FN(fc2SetFormat7Configuration, &f7, f7p.recommendedBytesPerPacket);
|
FC2FN(fc2SetFormat7Configuration, &f7, f7p.recommendedBytesPerPacket);
|
||||||
return 1;
|
getformat(fmt); // change values to currently used
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int connect(){
|
static int connect(){
|
||||||
if(connected) return 1;
|
|
||||||
FNAME();
|
FNAME();
|
||||||
unsigned int numCameras = 0;
|
unsigned int numCameras = 0;
|
||||||
if(FC2_ERROR_OK != (err = fc2CreateContext(&context))){
|
if(FC2_ERROR_OK != (err = fc2CreateContext(&context))){
|
||||||
WARNX("fc2CreateContext(): %s", fc2ErrorToDescription(err));
|
WARNX("fc2CreateContext(): %s", fc2ErrorToDescription(err));
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
FC2FN(fc2GetNumOfCameras, &numCameras);
|
FC2FN(fc2GetNumOfCameras, &numCameras);
|
||||||
if(numCameras == 0){
|
if(numCameras == 0){
|
||||||
WARNX("No cameras detected!");
|
WARNX("No cameras detected!");
|
||||||
disconnectGrasshopper();
|
camdisconnect();
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
DBG("Found %d camera[s]", numCameras);
|
DBG("Found %d camera[s]", numCameras);
|
||||||
if(numCameras > 1){
|
if(numCameras > 1){
|
||||||
@ -182,14 +199,12 @@ static int connect(){
|
|||||||
trigModeOff();
|
trigModeOff();
|
||||||
trigDelayOff();
|
trigDelayOff();
|
||||||
frameRateOff();
|
frameRateOff();
|
||||||
if(!changeformat()) WARNX("Can't change camera format");
|
return TRUE;
|
||||||
connected = 1;
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GrabImage(fc2Image *convertedImage){
|
static int GrabImage(fc2Image *convertedImage){
|
||||||
//FNAME();
|
//FNAME();
|
||||||
int ret = 0;
|
int ret = FALSE;
|
||||||
fc2Image rawImage;
|
fc2Image rawImage;
|
||||||
// start capture
|
// start capture
|
||||||
FC2FN(fc2StartCapture);
|
FC2FN(fc2StartCapture);
|
||||||
@ -210,166 +225,45 @@ static int GrabImage(fc2Image *convertedImage){
|
|||||||
WARNX("Error in fc2ConvertImageTo: %s", fc2ErrorToDescription(err));
|
WARNX("Error in fc2ConvertImageTo: %s", fc2ErrorToDescription(err));
|
||||||
goto rtn;
|
goto rtn;
|
||||||
}
|
}
|
||||||
ret = 1;
|
ret = TRUE;
|
||||||
rtn:
|
rtn:
|
||||||
fc2StopCapture(context);
|
fc2StopCapture(context);
|
||||||
fc2DestroyImage(&rawImage);
|
fc2DestroyImage(&rawImage);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void calcexpgain(float newexp){
|
// capture image, memory allocated HERE
|
||||||
DBG("recalculate exposition: oldexp=%g, oldgain=%g, newexp=%g", exptime, gain, newexp);
|
static Image *capture(){
|
||||||
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*)){
|
|
||||||
FNAME();
|
FNAME();
|
||||||
static float oldexptime = 0.;
|
|
||||||
static float oldgain = -1.;
|
|
||||||
static float oldbrightness = -1.;
|
|
||||||
Image *oIma = NULL;
|
Image *oIma = NULL;
|
||||||
fc2Image convertedImage;
|
fc2Image convertedImage;
|
||||||
err = fc2CreateImage(&convertedImage);
|
err = fc2CreateImage(&convertedImage);
|
||||||
if(err != FC2_ERROR_OK){
|
if(err != FC2_ERROR_OK){
|
||||||
WARNX("capture_grasshopper(): can't create image, %s", fc2ErrorToDescription(err));
|
WARNX("capture_grasshopper(): can't create image, %s", fc2ErrorToDescription(err));
|
||||||
disconnectGrasshopper();
|
return NULL;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
while(1){
|
if(!GrabImage(&convertedImage)){
|
||||||
if(stopwork){
|
WARNX("Can't grab image");
|
||||||
DBG("STOP");
|
return NULL;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
oIma = u8toImage(convertedImage.pData, convertedImage.cols, convertedImage.rows, convertedImage.stride);
|
||||||
fc2DestroyImage(&convertedImage);
|
fc2DestroyImage(&convertedImage);
|
||||||
disconnectGrasshopper();
|
return oIma;
|
||||||
DBG("GRASSHOPPER: out");
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return JSON with image status
|
static float maxgain(){
|
||||||
char *gsimagestatus(const char *messageid, char *buf, int buflen){
|
return GAIN_MAX;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exported object
|
||||||
|
camera GrassHopper = {
|
||||||
|
.disconnect = disconnect,
|
||||||
|
.connect = connect,
|
||||||
|
.capture = capture,
|
||||||
|
.setbrightness = setbrightness,
|
||||||
|
.setexp = setexp,
|
||||||
|
.setgain = setgain,
|
||||||
|
.setgeometry = changeformat,
|
||||||
|
.getgeomlimits = geometrylimits,
|
||||||
|
.getmaxgain = maxgain,
|
||||||
|
};
|
||||||
|
|||||||
@ -19,12 +19,10 @@
|
|||||||
#ifndef GRASSHOPPER_H__
|
#ifndef GRASSHOPPER_H__
|
||||||
#define GRASSHOPPER_H__
|
#define GRASSHOPPER_H__
|
||||||
|
|
||||||
#include "fits.h" // Image*
|
#include "cameracapture.h" // `camera`
|
||||||
|
|
||||||
#define GRASSHOPPER_CAPT_NAME "grasshopper"
|
#define GRASSHOPPER_CAPT_NAME "grasshopper"
|
||||||
|
|
||||||
void disconnectGrasshopper();
|
extern camera GrassHopper;
|
||||||
int capture_grasshopper(void (*process)(Image *));
|
|
||||||
char *gsimagestatus(const char *messageid, char *buf, int buflen);
|
|
||||||
|
|
||||||
#endif // GRASSHOPPER_H__
|
#endif // GRASSHOPPER_H__
|
||||||
|
|||||||
@ -27,6 +27,8 @@
|
|||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
#include <stb/stb_image_write.h>
|
#include <stb/stb_image_write.h>
|
||||||
|
|
||||||
|
#include "basler.h"
|
||||||
|
#include "cameracapture.h"
|
||||||
#include "cmdlnopts.h"
|
#include "cmdlnopts.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
@ -96,6 +98,7 @@ static InputType imtype(FILE *f){
|
|||||||
InputType chkinput(const char *name){
|
InputType chkinput(const char *name){
|
||||||
DBG("input name: %s", name);
|
DBG("input name: %s", name);
|
||||||
if(0 == strcmp(name, GRASSHOPPER_CAPT_NAME)) return T_CAPT_GRASSHOPPER;
|
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;
|
struct stat fd_stat;
|
||||||
stat(name, &fd_stat);
|
stat(name, &fd_stat);
|
||||||
if(S_ISDIR(fd_stat.st_mode)){
|
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){
|
Image *u8toImage(uint8_t *data, int width, int height, int stride){
|
||||||
|
FNAME();
|
||||||
Image *outp = MALLOC(Image, 1);
|
Image *outp = MALLOC(Image, 1);
|
||||||
outp->width = width;
|
outp->width = width;
|
||||||
outp->height = height;
|
outp->height = height;
|
||||||
|
|||||||
@ -33,7 +33,8 @@ typedef enum{
|
|||||||
T_GIF,
|
T_GIF,
|
||||||
T_JPEG,
|
T_JPEG,
|
||||||
T_PNG,
|
T_PNG,
|
||||||
T_CAPT_GRASSHOPPER // capture grasshopper
|
T_CAPT_GRASSHOPPER, // capture grasshopper
|
||||||
|
T_CAPT_BASLER
|
||||||
} InputType;
|
} InputType;
|
||||||
|
|
||||||
void Image_minmax(Image *I);
|
void Image_minmax(Image *I);
|
||||||
|
|||||||
@ -23,7 +23,9 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <usefull_macros.h>
|
#include <usefull_macros.h>
|
||||||
|
|
||||||
|
#include "basler.h"
|
||||||
#include "binmorph.h"
|
#include "binmorph.h"
|
||||||
|
#include "cameracapture.h"
|
||||||
#include "cmdlnopts.h"
|
#include "cmdlnopts.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
@ -163,6 +165,7 @@ process_corrections:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void process_file(Image *I){
|
void process_file(Image *I){
|
||||||
|
FNAME();
|
||||||
static double lastTproc = 0.;
|
static double lastTproc = 0.;
|
||||||
/*
|
/*
|
||||||
#ifdef EBUG
|
#ifdef EBUG
|
||||||
@ -187,7 +190,7 @@ void process_file(Image *I){
|
|||||||
//DELTA("Save original");
|
//DELTA("Save original");
|
||||||
Imtype bk;
|
Imtype bk;
|
||||||
if(calc_background(I, &bk)){
|
if(calc_background(I, &bk)){
|
||||||
//DBG("backgr = %g", bk);
|
DBG("backgr = %g", bk);
|
||||||
DELTA("Got background");
|
DELTA("Got background");
|
||||||
uint8_t *ibin = Im2bin(I, bk);
|
uint8_t *ibin = Im2bin(I, bk);
|
||||||
DELTA("Made binary");
|
DELTA("Made binary");
|
||||||
@ -290,40 +293,17 @@ void process_file(Image *I){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
FREE(tmpnm);
|
FREE(tmpnm);
|
||||||
++ImNumber;
|
|
||||||
if(lastTproc > 1.) FPS = 1. / (dtime() - lastTproc);
|
|
||||||
lastTproc = dtime();
|
|
||||||
FREE(outp);
|
FREE(outp);
|
||||||
}
|
}
|
||||||
FREE(Objects);
|
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);
|
}else Image_write_jpg(I, GP->outputjpg, theconf.equalize);
|
||||||
FREE(S);
|
FREE(S);
|
||||||
FREE(cc);
|
FREE(cc);
|
||||||
}
|
}
|
||||||
}
|
}else Image_write_jpg(I, GP->outputjpg, theconf.equalize);
|
||||||
|
++ImNumber;
|
||||||
|
if(lastTproc > 1.) FPS = 1. / (dtime() - lastTproc);
|
||||||
|
lastTproc = dtime();
|
||||||
DELTA("End");
|
DELTA("End");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,13 +324,19 @@ static char *watchfl(const char *messageid, char *buf, int buflen){
|
|||||||
}
|
}
|
||||||
|
|
||||||
int process_input(InputType tp, char *name){
|
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){
|
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){
|
}else if(tp == T_CAPT_GRASSHOPPER || tp == T_CAPT_BASLER){
|
||||||
imagedata = gsimagestatus;
|
camera *cam = &GrassHopper;
|
||||||
return capture_grasshopper(process_file);
|
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;
|
imagedata = watchfl;
|
||||||
return watch_file(name, process_file);
|
return watch_file(name, process_file);
|
||||||
|
|||||||
@ -58,7 +58,7 @@ void iffound_default(pid_t pid){
|
|||||||
ERRX("Another copy of this process found, pid=%d. Exit.", 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);
|
int p = process_input(tp, GP->inputname);
|
||||||
LOGDBG("process_input=%d", p);
|
LOGDBG("process_input=%d", p);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -94,6 +94,9 @@ static InputType chk_inp(const char *name){
|
|||||||
case T_CAPT_GRASSHOPPER:
|
case T_CAPT_GRASSHOPPER:
|
||||||
printf("capture grasshopper camera");
|
printf("capture grasshopper camera");
|
||||||
break;
|
break;
|
||||||
|
case T_CAPT_BASLER:
|
||||||
|
printf("capture basler camera");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
printf("Unsupported type\n");
|
printf("Unsupported type\n");
|
||||||
return T_WRONG;
|
return T_WRONG;
|
||||||
|
|||||||
@ -444,9 +444,10 @@ int get_stat(const Image *in, int seed, Image **mean, Image **std){
|
|||||||
* @return 0 if error
|
* @return 0 if error
|
||||||
*/
|
*/
|
||||||
int calc_background(Image *img, Imtype *bk){
|
int calc_background(Image *img, Imtype *bk){
|
||||||
|
//DBG("image: min=%g, max=%g", img->minval, img->maxval);
|
||||||
if(img->maxval - img->minval < DBL_EPSILON){
|
if(img->maxval - img->minval < DBL_EPSILON){
|
||||||
WARNX("Zero image!");
|
WARNX("Zero image!");
|
||||||
return 0;
|
return FALSE;
|
||||||
}
|
}
|
||||||
int w = img->width, h = img->height, wh = w*h;
|
int w = img->width, h = img->height, wh = w*h;
|
||||||
Imtype min = img->minval, ampl = img->maxval - min;
|
Imtype min = img->minval, ampl = img->maxval - min;
|
||||||
@ -472,24 +473,13 @@ int calc_background(Image *img, Imtype *bk){
|
|||||||
modeidx = i;
|
modeidx = i;
|
||||||
}
|
}
|
||||||
//DBG("Mode=%g @ idx%d (N=%d)", ((Imtype)modeidx / 255.)*ampl, modeidx, modeval);
|
//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};
|
int diff2[256] = {0};
|
||||||
for(int i = 2; i < 254; ++i) diff2[i] = (histogram[i+2]+histogram[i-2]-2*histogram[i])/4;
|
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 < 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;
|
int borderidx = modeidx;
|
||||||
// green("2\n");
|
// green("2\n");
|
||||||
for(int i = modeidx; i < 254; ++i){ // search bend-point by second derivate
|
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;
|
//borderidx = (borderidx + modeidx) / 2;
|
||||||
Imtype borderval = ((Imtype)borderidx / 255.)*ampl + min;
|
Imtype borderval = ((Imtype)borderidx / 255.)*ampl + min;
|
||||||
if(bk) *bk = borderval;
|
if(bk) *bk = borderval;
|
||||||
//green("HISTO:\n");
|
// green("HISTO:\n");
|
||||||
//for(int i = 0; i < 256; ++i) printf("%d:\t%d\t%d\n", i, histogram[i], diff2[i]);
|
// 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
|
// calculate values of upper 2% border
|
||||||
#if 0
|
#if 0
|
||||||
Image *out = Image_sim(img);
|
Image *out = Image_sim(img);
|
||||||
@ -515,5 +505,5 @@ int calc_background(Image *img, Imtype *bk){
|
|||||||
}
|
}
|
||||||
Image_minmax(out);
|
Image_minmax(out);
|
||||||
#endif
|
#endif
|
||||||
return 1;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user