ported to git

This commit is contained in:
eddyem 2014-09-22 14:48:19 +04:00
parent 0fb1222d34
commit 35a1e70547
46 changed files with 14076 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.hg*
*~
*.bak

24
CMakeLists.txt Normal file
View File

@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 2.6)
set(PROJ fitsview)
project(${PROJ})
set(CMAKE_COLOR_MAKEFILE ON)
set(DEBUG 1) # ÚÁËÏÍÍÅÎÔÉÒÏ×ÁÔØ ÜÔÕ ÓÔÒÏËÕ ÐÏ ÏËÏÎÞÁÎÉÉ "ÒÁÚÒÁÂÏÔËÉ"
if(NOT DEFINED NO_CUDA)
message("Try to use CUDA")
find_package(CUDA)
if(CUDA_FOUND)
add_definitions(-DCUDA_FOUND)
endif()
endif()
if(NOT DEFINED PROCESSOR_COUNT)
set(PROCESSOR_COUNT 2) # by default 2 cores
set(cpuinfo_file "/proc/cpuinfo")
if(EXISTS "${cpuinfo_file}")
file(STRINGS "${cpuinfo_file}" procs REGEX "^processor.: [0-9]+$")
list(LENGTH procs PROCESSOR_COUNT)
endif()
endif()
add_definitions(-DTHREAD_NUMBER=${PROCESSOR_COUNT})
message("In multithreaded operations will use ${PROCESSOR_COUNT} threads")
subdirs(src)

2
COPYING Normal file
View File

@ -0,0 +1,2 @@
eddy@sao.ru

99
ChangeLog Normal file
View File

@ -0,0 +1,99 @@
01.12.2011 Edward V. Emelianoff <eddy@sao.ru>
* Изменена функция определения изолиний. Теперь медлненно, но надежно. Планирую ускорить
- обнаружены ошибки при открывании файла сразу в 3D
- неверно отображаются изолинии на 3D при выборе части изображения
06.09.2011 Edward V. Emelianoff <eddy@sao.ru>
* добавлен поиск изолиний (с различными типами шкалы высот)
- АЛГОРИТМ ЖУТКО ГЛЮЧИТ
- сегфолт на экспоненциальной шкале высот
- почему-то некоторые контуры получаются разорванными, контуры выходят за пределы рисунка
29.08.2011 Edward V. Emelianoff <eddy@sao.ru>
* добавлен медианный фильтр
22.08.2011 Edward V. Emelianoff <eddy@sao.ru>
* убран сегфолт при попытке открыть битую симв. ссылку
* начал добавлять фильтры
14.08.2011 Edward V. Emelianoff <eddy@sao.ru>
* перешел на mercurial
18.07.2011 Edward V. Emelianoff <eddy@sao.ru>
- исправить глюки с гистограммами и Хафом
- переписать функции рассчета вершин и нормалей на CUDA
- сделать нормальные пределы на гистограмме (а не [0, 1])
- скорректировать линейки на графическом окне
30.05.2011 Edward V. Emelianoff <eddy@sao.ru>
* исправлена ошибка с чтением шапок фитс-файлов
* разделены операции обнаружения пятен и распознавания гартманограммы
* автоматическое распознавание пред- и зафокальных гартманограмм
* сохранение списка координат обнаруженных пятен
10.04.2011 Edward V. Emelianoff <eddy@sao.ru>
-+частично реализовано сохранение FITSов (и шапок)
-+частично реализовано вращение 3D мышью
04.04.2011 Edward V. Emelianoff <eddy@sao.ru>
* пункт меню "открыть" в окне 3D (без навигации pgUp/pgDwn)
* прокрутка в редактировании/просмотре шапки фитсов
* пункты меню "открыть в новом окне" и "открыть в 3D"
31.03.2011 Edward V. Emelianoff <eddy@sao.ru>
* навигация по файлам в текущей директории
31.03.2011 Edward V. Emelianoff <eddy@sao.ru>
Пре-альфа версия, на ближайшее время реализовать:
- отображение имени файла в строке состояния (а то непонятно, что за файл открыт)
- глюк: "увеличение рамкой" не отменяется
- исправить глюки при открывании нового файла (остаются старые режимы, spots и т.п.)
- предлагать на выбор методы определения центров тяжести (аппр. гауссом, параболой по логарифму, "тупой", параболой по макушке, "тупой" с порогами/весами, аппр. параболоидом...)
- отображать отмеченные треки, границы на гистограмме и т.п.
- редактировать границы треков
- разбор аргументов командной строки
- batch-режим
- вращение 3D мышью
- сохранение FITSов (и шапок) преобразованного изображения
- интерактивный Хаф
- распознавание окружностей
- расчет поверхности зеркала по гартманограммам (случай 1 и пары снимков)
- фильтрация изображений (свертки/Фурье, вейвлеты)
- корреляционный анализ
- визуальное сравнение двух изображений (верхнее - полупрозрачное)
- добавить в 3D NURBS'ы
- "лупа", копипаст, редактирование (с сохранением)
- полная статистика
- вычисление экв. ширины линии на срезе
- отождествление кривых (например, сп. порядков или теллур. линий) и "выпрямление" изображения
- арифметические операции над изображениями
- отображение комплексных изображений (варианты: амплитуда, фаза, Re, Im) и комплексные операции (Фурье, арифм., Фурье-фильтры, градиентные фильтры и т.п.)
- глючит редактирование (где-то лишнее free, не снимается выделение с ячеек)
- сохранение FITSов (и шапок)
- вращение 3D мышью
- исправить глюки с гистограммами и Хафом
- переписать функции рассчета вершин и нормалей на CUDA
- сделать нормальные пределы на гистограмме (а не [0, 1])
- скорректировать линейки на графическом окне
- при открывании нового изображения старое не удаляется из памяти - УТЕЧКА

0
INSTALL Normal file
View File

1
NEWS Normal file
View File

@ -0,0 +1 @@
none

6
README Normal file
View File

@ -0,0 +1,6 @@
Ключи cmake:
DEBUG=1 - отладочный режим
NO_CUDA=1 - не использовать CUDA, даже если она поддерживается системой
PROCESSOR_COUNT=X - в многопоточных задачах использовать X потоков
NO_LEPTONICA=1 - не использовать лептонику, даже если она есть в системе
NO_GSL=1 - не использовать GSL, даже если она есть в системе

88
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 2.6)
if(DEFINED DEBUG)
add_definitions(-DEBUG)
endif()
set(SRC ${CMAKE_SOURCE_DIR}/src)
aux_source_directory(${SRC} SOURCES)
set(NOCUFILE ${SRC}/NOCUDA.c)
set(CUFILE ${SRC}/CUDA.cu)
set(CFLAGS -O3 -Wall -Werror -W -std=c99)
set(LCPATH ${SRC}/locale/ru)
set(PO_FILE ${LCPATH}/messages.po)
set(MO_FILE ${LCPATH}/LC_MESSAGES/${PROJ}.mo)
set(RU_FILE ${LCPATH}/ru.po)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
find_package(GTK2 REQUIRED)
pkg_check_modules(${PROJ} REQUIRED
#glib-2.0>=2.10
gtkglext-1.0>=0.7.0
gtkglext-x11-1.0>=0.7.0
#gtk+-2.0>=2.6.0
cfitsio>=3.0
fftw3>=3.2.0
)
include(FindOpenMP)
if(OPENMP_FOUND)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}")
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -lfftw3_threads")
if(NOT DEFINED NO_LEPTONICA)
pkg_check_modules(LIBLEPT liblept)
endif()
if(NOT DEFINED NO_GSL)
pkg_check_modules(GSL gsl)
endif()
if(NOT DEFINED GSL_VERSION)
message("GSL not found, some mathematics functions wouldn't be avialable")
else()
add_definitions(-DGSL_FOUND)
endif()
if(NOT DEFINED LIBLEPT_VERSION)
message("Leptonica library not found, some functions wouldn't be avialable")
else()
add_definitions(-DLEPTONICA_FOUND)
endif()
if(CUDA_FOUND)
list(REMOVE_ITEM SOURCES ${NOCUFILE})
list(APPEND CUDA_NVCC_FLAGS --use_fast_math)
cuda_include_directories(include)
cuda_add_executable(${PROJ} ${SOURCES} ${CUFILE} ${PO_FILE} ${MO_FILE} ui.h)
target_link_libraries( ${PROJ} ${${PROJ}_LIBRARIES}
${GSL_LIBRARIES} ${LIBLEPT_LIBRARIES}
${CUDA_CUFFT_LIBRARIES} -lcuda)
else(CUDA_FOUND)
find_package(Threads)
add_executable(${PROJ} ${SOURCES} ${PO_FILE} ${MO_FILE} ui.h)
target_link_libraries( ${PROJ} ${${PROJ}_LIBRARIES}
${GSL_LIBRARIES} ${LIBLEPT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
endif(CUDA_FOUND)
include_directories(${SRC}/include ${${PROJ}_INCLUDE_DIRS} ${GSL_INCLUDE_DIRS} ${LIBLEPT_INCLUDE_DIRS})
link_directories(${${PROJ}_LIBRARY_DIRS} ${GSL_LIBRARY_DIRS} ${LIBLEPT_LIBRARY_DIRS})
add_definitions(-DPACKAGE_VERSION=\"0.0.1\" -DGETTEXT_PACKAGE=\"${PROJ}\"
-DLOCALEDIR=\"~/.local/share/locale\" ${CFLAGS})
#if(DEFINED DEBUG)
find_package(Gettext REQUIRED)
find_program(GETTEXT_XGETTEXT_EXECUTABLE xgettext)
if(NOT GETTEXT_XGETTEXT_EXECUTABLE )
message(FATAL_ERROR "xgettext not found")
endif(NOT GETTEXT_XGETTEXT_EXECUTABLE )
add_custom_command(
OUTPUT ${PO_FILE}
COMMAND ${GETTEXT_XGETTEXT_EXECUTABLE} --from-code=koi8-r ${SOURCES} ${SRC}/${PROJ}.glade -k_ -kN_ -o ${PO_FILE}
DEPENDS ${SOURCES})
add_custom_command(
OUTPUT ${MO_FILE}
COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${RU_FILE} -o ${MO_FILE}
DEPENDS ${RU_FILE})
add_custom_command(
OUTPUT ui.h
COMMAND gtk-builder-convert ${SRC}/${PROJ}.glade ${PROJ}.ui
COMMAND ${SRC}/scripts/genh ${PROJ}.ui
DEPENDS ${PROJ}.glade)
#endif(DEFINED DEBUG)

693
src/CUDA.cu Normal file
View File

@ -0,0 +1,693 @@
/*
* CUDA.cu - subroutines for GPU
*
* Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#define _CUDA_CU_
#include "include/CUtools.h"
#include <cuda.h>
#include <cufft.h>
//#include <cuda_runtime_api.h>
//#include <crt/device_runtime.h>
//#include <device_functions.h>
const int SHMEMSZ = 16383; // default constants, changed runtime
const int QBLKSZ = 16; // QBLKSZ = sqrt(LBLKSZ)
const int LBLKSZ = 512;
// static arrays for sines & cosines values
static float *Sin_d = NULL, *Cos_d = NULL;
// array size
static int sincosize = 0;
cudaError_t CUerr;
inline int CUERROR(char *str){
if(CUerr != cudaSuccess){
fprintf(stderr, "%s, %s\n", str, cudaGetErrorString(CUerr));
return 1;
}else return 0;
}
// error macro (by default - nothing)
#define RETMACRO return
// memory macros
#define CUALLOC(var, size) do{ \
CUerr = cudaMalloc((void**)&var, size); \
if(CUERROR("CUDA: can't allocate memory")){ \
RETMACRO; \
}}while(0)
#define CUMOV2DEV(dest, src, size) do{ \
CUerr = cudaMemcpy(dest, src, size, \
cudaMemcpyHostToDevice); \
if(CUERROR("CUDA: can't copy data to device")){\
RETMACRO;} \
}while(0)
#define CUMOV2HOST(dest, src, size) do{ \
CUerr = cudaMemcpy(dest, src, size, \
cudaMemcpyDeviceToHost); \
if(CUERROR("CUDA: can't copy data to host")){\
RETMACRO;} \
}while(0)
#define CUFREE(var) do{cudaFree(var); var = NULL; }while(0)
#define CUFFTCALL(fn) do{ \
cufftResult fres = fn; \
if(CUFFT_SUCCESS != fres){ \
fprintf(stderr, "CUDA fft error %d\n", fres);\
RETMACRO;} \
}while(0)
#ifdef EBUG
#define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
#define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n");} while(0)
#else
#define FNAME() do{}while(0)
#define DBG(...) do{}while(0)
#endif //EBUG
// getting the videocard parameters
extern "C" void getprops(){
cudaDeviceProp dP;
CUdevice dev; CUcontext ctx;
cudaGetDeviceProperties(&dP, 0);
cuDeviceGet(&dev,0);
cuCtxCreate(&ctx, 0, dev);
printf("\nDevice: %s, totalMem=%zd, memPerBlk=%zd,\n", dP.name, dP.totalGlobalMem, dP.sharedMemPerBlock);
printf("warpSZ=%d, TPB=%d, TBDim=%dx%dx%d\n", dP.warpSize, dP.maxThreadsPerBlock,
dP.maxThreadsDim[0],dP.maxThreadsDim[1],dP.maxThreadsDim[2]);
printf("GridSz=%dx%dx%d, MemovrLap=%d, GPUs=%d\n", dP.maxGridSize[0],
dP.maxGridSize[1],dP.maxGridSize[2],
dP.deviceOverlap, dP.multiProcessorCount);
printf("canMAPhostMEM=%d\n", dP.canMapHostMemory);
printf("compute capability %d.%d.\n\n", dP.major, dP.minor);
if(dP.major > 1){
// SHMEMSZ = 49151; QBLKSZ = 32; LBLKSZ = 1024;
}
size_t theFree, theTotal;
CUresult aaa = cuMemGetInfo( &theFree, &theTotal );
printf("CARD returns(err=%d): free mem:%zd, total mem:%zd\n", aaa, theFree, theTotal);
cuCtxDetach(ctx);
}
// normalisation of array arr with size arrsize
__global__ void normalize_vec(float *arr, int arrsize){
__shared__ float max[LBLKSZ];
int idx = threadIdx.x;
int blksize = (arrsize + blockDim.x - 1) / blockDim.x;
int b_beg = idx * blksize;
if(b_beg >= arrsize) return;
int b_end = b_beg + blksize;
if(b_end > arrsize) b_end = arrsize;
int i; float *ptr = &arr[b_beg];
float mm = *ptr++;
for(i = b_beg +1 ; i < b_end; i++, ptr++)
if(mm < *ptr) mm = *ptr;
max[idx] = mm;
__syncthreads();
if(idx == 0){
mm = max[0];
for(i = 1; i < LBLKSZ; i++)
if(mm < max[i]) mm = max[i];
max[0] = mm;
}
__syncthreads();
ptr = &arr[b_beg];
mm = max[0];
if(mm != 0.f)
for(i = b_beg ; i < b_end; i++, ptr++) *ptr /= mm;
}
/*
__global__ void fill_zeros(float *arr, int arrsize, int W, int H){
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
if(x > W || y > H) return;
arr[x + y*W] = 0.f;
}*/
// kernel of function for sin/cos array initialisation
__global__ void fill_sincos(int angles,
float *Sin_d, float *Cos_d,
float anglestep, float conv){
int k = blockIdx.x * blockDim.x + threadIdx.x;
if(k >= angles) return;
float theta = ((float)k / anglestep - 90.f) * conv;
sincosf(theta, &Sin_d[k], &Cos_d[k]);
}
// initialisation of sin/cos arrays
extern "C" int init_sincos(int angles){
#undef RETMACRO
#define RETMACRO return 0
// the value reciprocal for angle step
float anglestep = (float)angles / 270.f;
float conv = M_PI / 180.f;
int blks = (angles + QBLKSZ - 1) / QBLKSZ;
int threads = LBLKSZ;
// first time we initialize arrays
if(!Sin_d || !Cos_d || angles != sincosize){
CUFREE(Cos_d);
CUFREE(Sin_d);
CUALLOC(Sin_d, angles*sizeof(float));
CUALLOC(Cos_d, angles*sizeof(float));
fill_sincos<<<blks, threads>>>(angles, Sin_d, Cos_d, anglestep, conv);
sincosize = angles;
}
cudaThreadSynchronize();
return 1;
#undef RETMACRO
#define RETMACRO return
}
/*
* Lines Hough transform kernel
* ima_d - device array with the image
* imW, imH, min, max - width, height of the image and extreme values of its histogram
* Sin_d, Cos_d - device array with sines and cosines of angles (-90..180degr increments 270/angles)
* Rmax - the maximum range for the desired lines
* angles - the number of angles in the range -90 .. 180
* treshold - lower threshold of intensity (in relative units: I=tres*(max-min)+min) for inclusion of point into array
* hough_d - output array with Hough transform
*/
__global__ void fill_lin_hough_array(float *ima_d,
int imW, int imH,
float min, float max,
float *Sin_d, float *Cos_d,
int Rmax, int angles, float treshold,
float *hough_d){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + imW * yi;
float x = (float)xi;
float y = (float)yi;
int k, R;
if(xi >= imW || yi >= imH) return;
float wd = max-min; if(wd == 0.f) wd = 1.f;
float ima = (ima_d[i]-min)/wd;
if(ima > treshold){
for(k = 0; k < angles; k++){
// R = x*cos(theta) + y*sin(theta)
R = (int)(0.5f + x * Cos_d[k] + y * Sin_d[k]);
// THIS IS VERY BAD, BUT atomicAdd doesn't work in old devices
if(R > 0 && R < Rmax) hough_d[R + Rmax*k] += ima;
//if(R > 0 && R < Rmax) atomicAdd(&hough_d[R + Rmax*k], ima);
}
}
}
/*
* Build Hough transform to find lines
* Input:
* ima - the image data
* min, max - range of the data in it
* imW, imH - image width and height
* Rmax - the maximum value of R
* Angles - the array size of angles (the angle of pitch is 180/angles degrees)
* Output:
* hough - array initialized by an external function,
* in which the Hough transform will be
* !!! array must be initialized with zeros before calling this function
* Output array is normalized to unity
*/
extern "C" int fill_hough_lines(float *ima, float min, float max, int imW, int imH,
int Rmax, int angles, float *hough){
#undef RETMACRO
#define RETMACRO do{ ret = 0; goto free_all; }while(0)
int sz, ret = 1;
int lblksz = LBLKSZ;
float *ima_d = NULL, *hough_d = NULL;
float treshold = 0.1f;
sz = imW * imH;
getprops();
dim3 blkdim(QBLKSZ, QBLKSZ);
dim3 griddim((imW+QBLKSZ-1)/QBLKSZ, (imH+QBLKSZ-1)/QBLKSZ);
// dim3 hgriddim((Rmax+QBLKSZ-1)/QBLKSZ, (angles+QBLKSZ-1)/QBLKSZ);
if(!init_sincos(angles)) RETMACRO;
CUALLOC(ima_d, sz*sizeof(float));
CUMOV2DEV(ima_d, ima, sz*sizeof(float));
sz = Rmax * angles;
CUALLOC(hough_d, sz*sizeof(float));
cudaMemset(hough_d, 0, sz*sizeof(float));
// fill_zeros<<<hgriddim, blkdim>>>(hough_d, sz, Rmax, angles);
cudaThreadSynchronize();
CUMOV2DEV(hough_d, hough, sz*sizeof(float));
fill_lin_hough_array<<<griddim, blkdim>>>(ima_d, imW,imH, min,max, Sin_d,Cos_d,
Rmax, angles, treshold, hough_d);
cudaThreadSynchronize();
normalize_vec<<<1, lblksz>>>(hough_d, sz);
cudaThreadSynchronize();
CUMOV2HOST(hough, hough_d, sz*sizeof(float));
free_all:
CUFREE(hough_d);
CUFREE(ima_d);
return ret;
#undef RETMACRO
#define RETMACRO return
}
/*
* Kernels of the threshold filtering
* in, out - in and out
* stepfn - a pointer to a function of conversion
* sizex, sizey - image size
* min - the minimum intensity
* wd - range of the data
* step - a step for stepfn
*/
// uniform intensity distribution
__global__ void Funiform(float *in, int sizex, int sizey, float min, float step){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + yi*sizex;
if(xi >= sizex || yi >= sizey) return;
in[i] = floor((in[i]-min)/step);
}
// logarithm distribution
__global__ void Flog(float *in, int sizex, int sizey, float min, float step){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + yi*sizex;
if(xi >= sizex || yi >= sizey) return;
in[i] = floor(logf(in[i]-min+1.f)/step);
}
// exponential distribution
__global__ void Fexp(float *in, int sizex, int sizey, float min, float wd, float step){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + yi*sizex;
if(xi >= sizex || yi >= sizey) return;
in[i] = floor(expf((in[i]-min)/wd)/step);
}
// distribution of a square root
__global__ void Fsqrt(float *in, int sizex, int sizey, float min, float step){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + yi*sizex;
if(xi >= sizex || yi >= sizey) return;
in[i] = floor(sqrtf(in[i]-min)/step);
}
// distribution of a x^2
__global__ void Fpow(float *in, int sizex, int sizey, float min, float step){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
int i = xi + yi*sizex;
if(xi >= sizex || yi >= sizey) return;
in[i] = floor((in[i]-min)*(in[i]-min)/step);
}
// functions for calculation of output scale
float Suniform(float in, float min, float wd, float step){
return step*in + min;
}
float Slog(float in, float min, float wd, float step){
return expf(in*step) + min - 1.f;
}
float Sexp(float in, float min, float wd, float step){
return wd*logf(in*step) + min;
}
float Ssqrt(float in, float min, float wd, float step){
return in*in*step*step + min;
}
float Spow(float in, float min, float wd, float step){
return sqrtf(in*step) + min;
}
/*
* Threshold filtering ("posterization")
* Input:
* ima - picture (free() must be executed in the caller)
* f - filter:
* f-> w - number of levels of posterization, [2.255]
* f-> h - type of posterization (0 - uniform)
* sizex, sizey - image size
* min, max - minimum and maximum intensity of the image
* Output:
* result - filtered image, the memory is allocated in this procedure
* scale - the scale of intensities, the memory is allocated here (if the scale!=NULL)
*
* TODO: save the result in the char, not float; learn display function
*/
extern "C" int StepFilter(float *ima, float **result,
Filter *f, int sizex, int sizey,
float min, float max, float **scale){
#undef RETMACRO
#define RETMACRO do{ ret = 0; goto free_all; }while(0)
int ret = 1;
float wd = max - min;
int y;
float Nsteps = (float)f->w; // number of intervals
float step;
float *in=NULL; // image and result array
int sz = sizex*sizey*sizeof(float);
dim3 blkdim(QBLKSZ, QBLKSZ);
dim3 griddim((sizex+QBLKSZ-1)/QBLKSZ, (sizey+QBLKSZ-1)/QBLKSZ);
*result = (float*)malloc(sz);
if(!result) RETMACRO;
float (*scalefn)(float,float,float,float);
if(f->w < 2 || f->w > 255) return 0;
if(wd == 0.f) return 0;
CUALLOC(in, sz);
CUMOV2DEV(in, ima, sz);
switch(f->h){ // filter type
case LOG: // logarithm
scalefn = Slog;
step = logf(max-min+1.f)/Nsteps;
Flog<<<griddim, blkdim>>>(in, sizex, sizey, min, step);
break;
case EXP: // exponential
scalefn = Sexp;
step = expf(1.f)/Nsteps;
Fexp<<<griddim, blkdim>>>(in, sizex, sizey, min, wd, step);
break;
case SQRT: // square root
scalefn = Ssqrt;
step = sqrtf(wd)/Nsteps;
Fsqrt<<<griddim, blkdim>>>(in, sizex, sizey, min, step);
break;
case POW: // power of two
scalefn = Spow;
step = wd*wd/Nsteps;
Fpow<<<griddim, blkdim>>>(in, sizex, sizey, min, step);
break;
default: // uniform
scalefn = Suniform;
step = wd/Nsteps;
Funiform<<<griddim, blkdim>>>(in, sizex, sizey, min, step);
}
cudaThreadSynchronize();
CUMOV2HOST(*result, in, sz);
if(scale){
int M = f->w;
*scale = (float*)calloc(M, sizeof(float));
if(*scale) for(y = 0; y < M; y++){
(*scale)[y] = scalefn(y+1,min,wd,step);
}
}
free_all:
CUFREE(in);
return ret;
#undef RETMACRO
#define RETMACRO return
}
/*
* A set of functions for constructing differential filters
*/
int p2oi(int i){
unsigned int v = (unsigned int)i - 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return (int) v;
}
int nextpow2(int i, int j){
int p1 = p2oi(i), p2 = p2oi(j);
return (p1 > p2)? p1 : p2;
}
// multiplication of two complex matrices with size x size
// result in the entry of the first matrix
__global__ void ComplexMul(cufftComplex *inout, cufftComplex *in, int size){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
if(xi >= size || yi >= size) return;
int i = xi + yi*size;
cufftComplex a = inout[i], b = in[i];
inout[i].x = a.x * b.x - a.y * b.y;
inout[i].y = a.x * b.y + a.y * b.x;
}
// restore coordinates of the Fourier transform
__global__ void fftshift(int size, float *m){
int h = size/2;
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
if(xi >= h || yi >= h) return;
// k - point in left upper quadrant, k1 - in right upper
int k = yi * size+xi, k1 = k + h;
// p - point in right lower quadrant, p1 - in left lower
int p = k + (size+1)*h, p1 = k1 + (size-1)*h;
float tmp;
tmp = m[k]; m[k] = m[p]; m[p] = tmp;
tmp = m[k1]; m[k1] = m[p1]; m[p1] = tmp;
}
// data copying float->cufftReal (need because different sizes of picture and Fourier image)
__global__ void f2r(cufftReal *out, float *in, int sizex, int sizey, int size2){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
if(xi >= sizex || yi >= sizey) return;
out[xi+yi*size2] = (cufftReal) in[xi+yi*sizex];
}
// data copying cufftReal->float
__global__ void r2f(float *out, cufftReal *in, int sizex, int sizey, int size2){
int xi = blockIdx.x * blockDim.x + threadIdx.x;
int yi = blockIdx.y * blockDim.y + threadIdx.y;
if(xi >= sizex || yi >= sizey) return;
out[xi+yi*sizex] = (float) in[xi+yi*size2];
}
/*
* The kernel of the Laplacian of Gaussian
* Output:
* mask - the filled array
* Input:
* size - array size (size x size)
* x0, x1 - array bounds on x: [x0, x1) (outside this array filled by zeros)
* y0, y1 - -//- on y
* half - half the size of the array
* ss - normalizing factor
* sx2, sy2 - the variance of the filter in x and y
*/
__global__ void LGf_kernel(cufftReal *mask, int size, int x0, int x1,
int y0, int y1, float half, float ss,
float sx2, float sy2){
int xi = blockIdx.x * blockDim.x + threadIdx.x + x0;
int yi = blockIdx.y * blockDim.y + threadIdx.y + y0;
if(xi >= x1 || yi >= y1) return;
int i = xi + yi*size;
float x2 = (float)xi + half;
float y2 = (float)yi + half;
x2 = x2*x2 / sx2; y2 = y2*y2 / sy2;
mask[i] = (cufftReal)(ss * ((x2-1.f)/sx2+(y2-1.f)/sy2)*expf(-(x2+y2)/2.f));
}
// The kernel of Gaussian filter
__global__ void Gf_kernel(cufftReal *mask, int size, int x0, int x1,
int y0, int y1, float half, float ss,
float sx2, float sy2){
int xi = blockIdx.x * blockDim.x + threadIdx.x + x0;
int yi = blockIdx.y * blockDim.y + threadIdx.y + y0;
if(xi >= x1 || yi >= y1) return;
int i = xi + yi*size;
float x2 = (float)xi + half;
float y2 = (float)yi + half;
x2 = x2*x2 / sx2; y2 = y2*y2 / sy2;
mask[i] = (cufftReal)(ss * expf(-(x2+y2)/2.f));
}
/*
* Building mask of Gaussian or Laplasian of Gaussian
* Output:
* mask - filter array
* Input:
* size - mask size (size x size)
* f - filter parameters
*/
void build_GLG_filter(cufftReal *mask, int size, Filter *f){
int y0=0,y1=size, x0=0, x1=size;
float sx2 = f->sx * f->sx, sy2 = f->sy * f->sy;
float half;
dim3 blkdim(QBLKSZ, QBLKSZ);
dim3 griddim((size+QBLKSZ-1)/QBLKSZ, (size+QBLKSZ-1)/QBLKSZ);
if(f->w < size && f->w > 0){
x0 = (size - f->w + 1) / 2;
x1 = x0 + f->w;
}
if(f->h < size && f->h > 0){
y0 = (size - f->h + 1) / 2;
y1 = y0 + f->h;
}
half = -(float)size / 2.f;
float ss = 3.f / half / half / sqrt(-half);
switch(f->FilterType){
case LAPGAUSS:
LGf_kernel<<<griddim, blkdim>>>(mask, size, x0,x1, y0,y1, half, ss, sx2, sy2);
break;
case GAUSS:
Gf_kernel<<<griddim, blkdim>>>(mask, size, x0,x1, y0,y1, half, ss, sx2, sy2);
break;
default:
fprintf(stderr, "Error: bad filter\n");
}
cudaThreadSynchronize();
DBG("size=%d, x0=%d,x1=%d, y0=%d,y1=%d, half=%g, ss=%g, sx2=%g, sy2=%g",
size, x0,x1, y0,y1, half, ss, sx2, sy2);
}
/*
* Building of elementary filter mask
* Output:
* mask - filter array
* Input:
* size - mask size (size x size)
* f - filter parameters
*/
void build_S_filter(cufftReal *mask, int size, Filter *f){
int y, a0, a1;
float hh, Y, pt = 0.f;
a0 = (size - 2) / 2;
a1 = a0 + 3;
hh = -(float)(size / 2);
float ss = 1.f / (M_PI*2.f) / hh / hh / sqrtf(-hh);
Y = -1.f;
for(y = a0; y < a1; y++, Y+=1.f){
float X = -1.f;
int str, x;
str = y * size;
for(x = a0; x < a1; x++, X+=1.f){
switch(f->FilterType){
case SOBELH:
pt = -X*(2.f-fabs(Y));
break;
case SOBELV:
pt = -Y*(2.f-fabs(X));
break;
case PREWITTH:
pt = X;
break;
case PREWITTV:
pt = Y;
break;
}
cufftReal tmppar = (cufftReal)ss*pt;
cudaMemcpy(&mask[str + x], &tmppar, sizeof(cufftReal), cudaMemcpyHostToDevice);
}
}
}
/*
* Convolution filtering (convolution by FFT)
* Input:
* ima - picture, that need to be filtering
* f - filter parameters
* Output:
* result - memory area, allocated by this function,
* where the filtered picture to be store
* return: TRUE if the filtering succeed
*/
extern "C" int DiffFilter(float *ima, float **result,
Filter *f, int sizex, int sizey){
#undef RETMACRO
#define RETMACRO do{ ret = 0; goto free_all; }while(0)
int ssize, ret = 0, size2;
float *tmp;
size2 = nextpow2(sizex, sizey);
ssize = size2 * size2; // Fourier image size
dim3 blkdim(QBLKSZ, QBLKSZ);
dim3 griddim((size2+QBLKSZ-1)/QBLKSZ, (size2+QBLKSZ-1)/QBLKSZ);
dim3 halfgriddim((size2/2+QBLKSZ-1)/QBLKSZ, (size2/2+QBLKSZ-1)/QBLKSZ);
dim3 imgriddim((sizex+QBLKSZ-1)/QBLKSZ, (sizey+QBLKSZ-1)/QBLKSZ);
if(!result || !*result || !ima || !f){
fprintf(stderr, "DiffFilter: bad parameters\n");
return 0;
}
cufftHandle plan;
cufftComplex *Fmask=NULL, *Fimg=NULL;
cufftReal *mask=NULL, *img=NULL, *resm=NULL;
#ifdef EBUG
getprops();
#endif
// Allocate memory for new objects
DBG("allocate");
CUALLOC(img, ssize*sizeof(cufftReal));
// fill it zeros
cudaMemset(img, 0, ssize*sizeof(cufftReal));
// copy ima -> img
DBG("copy image to dev");
CUALLOC(tmp, sizex*sizey*sizeof(float));
CUMOV2DEV(tmp, ima, sizex*sizey*sizeof(float));
f2r<<<imgriddim, blkdim>>>(img, tmp, sizex, sizey, size2);
cudaThreadSynchronize();
CUFREE(tmp);
CUALLOC(Fimg, ssize*sizeof(cufftComplex));
// make FFT
DBG("doing image FFT");
CUFFTCALL(cufftPlan2d(&plan, size2, size2, CUFFT_R2C));
CUFFTCALL(cufftExecR2C(plan, img, Fimg));
CUFREE(img);
DBG("allocate");
CUALLOC(mask, ssize*sizeof(cufftReal));
cudaMemset(mask, 0, ssize*sizeof(cufftReal));
CUALLOC(Fmask, ssize*sizeof(cufftComplex));
switch(f->FilterType){
case LAPGAUSS:
case GAUSS:
build_GLG_filter(mask, size2, f);
break;
case SOBELH:
case SOBELV:
case PREWITTH:
case PREWITTV:
build_S_filter(mask, size2, f);
break;
default:
fprintf(stderr, "Error: bad filter\n");
RETMACRO;
}
// swap filter quadrants
fftshift<<<halfgriddim, blkdim>>>(size2, mask);
cudaThreadSynchronize();
// make FFT
DBG("doing filter FFT");
CUFFTCALL(cufftExecR2C(plan, mask, Fmask));
CUFFTCALL(cufftDestroy(plan));
CUFREE(mask);
// make convolution in Fourier space
DBG("multiplication");
ComplexMul<<<griddim, blkdim>>>(Fimg, Fmask, size2);
cudaThreadSynchronize();
CUFREE(Fmask);
// Inverse FFT
DBG("doing inverse FFT");
CUALLOC(resm, ssize*sizeof(cufftReal));
CUFFTCALL(cufftPlan2d(&plan, size2, size2, CUFFT_C2R));
CUFFTCALL(cufftExecC2R(plan, Fimg, resm));
CUFFTCALL(cufftDestroy(plan));
CUFREE(Fimg);
DBG("allocate");
CUALLOC(tmp, ssize*sizeof(float));
*result = (float*)calloc(sizex*sizey, sizeof(float));
if(!*result) RETMACRO;
// copy iFFT -> res
DBG("copy to host");
r2f<<<imgriddim, blkdim>>>(tmp, resm, sizex, sizey, size2);
cudaThreadSynchronize();
CUMOV2HOST(*result, tmp, sizex*sizey*sizeof(float));
ret = 1;
free_all:
CUFREE(Fmask); CUFREE(Fimg); CUFREE(img);
CUFREE(mask); CUFREE(resm); CUFREE(tmp);
#ifdef EBUG
getprops();
#endif
return ret;
#undef RETMACRO
#define RETMACRO return
}
extern "C" int MedFilter(float *ima, float **result, Filter *f, int sizex, int sizey){return 0;}
extern "C" int GradFilterSimple(float *ima, float **result, Filter *f, int sizex, int sizey){return 0;}

816
src/NOCUDA.c Normal file
View File

@ -0,0 +1,816 @@
// NOCUDA.c - CPU-variants when there's no CUDA
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsview.h"
#include "gtk.h"
#include "CUtools.h"
#include <pthread.h>
#include <fftw3.h>
static float *Sin_d = NULL, *Cos_d = NULL;
static int sincosize = 0;
#ifndef THREAD_NUMBER
#define THREAD_NUMBER 2 // default - 2 threads
#endif
typedef struct{
float *image;
float min;
float max;
float *hough;
int w;
int Y0;
int Y1;
int Rmax;
int angles;
pthread_mutex_t *mutex;
}Hough_kernel_args;
// sin/cos ini, [-90,+180)
void init_sincos(int angles){
float anglestep = (float)angles / 270.; // step by an angle
float conv = M_PI / 180.;
float *cp, *sp;
int k;
if(!Sin_d || !Cos_d || angles != sincosize){ // first time initialize arrays
free(Sin_d);
free(Cos_d);
Sin_d = malloc(angles * sizeof(float));
Cos_d = malloc(angles * sizeof(float));
cp = Cos_d; sp = Sin_d;
for(k = 0; k < angles; k++){ // fill arrays from -90 to +90 degr in radians
float theta = ((float)k / anglestep - 90.) * conv;
*sp++ = sinf(theta);
*cp++ = cosf(theta);
//sincosf(theta, sp++, cp++);
}
sincosize = angles;
}
}
// Line Hough transform kernel
void *fill_lin_hough_array(void *data){
Hough_kernel_args *HD = (Hough_kernel_args*) data;
float *ima = HD->image, *hough = HD->hough;
float min = HD->min, wd = HD->max - min;
pthread_mutex_t *mutex = HD->mutex;
int Y0 = HD->Y0, Y1 = HD->Y1, imW = HD->w, Rmax = HD->Rmax, angles = HD->angles;
int i, j, k, Y, R;
for(j = Y0; j < Y1; j++){
for(i = 0.; i < imW; i++, ima++){
float imdata = (*ima - min) / wd;
if(imdata > 0.1){
Y = 0;
pthread_mutex_lock(mutex);
for(k = 0; k < angles; k++, Y+=Rmax){
// R = x*cos(theta) + y*sin(theta)
R = (int)(0.5 + i*Cos_d[k] + j*Sin_d[k]);
if(R > 0 && R < Rmax) hough[R + Y] += imdata;
}
pthread_mutex_unlock(mutex);
}
}
}
return NULL;
}
/*
* Build of a Hough transform to find lines
* Input:
* ima - the image data
* min, max - range of the data in it
* imW, imH - image width and height
* Rmax - the maximum value of R
* angles - the array size of angles (the angle step is 180/angles degrees)
* Output:
* hough - initialized by an external function array for Hough transform
* !!! array must be initialized with zeros before calling this function !!!
* Output array is normalized to unity
*/
int fill_hough_lines(float *ima, float min, float max, int imW, int imH, int Rmax, int angles, float *hough){
int i, Y0, Y1, dY;
float *hptr = hough, hmax;
Hough_kernel_args HD[THREAD_NUMBER];
pthread_t threads[THREAD_NUMBER];
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
init_sincos(angles);
dY = (imH + THREAD_NUMBER/2) / THREAD_NUMBER;
Y0 = 0; Y1 = dY;
for(i = 0; i < THREAD_NUMBER; i++, Y0+=dY, Y1+=dY){
if(Y0 >= imW) break;
if(Y1 > imW) Y1 = imW;
HD[i].min = min; HD[i].max = max;
HD[i].image = ima; HD[i].hough = hough;
HD[i].Y0 = Y0; HD[i].Y1 = Y1; HD[i].w = imW;
HD[i].Rmax = Rmax; HD[i].angles = angles;
HD[i].mutex = &mutex;
pthread_create(&threads[i], NULL, fill_lin_hough_array, &HD[i]);
}
for(i = 0; i < THREAD_NUMBER; i++)
pthread_join(threads[i], NULL);
pthread_mutex_destroy(&mutex);
hmax = *hough++;
Y1 = Rmax * angles;
for(i = 1; i < Y1; i++, hough++)
if(*hough > hmax) hmax = *hough;
hough = hptr;
for(i = 0; i < Y1; i++, hough++)
*hough /= hmax;
return 1;
}
// from http://graphics.stanford.edu/%7Eseander/bithacks.html#RoundUpPowerOf2
int nextpow2(int i, int j){
inline int p2oi(int i){
unsigned int v = (unsigned int)i - 1;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return (int) v;
}
int p1 = p2oi(i), p2 = p2oi(j);
return MAX(p1, p2);
}
// FFT
fftw_plan fftimg, fftmask, ifft;
// FFT buffrers for image & filter
fftw_complex *Fmask = NULL, // FFT of a filter
*Fimg = NULL; // picture FFT
static double *mask = NULL, // filter mask
*img = NULL, // picture
*resm = NULL;
void fftshift(int size, double *m){
int h = size/2, ss, ss1, i, j, k, l, p;
double tmp;
ss = (2*h+1)*h;
ss1 = (2*h-1)*h;
for(j = 0; j < h; j++){
k = j * size;
l = k + h;
for(i = 0; i < h; i++, k++, l++){
p = k + ss;
tmp = m[k]; m[k] = m[p]; m[p] = tmp;
p = l + ss1;
tmp = m[l]; m[l] = m[p]; m[p] = tmp;
}
}
}
// Lapgauss mask building
void build_LG_filter(int size, Filter *f){
int y, y0=0,y1=size, x0=0, x1=size;
double sx2 = f->sx * f->sx, sy2 = f->sy * f->sy;
double hw, hh;
#ifdef EBUG
double t0=dtime();
#endif
if(f->w < size && f->w > 0){
x0 = (size - f->w + 1) / 2;
x1 = x0 + f->w;
}
if(f->h < size && f->h > 0){
y0 = (size - f->h + 1) / 2;
y1 = y0 + f->h;
}
hh = -(double)size / 2.;
hw = -(double)size / 2.+(double)x0;
DBG("y0=%d, y1=%d, hw=%g, hh=%g",y0,y1,hw,hh);
double ss = 3. / hh / hh / sqrt(-hh);
// double ss = 1./sqrt(2*M_PI*f->sx*f->sy);
#pragma omp parallel for
for(y = y0; y < y1; y++){
double X, Y, y2, x2, R;
int str, x;
X = hw;
str = y * size;
Y = ((double)y) + hh;
y2 = Y*Y/sy2;
for(x = x0; x < x1; x++, X+=1.){
x2 = X*X/sx2;
R = x2 + y2;
mask[str + x] =ss * ((x2-1.)/sx2+(y2-1.)/sy2)*exp(-R/2.);
}
}
DBG("time=%f\n", dtime()-t0);
}
// Gaussian mask building/
void build_G_filter(int size, Filter *f){
int y, y0=0,y1=size, x0=0, x1=size;
double sx2 = f->sx * f->sx, sy2 = f->sy * f->sy;
double hw, hh;
#ifdef EBUG
double t0=dtime();
#endif
if(f->w < size && f->w > 0){
x0 = (size - f->w + 1) / 2;
x1 = x0 + f->w;
}
if(f->h < size && f->h > 0){
y0 = (size - f->h + 1) / 2;
y1 = y0 + f->h;
}
hh = -(double)size / 2.;
hw = -(double)size / 2.+(double)x0;
DBG("y0=%d, y1=%d, hw=%g, hh=%g",y0,y1,hw,hh);
double ss = 1. / (M_PI*2.) / sx2 / sy2 / hh / hh;
#pragma omp parallel for
for(y = y0; y < y1; y++){
double X, Y, y2, x2, R;
int str, x;
X = hw;
str = y * size;
Y = ((double)y) + hh;
y2 = Y*Y/sy2;
for(x = x0; x < x1; x++, X+=1.){
x2 = X*X/sx2;
R = x2 + y2;
mask[str + x] =ss * exp(-R/2.);
}
}
DBG("time=%f\n", dtime()-t0);
}
// Elementary filter mask building
void build_S_filter(int size, Filter *f){
int y, a0, a1;
double hh, Y, pt = 0.;
a0 = (size - 2) / 2;
a1 = a0 + 3;
hh = -(double)(size / 2);
double ss = 1. / (M_PI*2.) / hh / hh / sqrt(-hh);
Y = -1.;
for(y = a0; y < a1; y++, Y+=1.){
double X = -1.;
int str, x;
str = y * size;
for(x = a0; x < a1; x++, X+=1.){
switch(f->FilterType){
case SOBELH:
pt = -X*(2.-fabs(Y));
break;
case SOBELV:
pt = -Y*(2.-fabs(X));
break;
case PREWITTH:
pt = X;
break;
case PREWITTV:
pt = Y;
break;
/* case :
pt =
break;
case :
pt =
break;
case :
pt =
break;
case :
pt =
break;
case :
pt =
break;*/
}
mask[str + x] = ss*pt;
}
}
//mask[size*size/2]=0.;
}
/*
* Filtering by convolution with a filter
* Input:
* ima - a pointer to picture data, which should be filtered
* f - filter parameters
* Output:
* result - allocated by this function memory area where
* the filtered image is placed
* Returns TRUE, if the filter succeeded
*/
int DiffFilter(float *ima,
float **result,
Filter *f,
int sizex,
int sizey
){
int ssize, i, j, k, l;
static int fftw_ini = 0;
int size2;
#ifdef EBUG
double t0 = dtime();
#endif
size2 = nextpow2(sizex, sizey);
ssize = size2 * size2; // FFT image size
if(!fftw_ini)
if(!(fftw_ini = fftw_init_threads())){
//g_err(_("FFTW error");
return FALSE;
}
void free_all(){
_FREE(mask); _FREE(Fmask); _FREE(img);
_FREE(Fimg); _FREE(resm);
}
free_all();
DBG("img (%d x %d) -> (%d x %d), time=%f\n", sizex,sizey, size2,size2, dtime()-t0);
// allocate memory for objects
img = (double*)calloc(ssize, sizeof(double));
Fimg = (fftw_complex*)calloc(ssize, sizeof(fftw_complex));
if(!img || !Fimg){free_all(); return FALSE;}
// define direct FFT
fftw_plan_with_nthreads(THREAD_NUMBER);
fftimg = fftw_plan_dft_r2c_2d(size2, size2, img, Fimg, FFTW_ESTIMATE);
// copy ima -> img
for(j = 0; j < sizey; j++){
k = j * size2;
l = j * sizex;
for(i = 0; i < sizex; i++, k++, l++)
img[k] = ima[l];
}
fftw_execute(fftimg);
_FREE(img);
// build filter
DBG("build filter, time=%f\n", dtime()-t0);
mask = (double*)calloc(ssize, sizeof(double));
Fmask = (fftw_complex*)calloc(ssize, sizeof(fftw_complex));
if(!mask || !Fmask){free_all(); return FALSE;}
switch(f->FilterType){
case LAPGAUSS:
build_LG_filter(size2, f);
break;
case GAUSS:
build_G_filter(size2, f);
break;
case SOBELH:
case SOBELV:
case PREWITTH:
case PREWITTV:
build_S_filter(size2, f);
break;
}
// define filter FFT
fftw_plan_with_nthreads(THREAD_NUMBER);
fftmask = fftw_plan_dft_r2c_2d(size2, size2, mask, Fmask, FFTW_ESTIMATE);
fftw_execute(fftmask);
_FREE(mask);
// filtered picture:
DBG("filter image, time=%f\n", dtime()-t0);
resm = (double*)calloc(ssize, sizeof(double));
if(!resm){free_all(); return FALSE;}
// define inverse FFT
fftw_plan_with_nthreads(THREAD_NUMBER);
ifft = fftw_plan_dft_c2r_2d(size2, size2, Fimg, resm, FFTW_ESTIMATE);
// DON'T PARALLEL THIS, it will be slower
for(i=0; i<ssize; i++){ // convolution by multiplication in Fourier space
/**/
double a,b,c,d;
a=Fimg[i][0]; c=Fmask[i][0];
b=Fimg[i][1]; d=Fmask[i][1];
Fimg[i][0] = a*c - b*d;
Fimg[i][1] = b*c + a*d;
/**
Fimg[i][0] = Fmask[i][0];
Fimg[i][1] = Fmask[i][1];
**/
}
_FREE(Fmask);
fftw_execute(ifft);
_FREE(Fimg);
fftshift(size2, resm);
*result = calloc(ssize, sizeof(float));
if(!*result){free_all(); return FALSE;}
float *tmp = *result;
// copy inv FFT -> result
for(j = 0; j < sizey; j++){
k = j * size2;
l = j * sizex;
for(i = 0; i < sizex; i++, k++, l++)
tmp[l] = resm[k];
}
_FREE(resm);
fftw_destroy_plan(fftimg);
fftw_destroy_plan(ifft);
fftw_destroy_plan(fftmask);
fftw_cleanup_threads();
DBG("time=%f\n", dtime()-t0);
return TRUE;
}
/*
* Simple gradient filter based on two Sobel filters
* output = sqrt(SobelH(input)^2+SobelV(input)^2)
*/
int GradFilterSimple(float *ima, float **result, Filter *f, int w, int h){
float *dst1, *dst2;
int res = FALSE, y;
#ifdef EBUG
double t0 = dtime();
#endif
f->FilterType = SOBELH;
res = DiffFilter(ima, &dst1, f, w, h);
if(!res) return FALSE;
f->FilterType = SOBELV;
res = DiffFilter(ima, &dst2, f, w, h);
if(!res){free(dst1); return FALSE;}
*result = dst1;
#pragma omp parallel for
for(y = 0; y < h; y++){
int x;
float *in, *out;
in = dst2 + y*w;
out = dst1 + y*w;
for(x = 0; x < w; x++, in++, out++)
*out = sqrtf((*in)*(*in) + (*out)*(*out));
}
free(dst2);
DBG("time=%f\n", dtime()-t0);
return TRUE;
}
/*
* Quick median functions stolen from
*
* Fast median search: an ANSI C implementation
* Nicolas Devillard - ndevilla AT free DOT fr
* July 1998
*/
#define PIX_SORT(a,b) { if ((*a)>(*b)) ELEM_SWAP((a),(b)); }
#define ELEM_SWAP(a,b) { register float *t=(a);(a)=(b);(b)=t; }
float opt_med3(float **p, int n __attribute__((unused))){
PIX_SORT(p[0],p[1]) ; PIX_SORT(p[1],p[2]) ; PIX_SORT(p[0],p[1]) ;
return(*p[1]) ;
}float opt_med5(float **p, int n __attribute__((unused))){
PIX_SORT(p[0],p[1]) ; PIX_SORT(p[3],p[4]) ; PIX_SORT(p[0],p[3]) ;
PIX_SORT(p[1],p[4]) ; PIX_SORT(p[1],p[2]) ; PIX_SORT(p[2],p[3]) ;
PIX_SORT(p[1],p[2]) ; return(*p[2]) ;
}float opt_med7(float **p, int n __attribute__((unused))){
PIX_SORT(p[0], p[5]) ; PIX_SORT(p[0], p[3]) ; PIX_SORT(p[1], p[6]) ;
PIX_SORT(p[2], p[4]) ; PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[5]) ;
PIX_SORT(p[2], p[6]) ; PIX_SORT(p[2], p[3]) ; PIX_SORT(p[3], p[6]) ;
PIX_SORT(p[4], p[5]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[1], p[3]) ;
PIX_SORT(p[3], p[4]) ; return (*p[3]) ;
}float opt_med9(float **p, int n __attribute__((unused))){
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
PIX_SORT(p[4], p[2]) ; return(*p[4]) ;
} float opt_med25(float **p, int n __attribute__((unused))){
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[2], p[4]) ;
PIX_SORT(p[2], p[3]) ; PIX_SORT(p[6], p[7]) ; PIX_SORT(p[5], p[7]) ;
PIX_SORT(p[5], p[6]) ; PIX_SORT(p[9], p[10]) ; PIX_SORT(p[8], p[10]) ;
PIX_SORT(p[8], p[9]) ; PIX_SORT(p[12], p[13]); PIX_SORT(p[11], p[13]) ;
PIX_SORT(p[11], p[12]); PIX_SORT(p[15], p[16]); PIX_SORT(p[14], p[16]) ;
PIX_SORT(p[14], p[15]); PIX_SORT(p[18], p[19]); PIX_SORT(p[17], p[19]) ;
PIX_SORT(p[17], p[18]); PIX_SORT(p[21], p[22]); PIX_SORT(p[20], p[22]) ;
PIX_SORT(p[20], p[21]); PIX_SORT(p[23], p[24]); PIX_SORT(p[2], p[5]) ;
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[0], p[6]) ; PIX_SORT(p[0], p[3]) ;
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[1], p[7]) ; PIX_SORT(p[1], p[4]) ;
PIX_SORT(p[11], p[14]); PIX_SORT(p[8], p[14]) ; PIX_SORT(p[8], p[11]) ;
PIX_SORT(p[12], p[15]); PIX_SORT(p[9], p[15]) ; PIX_SORT(p[9], p[12]) ;
PIX_SORT(p[13], p[16]); PIX_SORT(p[10], p[16]); PIX_SORT(p[10], p[13]) ;
PIX_SORT(p[20], p[23]); PIX_SORT(p[17], p[23]); PIX_SORT(p[17], p[20]) ;
PIX_SORT(p[21], p[24]); PIX_SORT(p[18], p[24]); PIX_SORT(p[18], p[21]) ;
PIX_SORT(p[19], p[22]); PIX_SORT(p[8], p[17]) ; PIX_SORT(p[9], p[18]) ;
PIX_SORT(p[0], p[18]) ; PIX_SORT(p[0], p[9]) ; PIX_SORT(p[10], p[19]) ;
PIX_SORT(p[1], p[19]) ; PIX_SORT(p[1], p[10]) ; PIX_SORT(p[11], p[20]) ;
PIX_SORT(p[2], p[20]) ; PIX_SORT(p[2], p[11]) ; PIX_SORT(p[12], p[21]) ;
PIX_SORT(p[3], p[21]) ; PIX_SORT(p[3], p[12]) ; PIX_SORT(p[13], p[22]) ;
PIX_SORT(p[4], p[22]) ; PIX_SORT(p[4], p[13]) ; PIX_SORT(p[14], p[23]) ;
PIX_SORT(p[5], p[23]) ; PIX_SORT(p[5], p[14]) ; PIX_SORT(p[15], p[24]) ;
PIX_SORT(p[6], p[24]) ; PIX_SORT(p[6], p[15]) ; PIX_SORT(p[7], p[16]) ;
PIX_SORT(p[7], p[19]) ; PIX_SORT(p[13], p[21]); PIX_SORT(p[15], p[23]) ;
PIX_SORT(p[7], p[13]) ; PIX_SORT(p[7], p[15]) ; PIX_SORT(p[1], p[9]) ;
PIX_SORT(p[3], p[11]) ; PIX_SORT(p[5], p[17]) ; PIX_SORT(p[11], p[17]) ;
PIX_SORT(p[9], p[17]) ; PIX_SORT(p[4], p[10]) ; PIX_SORT(p[6], p[12]) ;
PIX_SORT(p[7], p[14]) ; PIX_SORT(p[4], p[6]) ; PIX_SORT(p[4], p[7]) ;
PIX_SORT(p[12], p[14]); PIX_SORT(p[10], p[14]); PIX_SORT(p[6], p[7]) ;
PIX_SORT(p[10], p[12]); PIX_SORT(p[6], p[10]) ; PIX_SORT(p[6], p[17]) ;
PIX_SORT(p[12], p[17]); PIX_SORT(p[7], p[17]) ; PIX_SORT(p[7], p[10]) ;
PIX_SORT(p[12], p[18]); PIX_SORT(p[7], p[12]) ; PIX_SORT(p[10], p[18]) ;
PIX_SORT(p[12], p[20]); PIX_SORT(p[10], p[20]); PIX_SORT(p[10], p[12]) ;
return (*p[12]);
}
float quick_select(float **arr, int n){
int low, high;
int median;
int middle, ll, hh;
float ret;
low = 0 ; high = n-1 ; median = (low + high) / 2;
for(;;){
if(high <= low) /* One element only */
break;
if(high == low + 1){ /* Two elements only */
PIX_SORT(arr[low], arr[high]) ;
break;
}
/* Find median of low, middle and high items; swap into position low */
middle = (low + high) / 2;
PIX_SORT(arr[middle], arr[high]) ;
PIX_SORT(arr[low], arr[high]) ;
PIX_SORT(arr[middle], arr[low]) ;
/* Swap low item (now in position middle) into position (low+1) */
ELEM_SWAP(arr[middle], arr[low+1]) ;
/* Nibble from each end towards middle, swapping items when stuck */
ll = low + 1;
hh = high;
for(;;){
do ll++; while (*arr[low] > *arr[ll]);
do hh--; while (*arr[hh] > *arr[low]);
if(hh < ll) break;
ELEM_SWAP(arr[ll], arr[hh]) ;
}
/* Swap middle item (in position low) back into correct position */
ELEM_SWAP(arr[low], arr[hh]) ;
/* Re-set active partition */
if (hh <= median) low = ll;
if (hh >= median) high = hh - 1;
}
ret = *arr[median];
return ret;
}
#undef PIX_SORT
#undef ELEM_SWAP
/*
#define _0(x) (x & 0x7FF) // lower 11 bits
#define _1(x) (x >> 11 & 0x7FF) // middle 11 bits
#define _2(x) (x >> 22 ) // upper 10 bits
float hist_select(float **arr, int n){
inline uint32_t FloatFlip(uint32_t f){
uint32_t mask = -((int32_t)(f >> 31)) | 0x80000000;
return f ^ mask;
}
inline uint32_t IFloatFlip(uint32_t f){
uint32_t mask = ((f >> 31) - 1) | 0x80000000;
return f ^ mask;
}
uint32_t b0
}
#undef _0
#undef _1
#undef _2
*/
/*
* Median filtering
* Input:
* ima - picture (free() should be in a caller)
* f - filter
* sizex, sizey - picture size
* Output:
* result - filtered picture, memory allocates in this routine.
* Image borders that didn't pass through the filter (+- 1/2 of filter size)
* filled zeros
*/
int MedFilter(float *ima,
float **result,
Filter *f,
int sizex,
int sizey
){
int xlow = f->w / 2, xhigh = f->w - xlow;// filter area borders [x0-xlow, x0+xhigh)
int ylow = f->h / 2, yhigh = f->h - ylow; // [y0-ylow, y0+yhigh)
int x, xm=sizex-xhigh, ym=sizey-yhigh; // xm,ym - upper boundaries if filtered picture
int ssize=sizex*sizey, fsz=f->w*f->h, H = f->h - 1;
#ifdef EBUG
double t0 = dtime();
#endif
float (*medfn)(float **p, int n);
*result = calloc(ssize, sizeof(float));
if(!result) return FALSE;
switch(f->w*f->h){
case 3:
medfn = opt_med3;
break;
case 5:
medfn = opt_med5;
break;
case 7:
medfn = opt_med7;
break;
case 9:
medfn = opt_med9;
break;
case 25:
medfn = opt_med25;
break;
default:
medfn = quick_select;
break;
}
/*
* Selection of picture elements on the square wxh into an array arr
* x0, y0 - coordinates of the center of the square
* cntr - current replacement string, or -1 if it is necessary to fill the entire arr
* If cntr!=-1 next line replaces a string cntr
*/
float selarr0(int x0, int y0, float *arr, float **sel){
int x,y,tx,ty;
float *tmp, *ptr;
tx=x0+xhigh; ty=y0+yhigh;
tmp = arr;
for(y=y0-ylow; y<ty; y++){ // completely fill the array
ptr = &ima[y*sizex+x0-xlow]; // FILL BY LINES!!!
for(x=x0-xlow; x<tx; x++)
*tmp++ = *ptr++;
}
return medfn(sel, fsz);
}
float selarr1(int x0, int y0, float *arr, float **sel, int *cntr){
int x,tx,ty;
float *tmp, *ptr;
tx=x0+xhigh; ty=y0+yhigh;
tmp = &arr[*cntr*f->w]; // pointer to a changed line
ptr = &ima[(ty-1)*sizex+x0-xlow];
for(x=x0-xlow; x<tx; x++) // change data in column
*tmp++ = *ptr++;
if(++(*cntr) > H) *cntr = 0;
return medfn(sel, fsz);
}
#pragma omp parallel
{
float *arr = calloc(fsz, sizeof(float*)); // array to sample storage
float **sel = calloc(fsz, sizeof(float*));// array to sorted sample storage
for(x = 0; x < fsz; x++) sel[x] = arr + x;
if(arr){
#pragma omp for
for(x = xlow; x < xm; x++){
int y, cntr = 0;
(*result)[ylow*sizex + x] = selarr0(x,ylow, arr, sel);
for(y = ylow+1; y < ym; y++){
(*result)[y*sizex + x] = selarr1(x,y, arr, sel, &cntr);
}
}
free(arr);
free(sel);
}
}
DBG("time=%f\n", dtime()-t0);
return TRUE;
}
/*
* Fill isolines' scale (an array)
* Input:
* f - filter for given method
* min - minimum value of intensity
* wd - max-min (dinamic range)
* Output:
* scale - a pointer to array (allocated in this function)
*/
int fillIsoScale(Filter *f, float **scale, float min, float wd){
int M = f->w, y;
float (*scalefn)(float in);
float step, Nsteps = (float)f->w; // amount of intervals
float Suniform(float in){
return step*in + min;
}
float Slog(float in){
return expf(in*step) + min - 1.;
}
float Sexp(float in){
return wd*logf(in*step) + min;
}
float Ssqrt(float in){
return in*in*step*step + min;
}
float Spow(float in){
return sqrtf(in*step) + min;
}
if(!scale) return FALSE;
*scale = calloc(M, sizeof(float));
if(!*scale) return FALSE;
switch(f->h){
case LOG:
scalefn = Slog; step = logf(wd+1.)/Nsteps;
break;
case EXP:
scalefn = Sexp; step = expf(1.)/Nsteps;
break;
case SQRT:
scalefn = Ssqrt; step = sqrtf(wd)/Nsteps;
break;
case POW:
scalefn = Spow; step = wd*wd/Nsteps;
break;
default:
scalefn = Suniform; step = wd/Nsteps;
}
for(y = 0; y < M; y++){
(*scale)[y] = scalefn(y+1);
DBG("level %d: I=%g", y, (*scale)[y]);
}
return TRUE;
}
/*
* Threshold filtering ("posterization")
* Input:
* ima - picture (free() must be executed in the caller)
* f - filter:
* f-> w - number of levels of posterization, [2.255]
* f-> h - type of posterization (0 - uniform)
* sizex, sizey - image size
* min, max - minimum and maximum intensity of the image
* Output:
* result - filtered image, the memory is allocated in this procedure
* scale - the scale of intensities, the memory is allocated here (if the scale!=NULL)
*
* TODO: save the result in the char, not float; learn display function
*/
int StepFilter(float *ima, float **result, Filter *f, int sizex, int sizey,
float min, float max, float **scale){
if(f->w < 2 || f->w > 255) return FALSE;
int y;
float Nsteps = (float)f->w; // amount of intervals
float step;
float wd = max - min;
if(fabs(wd) < FLT_EPSILON) return FALSE;
float (*stepfn)(float in);
float Funiform(float in){
return floor((in-min)/step);
}
float Flog(float in){
return floor(logf(in-min+1.)/step);
}
float Fexp(float in){
return floor(expf((in-min)/wd)/step);
}
float Fsqrt(float in){
return floor(sqrtf(in-min)/step);
}
float Fpow(float in){
return floor((in-min)*(in-min)/step);
}
#ifdef EBUG
double t0 = dtime();
#endif
switch(f->h){
case LOG:
stepfn = Flog;
step = logf(max-min+1.)/Nsteps;
break;
case EXP:
stepfn = Fexp;
step = expf(1.)/Nsteps;
break;
case SQRT:
stepfn = Fsqrt;
step = sqrtf(wd)/Nsteps;
break;
case POW:
stepfn = Fpow;
step = wd*wd/Nsteps;
break;
default:
stepfn = Funiform;
step = wd/Nsteps;
}
*result = calloc(sizex*sizey, sizeof(float));
if(!result) return FALSE;
#pragma omp parallel for
for(y = 0; y < sizey; y++){
int x; float *out, *in;
in = ima + y*sizex; out = *result + y*sizex;
for(x = 0; x < sizex; x++, in++, out++){
*out = stepfn(*in);
}
}
if(scale && !fillIsoScale(f, scale, min, wd)) g_err(_("No memory left"));
DBG("SF: %d sublevels, step=%g, time=%f\n", f->w, step, dtime()-t0);
return TRUE;
}

506
src/contours.c Normal file
View File

@ -0,0 +1,506 @@
// contours.c - find isophotos
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "contours.h"
#include "opengl.h"
// contours' minimum size limits
const int MIN_CONTOUR_SIZE = 4;
const int MIN_CLOSED_CONTOUR_SIZE = 7;
// maximum amount of contour levels
const int MAX_CONTOUR_LEVELS = 255;
// frees contour's data
void free_contour(Contour **c){
cPoint *pp, *p;
p = (*c)->first;
while(p){ // go through all points of current contour
pp = p; p = p->next;
_FREE(pp);
}
_FREE(*c);
}
// delete contours' list for the picture
void free_contours(IMAGE *image){
FNAME();
int i;
cList *l;
Contour *cp, *c;
if(image->Ncontours < 1 || !image->contours) return;
for(i = 0; i < image->Ncontours; i++){ // go through all levels
l = image->contours[i];
c = l->first;
while(c){ // go throug all contours of current level
cp = c; c = c->next;
free_contour(&cp);
}
}
}
// create new element of contours' list
cList *new_clist(int lvl){
DBG("level: %d", lvl);
cList *c = calloc(1, sizeof(cList));
c->L = lvl;
return c;
}
// create new contour
Contour *new_contour(){
Contour *c = calloc(1, sizeof(Contour));
return c;
}
// create new point
cPoint *new_point(){
cPoint *p = calloc(1, sizeof(cPoint));
return p;
}
// add contour c to list
void cList_add(cList *cl, Contour *c){
cl->N++;
//DBG("contour #%d added to list with levl %d", cl->N, cl->L);
if(!cl->first)
cl->first = c;
else
cl->last->next = c;
cl->last = c;
}
// add point p to contour c
// headflag == 0 - add to tail; ==1 - add to head
void c_add(Contour *c, cPoint *p, char headflag){
c->N++;
if(!c->first || !c->last){
c->first = p;
c->last = p;
}else{
if(headflag){ // add point to head
c->first->prev = p;
p->next = c->first;
c->first = p;
}
else{
c->last->next = p; // add point to tail
p->prev = c->last;
c->last = p;
}
}
}
/*
* copy contours from image in to image out
* with shift (dX, dY): Xnew=Xold+dX, Ynew=Yold+dY
*/
int copy_contours(IMAGE *in, IMAGE *out, float dX, float dY){
int i, N = in->Ncontours;
cList *l;
cPoint *pCur, *p;
Contour *cCur, *c;
if(N < 1 || !N) return TRUE;
if(!(out->contours = calloc(in->Ncontours, sizeof(Contour)))) return FALSE;
for(i = 0; i < N; i++){ // go through all levels
l = in->contours[i];
if(!(out->contours[i] = new_clist(i))) goto badExit;
c = l->first;
while(c){ // go throug all contours of current level
if(!(cCur = new_contour())) goto badExit;
pCur = c->first;
while(pCur){
if(!(p = new_point())) goto badExit;
p->x = pCur->x+dX; p->y = pCur->y+dY;
c_add(cCur, p, 0);
pCur = pCur->next;
}
cList_add(out->contours[i], cCur);
c = c->next;
}
}
out->Ncontours = in->Ncontours;
return TRUE;
badExit:
free_contours(out); return FALSE;
}
// direction bits
enum{
D_RIGHT = 1
,D_LEFT = 2
,D_DOWN = 4
,D_UP = 8
};
// directions of next moving for mask values
// spetial values 6 & 9 also checks separately
int directions[16] = {
0, // no isoline
D_RIGHT | D_DOWN,
D_LEFT | D_DOWN,
D_RIGHT | D_LEFT,
D_RIGHT | D_UP,
D_UP | D_DOWN,
0, // special point 6
D_LEFT | D_UP,
D_LEFT | D_UP,
0, // special point 9
D_UP | D_DOWN,
D_RIGHT | D_UP,
D_RIGHT | D_LEFT,
D_LEFT | D_DOWN,
D_RIGHT | D_DOWN,
0 // no isoline
};
/*
* coordinate shifts {dx, dy} according to direction value bits
* their values are choosen so, that even if there would be some non-zero
* bits in direction mask, new direction of isoline search will be choosen
* as possible nearest to "right" or " down" direction
*/
int dxdy[9][2] = {
{0 , 0}, // 0
{1 , 0}, // D_RIGHT
{-1, 0}, // D_LEFT
{0 , 0},
{0 , 1}, // D_DOWN
{0 , 0},
{0 , 0},
{0 , 0},
{0 ,-1} // D_UP
};
// new directions in simplex case, 2nd dimension - head flag (if==1, rotate CCW)
int newdirs[16][2] = {
{0, 0}, // 0
{D_RIGHT,D_RIGHT},// D_RIGHT
{D_LEFT, D_LEFT}, // D_LEFT
{D_RIGHT, D_LEFT},// D_RIGHT | D_LEFT
{D_DOWN, D_DOWN}, // D_DOWN
{D_RIGHT, D_DOWN},// D_DOWN | D_RIGHT
{D_DOWN, D_LEFT}, // D_DOWN | D_LEFT
{D_RIGHT, D_LEFT},// D_DOWN | D_RIGHT | D_LEFT
{D_UP, D_UP}, // D_UP
{D_RIGHT, D_UP}, // D_UP | D_RIGHT
{D_LEFT, D_UP}, // D_UP | D_LEFT
{D_RIGHT, D_UP}, // D_UP | D_RIGHT | D_LEFT
{D_DOWN, D_UP}, // D_UP | D_DOWN
{D_RIGHT, D_UP}, // D_UP | D_DOWN | D_RIGHT
{D_DOWN, D_UP}, // D_UP | D_DOWN | D_LEFT
{D_RIGHT, D_UP} // D_UP | D_DOWN | D_RIGHT | D_LEFT
};
/*
* Functions that calculate the position of the contour (linear interpolation)
* P - the point at which are recorded the coordinates (relative to the center of the upper left quadrant)
* to the resulting coordinates p shold be added the coordinates of the square to obtain the coordinates in image SC
* A, b, c, d - intensity in the UL, UR DL and DR corners of the square
* Lvl - contours level
*/
// right & up borders
void getRU(cPoint *p, float a, float b, float c __attribute__((unused)), float d, float I){
float x = 0., y = 0.;
if(fabs(b-a) > FLT_EPSILON) x = (I-a)/(b-a);
if(fabs(d-b) > FLT_EPSILON) y = (I-b)/(d-b);
p->x = x/2.+0.5; p->y = y/2.;
}
// left & up
void getLU(cPoint *p, float a, float b, float c, float d __attribute__((unused)), float I){
float x = 0., y = 0.;
if(fabs(b-a) > FLT_EPSILON) x = (I-a)/(b-a);
if(fabs(c-a) > FLT_EPSILON) y = (I-a)/(c-a);
p->x = x/2.; p->y = y/2.;
}
// left & down
void getLD(cPoint *p, float a, float b __attribute__((unused)), float c, float d, float I){
float x = 0., y = 0.;
if(fabs(d-c) > FLT_EPSILON) x = (I-c)/(d-c);
if(fabs(c-a) > FLT_EPSILON) y = (I-a)/(c-a);
p->x = x/2.; p->y = y/2.+0.5;
}
// right & down
void getRD(cPoint *p, float a __attribute__((unused)), float b, float c, float d, float I){
float x = 0., y = 0.;
if(fabs(d-c) > FLT_EPSILON) x = (I-c)/(d-c);
if(fabs(d-b) > FLT_EPSILON) y = (I-b)/(d-b);
p->x = x/2.+0.5; p->y = y/2.+0.5;
}
// up & down
void getUD(cPoint *p, float a, float b, float c, float d, float I){
float x1 = 0., x2 = 1.;
if(fabs(d-c) > FLT_EPSILON) x1 = (I-c)/(d-c);
if(fabs(b-a) > FLT_EPSILON) x2 = (I-a)/(b-a);
p->x = (x1+x2)/2.; p->y = 0.5;
}
// right & left
void getRL(cPoint *p, float a, float b, float c, float d, float I){
float y1 = 0., y2 = 1.;
if(fabs(d-b) > FLT_EPSILON) y1 = (I-b)/(d-b);
if(fabs(c-a) > FLT_EPSILON) y2 = (I-a)/(c-a);
p->x = 0.5; p->y = (y1+y2)/2.;
}
// bung for a case
void getNull(cPoint *p __attribute__((unused)), float a __attribute__((unused)),
float b __attribute__((unused)), float c __attribute__((unused)),
float d __attribute__((unused)), float I __attribute__((unused))){
p->x = 0.; p->y = 0.;
}
// array of a funtions to coordinates calculation
typedef void (*FnXY)(cPoint *p, float a, float b, float c, float d, float I);
FnXY getXY[16] = {
getNull, // 0
getRD,
getLD,
getRL,
getRU,
getUD,
getNull,//6 must be getLU or get RD
getLU,
getLU,
getNull,//9 must be getLD or get RU
getUD,
getRU,
getRL,//12
getLD,
getRD,
getNull
};
static float *isoscale = NULL;
static int w, w1, h, h1;
static cList **contours = NULL;
/*
* filling of the contour line, beginning at the point x1, y1
* flag==1 - add points not to the tail of the circuit but to the head
* cCur - contour to add the point
* imdata - the original picture
* lvl - isoline level number
* olddir - direction (D_...) to a previous point
* mask - pointer to current mask
*/
int proc_contour(int x1, int y1, int flag,
Contour *cCur, float *imdata,
int lvl, int olddir,
unsigned char *mask){
int frst = flag;
float Lvl = isoscale[lvl]; // intensity at isoline level lvl
float *point;
int newdir;
do{
int mid = y1*w1 + x1, iid = y1*w + x1;
unsigned char m = mask[mid];
if(m == 0 || m > 14) break;
if(!frst){
cPoint *p = new_point();
if(!p) return FALSE;
c_add(cCur, p, flag);
// get coordinates p->x, p->y (by simplified linear approximation)
point = imdata + iid; // LU pixel for a square
// find coordinates
switch(m){ // check special points 6 & 9, their values depends on olddir
case 6: // m = 7/14, mask=14/7
switch(olddir){
case D_LEFT: // next - UP or LEFT
case D_UP:
m = 7; mask[mid] = 14; // clear only used point
break;
default: // D_DOWN/D_RIGHT, next - RIGHT or UP
m = 14; mask[mid] = 7;
}
break;
case 9: // m = 13/11, mask=11/13
switch(olddir){
case D_LEFT: // next - DOWN or LEFT
case D_DOWN:
m = 13; mask[mid] = 11; // clear only used point
break;
default: // D_UP/D_RIGHT, next - RIGHT or DOWN
m = 11; mask[mid] = 13;
}
break;
default:
mask[mid] = 0; // just clear used mask's point
}
getXY[m](p,point[0],point[1],point[w],point[w+1],Lvl);
if(p->x < -0.5 || p->x > 1.5) p->x = 0.5;
if(p->y < -0.5 || p->y > 1.5) p->y = 0.5;
// add .5, beacause of mask's shift
p->x += (float)x1 + .5;
p->y += (float)y1 + .5;
}
gboolean found = FALSE; // whether next isoline point was found
int x2, y2;
do{ // find next contour's point
unsigned char pp;
// check right new direction
newdir = newdirs[directions[m] & (~olddir)][flag];
//newdir = directions[m] & (~olddir);
if(newdir > 8){DBG("WTF? m=%d, newdir=%d, olddir=%d",m,newdir, olddir); break;} // This can't be so, but WTF if?
x2 = x1 + dxdy[newdir][0];
y2 = y1 + dxdy[newdir][1];
if(x1 == x2 && y1 == y2) break;
// is next square out of an picture?
if(x2 < 0 || x2 >= w1 || y2 < 0 || y2 >= h1) break;
int ii = y2*w1+x2;
pp = mask[ii];
// has the next square a contour points?
if(pp == 0 || pp > 14) break;
found = TRUE;
}while(0);
x1 = x2; y1 = y2;
if(!found){
break; // end of isoline
}
switch(newdir){ // calculate old direction
case D_LEFT: olddir = D_RIGHT; break;
case D_RIGHT: olddir = D_LEFT; break;
case D_UP: olddir = D_DOWN; break;
case D_DOWN: olddir = D_UP; break;
default: olddir = 0;
}
frst = FALSE;
}while(1); // cycle through the contour
return TRUE;
}
/*
* Processing of the point with coordinates x, y
* imdata - the original picture
* lvl - number of contour's level
* mask - current mask
* return FALSE if failed
*/
int process_it_(int x, int y, int lvl, float *imdata, unsigned char *mask){
Contour *cCur;
unsigned char pt0 = mask[y*w1+x];
if(pt0 == 0 || pt0 > 14) return TRUE;;
do{
if(!contours[lvl]){ // countour wasn't created - create it
contours[lvl] = new_clist(lvl);
if(!contours[lvl]){
g_err(_("Can't allocate memory"));
return FALSE;
}
}
cCur = new_contour();
if(!cCur){
g_err(_("Can't allocate memory"));
return FALSE;
}
// start processing contour, adding points to its tail
// D_LEFT - beacause we check points from left to the right
if(!proc_contour(x, y, 0, cCur, imdata, lvl, D_LEFT, mask))
return FALSE;
// continue processing contour, adding points to its head
// D_UP - beacause there's no more contour points upper
// if((y + 1) < w1) // mask(x,y) == 0, so we check down point
// if(!proc_contour(x, y+1, 1, cCur, imdata, lvl, D_UP, mask))
// return FALSE;
if(cCur->N < MIN_CONTOUR_SIZE){ // contour is too short
free_contour(&cCur);
break;
}
if( (fabs(cCur->first->x - cCur->last->x) +
fabs(cCur->first->y - cCur->last->y)) < 2.){ // closed contour
cCur->closed = TRUE;
if(cCur->N < MIN_CLOSED_CONTOUR_SIZE){ // contour is too short
free_contour(&cCur);
break;
}
}
cList_add(contours[lvl], cCur);
}while(0);
return TRUE;
}
/*
* Find isophotos on picture window->image
* nLevl - amount of isophotos
* type - type of isophotos segmentation (the same as f->h for STEP filter)
*/
void find_contours(Window *window,
int nLevl,
int type){
IMAGE *ima = window->image;
int y, level;
float *image = ima->data;
if(nLevl < 2 || nLevl > MAX_CONTOUR_LEVELS) return;
unsigned char *mask; // array for marching squares matrix
Filter f;
#ifdef EBUG
double t0 = dtime();
#endif
w = ima->width; w1=w-1; h = ima->height; h1=h-1;
mask = calloc((w1)*(h1),1); // mask - one mask pixel is square of 2x2 pixels on an picture
if(!mask){g_err(_("Can't allocate memory"));return;}
// prepare isoscale
f.FilterType = STEP; f.w = nLevl; f.h = type;
if(!fillIsoScale(&f, &isoscale, ima->stat.min, ima->stat.max - ima->stat.min)){
g_err(_("Can't allocate memory"));goto endOF;}
// Go through all levels
contours = calloc(nLevl, sizeof(Contour)); // all-picture contours array
if(!contours){g_err(_("Can't allocate memory"));goto endOF;}
for(level = 0; level < nLevl; ++level){
/*
* 1. level segmentation & build mask
* Mask by square (if zero point in left upper corner)
* _______
* | a | b |
* |---+---|
* | c | d |
* -------
* bits order: 0000abcd
*/
int y;
float lvl = isoscale[level]; // current isoline's intensity level
#pragma omp parallel for
for(y = 0; y < h1; y++){
int x;
unsigned char *out = &mask[y*w1];
float *in = &image[y*w];
for(x = 0; x < w1; x++, in++, out++)
*out = (unsigned char)
((in[0]>lvl)<<3) |
((in[1]>lvl)<<2) |
((in[w]>lvl)<<1) |
(in[w+1]>lvl);
}
// go through all mask's points
for(y = 0; y < h1; y++){
int x;
for(x = 0; x < w1; x++)
if(!process_it_(x, y, level, image, mask)) goto endOF;
}
}
endOF:
_FREE(mask);
if(contours){
for(y = 0; y < nLevl; y++){
g_print("Level: %d", y);
if(contours[y]) g_print(", %d contours\n", contours[y]->N);
else g_print("\n");
}
// fill contour structure
ima->contours = contours; contours = NULL;
ima->Ncontours = nLevl;
if(isoscale){ima->cLevels = isoscale; isoscale = NULL;}
}
DBG("time: %g", dtime()-t0);
window->context->visualMode |= SHOW_ISOLINES;
gen_texture(ima, window, TRUE);
force_redraw(window->drawingArea);
}

168
src/filelist.c Normal file
View File

@ -0,0 +1,168 @@
// filelist.c - functions to work with list of FITS files in current directory
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "filelist.h"
#include "fits.h"
/*
* receiving the file name of list, type can be one of the following:
* CURRENT - current file
* FIRST - first file
* LAST - last file
* PREVIOUS - previous file
* NEXT - next file
*/
gchar *get_filename(guchar type, Window *window){
DBG("length of list = %d", window->files.list_length);
if(window->files.list_length < 1) return NULL;
if(!window->files.list_current){
DBG("No current file. WTF?");
return NULL;
}
//~ g_list_foreach(window->files.files_list, (GFunc)prnt, NULL);
switch(type){
case FIRST:
if(window->files.list_current == window->files.files_list){
DBG("1st or only file");
return NULL; // try to open current opened file or there's no files
}
window->files.list_current = window->files.files_list;
break;
case LAST:
if(window->files.list_length < 2 ||
window->files.list_current == window->files.list_end){
DBG("last or only file");
return NULL;
}
window->files.list_current = window->files.list_end;
break;
case PREVIOUS:
if(!window->files.list_current->prev){
DBG("1st file");
return NULL;
}
window->files.list_current = window->files.list_current->prev;
break;
case NEXT:
if(!window->files.list_current->next){
DBG("last file");
return NULL;
}
window->files.list_current = window->files.list_current->next;
break;
default: // current
break;
}
if(!window->files.list_current || !window->files.list_current->data){
DBG("no current file? WTF?");
return NULL;
}
DBG("change filename: %s\n\n", (gchar*)window->files.list_current->data);
return (gchar*)window->files.list_current->data;
}
// get filename suffix
gboolean get_ext(gchar *filename){
gchar *basename, *ext;
basename = strrchr(filename, '/');
if(!basename) basename = filename;
else basename++;
ext = strrchr(basename, '.');
if(!ext) return FALSE;
else ext++;
//DBG("basename = %s; ext = %s", basename, ext);
if(strcasecmp(ext, "fts") == 0 || strcasecmp(ext, "fit") == 0 ||
strcasecmp(ext, "fits") == 0) return TRUE;
return FALSE;
}
// File test for loadable
gboolean is_loadable(gchar *filename){
if(!Global->add_all){ // if we don't try to open any file
if(!get_ext(filename)) // file suffix isn't fit/fits/fts
return FALSE;
}
return guess_fits(filename);
}
gint add_file_to_list(gchar *filename, Window *window){
if(is_loadable(filename)){
DBG("File %s is FITS file\n", filename);
window->files.files_list = g_list_prepend(window->files.files_list, (gpointer)filename);
window->files.list_length++;
if(window->files.list_length == 1)
window->files.list_end = window->files.files_list;
return 1;
}
return 0;
}
void free_filelist(Window *window){
if(window->files.files_list){
g_list_foreach(window->files.files_list, (GFunc)free, NULL);
g_list_free(window->files.files_list);
window->files.list_length = 0;
window->files.files_list = NULL;
}
}
/*
* get a FITS files list in current directory
* filename - full filename (with path)
* returns a files number in list
*/
gint fill_filelist(gchar *filename, Window *window){
gint nb_inserted = 0;
GDir *dir;
GError *err = NULL;
const gchar *dir_entry;
gchar *dirname = g_path_get_dirname(filename);
DBG("Open directory %s", dirname);
dir = g_dir_open(dirname, 0, &err);
if(dir == NULL){
g_err(err->message);
g_error_free(err);
return 0;
}
free_filelist(window); // clear list if it was already created
while((dir_entry = g_dir_read_name(dir))){
gchar *full_path = g_build_filename(dirname, dir_entry, NULL);
if(!g_file_test(full_path, G_FILE_TEST_IS_DIR)){
nb_inserted += add_file_to_list(full_path, window);
}
}
g_dir_close(dir);
g_free(dirname);
window->files.files_list = g_list_sort(window->files.files_list, (GCompareFunc)strcmp);
DBG("\nSort %d items\n", nb_inserted);
window->files.list_end = g_list_last(window->files.files_list);
window->files.list_current = g_list_find_custom(window->files.files_list, filename,(GCompareFunc)strcmp);
//if(!window->files.list_current) window->files.list_current = g_list_first(window->files.files_list);
//~ place_first = g_list_find_custom(window->files.files_list, filename, strcmp);
//~ if(place_first && place_first->prev){
//~ place_first->prev->next = place_first->next;
//~ if(place_first->next)
//~ place_first->next->prev = place_first->prev;
//~ place_first->prev = NULL;
//~ place_first->next = window->files.files_list;
//~ window->files.files_list->prev = place_first;
//~ window->files.files_list = place_first;
//~ }
return nb_inserted;
}

396
src/fits.c Normal file
View File

@ -0,0 +1,396 @@
// fits.c - main functions to work with FITS files
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fits.h"
#include "gtk.h"
#include "fitsheaders.h"
/*
* Macros for error processing when working with cfitsio functions
*/
#define TRYFITS(f, ...) \
do{ gboolean status = FALSE; \
f(__VA_ARGS__, &status); \
if(status){ \
fits_report_error(stderr, status); \
return FALSE;} \
}while(0)
#define FITSFUN(f, ...) \
do{ gboolean status = FALSE; \
int ret = f(__VA_ARGS__, &status); \
if(ret || status) \
fits_report_error(stderr, status); \
}while(0)
#define WRITEKEY(...) \
do{ gboolean status = FALSE; \
fits_write_key(__VA_ARGS__, &status); \
if(status) fits_report_error(stderr, status);\
}while(0)
/*
* Check whether a number is a complex
* If yes - returns true, no - false
* If re and/or im!=NULL, in them stored values
* of real and imaginary components
*/
gboolean cmplx_conv(gchar *value, double *re, double *im){
double sign = 1.;
double r,i;
gchar *ptr1, *ptr2, *eptr;
ptr1 = g_strstr_len(value, -1, "(");
if(ptr1){ // form is (re, im) or (re im)
ptr1++;
ptr2 = g_strstr_len(ptr1, -1, ")");
if(ptr2) *ptr2 = 0;
ptr2 = g_strstr_len(ptr1, -1, ",");
if(!ptr2)
ptr2 = g_strstr_len(ptr1, -1, " ");
}
else{ // form is re + iim or re + jim
ptr1 = value;
ptr2 = g_strstr_len(ptr1, -1, "i");
if(!ptr2)
ptr2 = g_strstr_len(ptr1, -1, "j");
}
if(ptr2){
*ptr2 = 0;
ptr2++;
}
else
return FALSE;
if(g_strrstr_len(ptr1, 3, "-")) sign=-1.;
r = strtod(ptr1, &eptr);
if(eptr == ptr1) return FALSE;
i = sign*strtod(ptr2, &eptr);
if(eptr == ptr2) return FALSE;
if(re) *re = r;
if(im) *im = i;
return TRUE;
}
/*
* try to guess key type:
* TLONGLONG, TDOUBLE, TDBLCOMPLEX or TSTRING
*/
int guess_type(gchar *keyval, gchar* keytype){
char *eptr, *val;
int dtype = TSTRING;
val = strdup(keyval);
if(strchr(val, '.')){
errno = 0;
strtod(val, &eptr);
if(eptr != val && errno != ERANGE)
dtype = TDOUBLE;
}
if(dtype == TSTRING){
errno = 0;
strtoll(val, &eptr, 0);
if(eptr != val && errno != ERANGE) dtype = TLONGLONG;
}
if(dtype == TSTRING && cmplx_conv(val, NULL, NULL))
dtype = TDBLCOMPLEX;
if(keytype) switch(dtype){
case TDOUBLE:
sprintf(keytype, "TDOUBLE");
break;
case TLONGLONG:
sprintf(keytype, "TLONGLONG");
break;
case TDBLCOMPLEX:
sprintf(keytype, "TDBLCOMPLEX");
break;
default:
sprintf(keytype, "TSTRING");
}
//~ DBG("dtype: %d, keytype: %s", dtype, (keytype) ? keytype : "");
free(val);
return dtype;
}
/*
* Save image-structure to a FITS file
* (together whith keys)
*/
gboolean writefits(char *filename, IMAGE *image){
FNAME();
long naxes[2] = {image->width, image->height};
int w = image->width, h = image->height;
ssize_t sz = w * h;
gchar *keytype, *keyname, *keyval, *keycomment; int keyfitstype;
GtkTreeIter iter;
fitsfile *fp;
gchar *card;
#ifdef EBUG
int n = 0;
#endif
TRYFITS(fits_create_file, &fp, filename);
TRYFITS(fits_create_img, fp, image->dtype, 2, naxes);
if(image->store){
GtkTreePath *path = gtk_tree_path_new_first();
GtkTreeModel *tree_model = GTK_TREE_MODEL(image->store);
if(gtk_tree_model_get_iter(tree_model, &iter, path)){ // there's keys
DBG("filename: %s", filename);
do{
gtk_tree_model_get(tree_model, &iter,
L_TYPENM, &keytype,
L_KEY, &keyname,
L_VAL, &keyval,
L_COMM, &keycomment,
L_TYPE, &keyfitstype,
-1);
if(keyfitstype == TSTRING) // make sure, that string is in quotes
if(*keyval != '\''){
gchar *str = g_strdup_printf("'%s'", keyval);
g_free(keyval);
keyval = str;
}
card = g_strdup_printf("%-8s= %s / %s",
keyname, keyval, keycomment);
g_free(keytype); g_free(keyname); g_free(keyval);
g_free(keycomment);
DBG("key#%d: %s", ++n, card);
FITSFUN(fits_write_record, fp, card);
g_free(card);
}while(gtk_tree_model_iter_next(tree_model, &iter));
}
gtk_tree_path_free(path);
}
GLfloat min, wd;
min = image->stat.min;
wd = image->stat.max - min;
DBG("min = %f, wd = %f", min, wd);
/* int i,j;
GLfloat *newdata = malloc(sz*sizeof(GLfloat));
GLfloat *ptro = newdata, *ptri = image->data;
for(i=0; i<h; i++)
for(j=0; j<w; j++, ptro++, ptri++){
*ptro = min + (*ptri) * wd;
}
TRYFITS(fits_write_img, fp, TFLOAT, 1, sz, newdata);
free(newdata);*/
TRYFITS(fits_write_img, fp, TFLOAT, 1, sz, image->data);
TRYFITS(fits_close_file, fp);
return TRUE;
}
gboolean try_open_file(gchar *filename, IMAGE *ima){
if(!readfits(filename, ima)){
g_err(_("Can't read fits file"));
ima->data = NULL;
return FALSE;
}
return TRUE;
}
/*
char *del_spaces(char *str){
char *beg, *end;
beg = str;
end = str + strlen(str);
while(*beg == ' ' && beg < end) beg++;
if(beg == end) return beg;
end--;
if(beg == end) return beg;
while(*end == ' ' && end > beg)
*end-- = 0;
return beg;
}
gboolean strip_card(char *card, char *keyname, char *keyval, char *keycomment){
char *ptr, *ptr1, *end;
char *c = strdup(card);
end = c + strlen(c);
ptr = strchr(c, '=');
if(!ptr) return FALSE;
*ptr = 0; ptr++;
ptr1 = del_spaces(c);
strncpy(keyname, ptr1, FLEN_KEYWORD-1);
ptr1 = strchr(ptr, '\'');
if(ptr1){
ptr1++;
ptr = strchr(ptr1, '\'');
if(!ptr){
ptr = strchr(ptr1, '/');
if(!ptr){
free(c);
return FALSE;
}
*ptr = 0; ptr++;
}else{
*ptr = 0; ptr = strchr(ptr+1, '/');
if(!ptr){
free(c);
return FALSE;
}
*ptr = 0; ptr++;
}
}else{
ptr1 = ptr;
ptr = strchr(ptr1, '/');
if(!ptr){
free(c);
return FALSE;
}
*ptr = 0; ptr++;
ptr1 = del_spaces(ptr1);
}
if(ptr >= end) return FALSE;
ptr1 = del_spaces(ptr1);
strncpy(keyval, ptr1, FLEN_VALUE);
ptr1 = del_spaces(ptr);
strncpy(keycomment, ptr1, FLEN_COMMENT);
free(c);
return TRUE;
}*/
/*
* Read FITS file filename to a structure image
*/
gboolean readfits(gchar *filename, IMAGE *image){
FNAME();
gboolean ret = TRUE;
fitsfile *fp;
GLfloat nullval = 0., imBits, bZero = 0., bScale = 1.;
free(image->data);
image->data = NULL;
int i, j, hdunum, keynum, morekeys, dtype, stat;
int naxis, w, h;
double val_f = -1e6; // VAL_F value
long naxes[4];
ssize_t sz;
//char card[FLEN_CARD];
char keyname[FLEN_KEYWORD], keyval[FLEN_VALUE];
char keycomment[FLEN_COMMENT], cdtype[32];
TRYFITS(fits_open_file, &fp, filename, READWRITE);
/* TRYFITS(fits_get_hdrspace, fp, &keynum, &morekeys);
if(morekeys == -1){
return FALSE;
}
g_print("Read header of %s, %d keys\n", filename, keynum);*/
FITSFUN(fits_get_num_hdus, fp, &hdunum);
//~ g_print(_("\nFile includes %d HDUs\n"), hdunum);
if(hdunum < 1){
g_err(_("Can't read HDU"));
TRYFITS(fits_close_file, fp);
return FALSE;
}
FITSFUN(fits_get_hdu_type, fp, &dtype);
//~ if(dtype) g_print(_("Current HDU type: %d\n"), dtype);
init_keylist(image);
FITSFUN(fits_get_hdrpos ,fp, &keynum, &morekeys);
for (j = 1; j <= keynum; j++){
/*FITSFUN(fits_read_record, fp, j, card);
if(!strip_card(card, keyname, keyval, keycomment)){
g_print("Corrupted card: %s!\n", card);
continue;
}*/
FITSFUN(fits_read_keyn, fp, j, keyname, keyval, keycomment);
if(strcmp(keyname, "SIMPLE") == 0 || strcmp(keyname, "EXTEND") == 0) // key "file does conform ..."
continue;
else if(strcmp(keyname, "COMMENT") == 0) // comment of obligatory key in FITS head
continue;
else if(strstr(keyname, "NAXIS") == keyname || strcmp(keyname, "BITPIX") == 0) // NAXIS, NAXISxxx, BITPIX
continue;
else if(strcmp(keyname, "BZERO") == 0){
bZero = atof(keyval);
//continue;
}
else if(strcmp(keyname, "BSCALE") == 0){
bScale = atof(keyval);
if(bScale < SCALE_MIN) bScale = 1.;
//continue;
}
else if(strcmp(keyname, "VAL_F") == 0){
val_f = atof(keyval);
}
dtype = guess_type(keyval, cdtype);
//DBG("key#%d; name=%s, dtype=%d (%s), value=%s, comment=%s\n",
// j, keyname, dtype, cdtype, keyval, keycomment);
add_key(image, cdtype, keyname, keyval, keycomment, dtype);
}
if(hdunum > 1){
for(i = 2; !(fits_movabs_hdu(fp, i, &dtype, &stat)); i++){
FITSFUN(fits_get_hdrpos ,fp, &keynum, &morekeys);
for (j = 1; j <= keynum; j++){
FITSFUN(fits_read_keyn, fp, j, keyname, keyval, keycomment);
dtype = guess_type(keyval, cdtype);
add_key(image, cdtype, keyname, keyval, keycomment, dtype);
}
}
if(stat != END_OF_FILE){
fits_report_error(stderr, (stat));
ret = FALSE;
}
}
TRYFITS(fits_get_img_param, fp, 4, &dtype, &naxis, naxes);
if(dtype > 0) imBits = (GLfloat)(1LL << dtype); // compute the max amplitude
else imBits = -1.; // image has floating point type
DBG("Image type: %d (%f bits)", dtype, imBits);
image->maxVal = imBits;
image->dtype = dtype;
if(naxis != 2){
g_err(_("Not an image? (dimensions != 2)"));
return FALSE;}
image->val_f = val_f;
image->bZero = bZero;
image->bScale = bScale;
image->width = w = naxes[0];
image->height = h = naxes[1];
sz = (ssize_t)(w) * (ssize_t)(h);
image->data = malloc(sz * sizeof(GLfloat));
TRYFITS(fits_read_img, fp, TFLOAT, 1, sz, &nullval, image->data, &stat);
GLfloat *ptr = image->data;
GLfloat min, max, wd;
min = 1e6; max = 0.;
for(i=0; i<h; i++)
for(j=0; j<w; j++, ptr++){
GLfloat tmp = *ptr;
if(tmp > max) max = tmp;
else if(tmp < min) min = tmp;
}
wd = max - min;
DBG("min = %f, wd = %f", min, wd);
image->stat.max = max;
image->stat.min = min;
// histogram isn't initialized yet
image->stat.histogram.data = NULL;
image->stat.histogram.size = 0;
/*
ptr = image->data;
for(i=0; i<h; i++)
for(j=0; j<w; j++, ptr++){
// *ptr = (*ptr * bScale + bZero) / imBits;
*ptr = (*ptr - min) / wd;
}
*/
TRYFITS(fits_close_file, fp);
return(ret);
}
// Try to open FITS and find an image in it
gboolean guess_fits(gchar *filename){
fitsfile *fp;
long naxes[4];
int naxis, dtype;
DBG("Try to open file %s\n", filename);
TRYFITS(fits_open_file, &fp, filename, READONLY);
TRYFITS(fits_get_img_param, fp, 4, &dtype, &naxis, naxes);
if(naxis != 2) return FALSE;
TRYFITS(fits_close_file, fp);
return TRUE;
}

530
src/fitsheaders.c Normal file
View File

@ -0,0 +1,530 @@
// fitsheaders.c - functions to edit FITS head
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsheaders.h"
#include "fits.h"
GtkListStore *combo_ = NULL;
GtkTreeSelection *sel;
GtkTreeView* Tree;
GtkWidget *del_button;
GtkWidget *window;
// Data types to write FITS keys into menu
gchar* key_types[] = {
"TSTRING", "TLOGICAL", "TBYTE", "TSHORT", "TUSHORT", "TINT", "TUINT",
"TLONG", "TULONG", "TLONGLONG", "TFLOAT", "TDOUBLE", "TCOMPLEX",
"TDBLCOMPLEX"
};
// Values itself
int type_vals[] = {
TSTRING,
TLOGICAL,
TBYTE,
TSHORT,
TUSHORT,
TINT,
TUINT,
TLONG,
TULONG,
TLONGLONG,
TFLOAT,
TDOUBLE,
TCOMPLEX,
TDBLCOMPLEX
};
int VALS_N; // Number of items
gchar *colnames[] = {N_("Type"), N_("Name"), N_("Value"), N_("Comment")};
#define VISIBLE_COLS 4 // The amount of visible columns
int find_list_type(char *type){
int i;
for(i = 0; i < VALS_N; i++)
if(strcmp(key_types[i], type) == 0) break;
if(i == VALS_N) // Error: there's no such value in list
i = 0;
return i;
}
gboolean ll_conv(gchar **value, int type){
long long ll_val, oll_val;
gboolean ret = TRUE;
ll_val = oll_val = strtoll(*value, NULL, 0);
//g_free(*value);
switch(type){
case TBYTE:
if(ll_val > CHAR_MAX || ll_val < CHAR_MIN) ll_val = 0L;
break;
case TSHORT:
if(ll_val > SHRT_MAX || ll_val < SHRT_MIN) ll_val = 0L;
break;
case TINT:
if(ll_val > INT_MAX || ll_val < INT_MIN) ll_val = 0L;
break;
case TLONG:
if(ll_val > LONG_MAX || ll_val < LONG_MIN) ll_val = 0L;
break;
case TLONGLONG:
if(ll_val > LLONG_MAX || ll_val < LLONG_MIN) ll_val = 0L;
break;
case TUSHORT:
if(ll_val > USHRT_MAX || ll_val < 0L) ll_val = 0L;
break;
case TUINT:
if(ll_val > UINT_MAX || ll_val < 0L) ll_val = 0L;
break;
case TULONG:
if((unsigned long)ll_val > ULONG_MAX || ll_val < 0L) ll_val = 0L;
break;
default:
ll_val = 0L;
break;
}
DBG("long long: %lld", ll_val);
if(ll_val != oll_val){
g_err(_("Error in value range!"));
ret = FALSE;
}
g_free(*value);
*value = g_strdup_printf("%lld", ll_val);
return ret;
}
gboolean mystrtod(gchar **val){
double d_val;
gboolean ret = TRUE;
char *eptr = NULL, *ptr = *val;
errno = 0;
d_val = strtod(ptr, &eptr);
DBG("double: %.16e (was: %s)", d_val, *val);
if((eptr && *eptr) || errno == ERANGE){
g_free(*val);
DBG("try convert %g into val", d_val);
*val = g_strdup_printf("%.16e", d_val);
g_err(_("Invalid double number!"));
ret = FALSE;
}
DBG("val: %s", *val);
return ret;
}
gboolean check_complex_number(gchar **val){
double re = 0., im = 0.;
gboolean ret = TRUE;
if(!cmplx_conv(*val, &re, &im)){
gchar *msg = g_strdup_printf("%s\n%s",
_("Format error: complex number must be in format"),
"RE + iIM, RE + jIM, RE iIM, RE jIM, (RE, IM) or (RE IM)");
g_err(msg);
g_free(msg);
ret = FALSE;
}
DBG("complex: (%g, %g)", re, im);
g_free(*val);
*val = g_strdup_printf("(%g, %g)", re, im);
return ret;
}
gboolean check_val(gchar **newval, int type){
gboolean b_val, ret = TRUE;
switch(type){
case TBYTE:
case TSHORT:
case TINT:
case TLONG:
case TLONGLONG:
case TUSHORT:
case TUINT:
case TULONG:
// convert *char -> long long vith checking value
ret = ll_conv(newval, type);
break;
case TFLOAT:
case TDOUBLE:
// convert *char -> double
ret = mystrtod(newval);
break;
case TCOMPLEX:
case TDBLCOMPLEX:
// check whether a number is complex
ret = check_complex_number(newval);
break;
case TLOGICAL:
// convert value into boolean
if(g_ascii_strncasecmp("true", *newval, 4)==0) b_val = TRUE;
else
b_val = atoi(*newval);
g_free(*newval);
*newval = g_strdup_printf("%s", b_val ? "TRUE" : "FALSE");
DBG("boolean: %s", *newval);
break;
default:
DBG("string: %s", *newval); // all other values a strings
}
return ret;
}
gboolean try_change_val(IMAGE *ima, gchar **newval){
int type;
GtkTreeIter cur_items;
gboolean ret;
GtkTreeModel *model;
FNAME();
if(!gtk_tree_selection_get_selected(sel,&model, &cur_items)) return FALSE;
gtk_tree_model_get(GTK_TREE_MODEL(ima->store), &cur_items, L_TYPE, &type, -1);
ret = check_val(newval, type);
gtk_list_store_set(ima->store, &cur_items, L_VAL, *newval, -1);
DBG("ret");
return ret;
}
guint cur_column;
void edit_cell( GtkCellRendererText *cell,
gchar *path,
gchar *new_text,
IMAGE *ima){
GtkTreeModel *model;
GtkTreeIter cur_items;
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(cell), "column_num"));
gboolean nxtcol = TRUE, editable = TRUE;
if(!path) return;
DBG("path: %s, new: %s, colnum: %d", path, new_text, cur_column);
gchar *textptr;
if(!gtk_tree_selection_get_selected(sel,&model, &cur_items)) return;
switch(cur_column){
case L_TYPENM: // key type was changed
gtk_list_store_set(ima->store, &cur_items, L_TYPE,
type_vals[find_list_type(new_text)], -1);
gtk_list_store_set(ima->store, &cur_items, cur_column, new_text, -1);
break;
case L_VAL: // key value was changed
textptr = g_strdup_printf("%s", new_text);
nxtcol = try_change_val(ima, &textptr); // try to change parameter according to its type
g_free(textptr);
break;
default:
gtk_list_store_set(ima->store, &cur_items, cur_column, new_text, -1);
break;
}
if(nxtcol){
if(cur_column < L_COMM) // it's not a last column - select next
cur_column++;
else{
cur_column = 0;
editable = FALSE;
}
}
DBG("set cursor col:%d, e=%d", cur_column, editable);
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(ima->store), &cur_items),
gtk_tree_view_get_column(Tree, cur_column), editable);
DBG("ret");
}
/*
void cancel_edit(GtkCellRenderer *renderer){
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(renderer), "column_num"));
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(store), &cur_items),
gtk_tree_view_get_column(Tree, cur_column), FALSE);
}
*/
void add_key( IMAGE *ima,
gchar *keytype, gchar *keyname, gchar *keyval,
gchar *keycomment, int keyfitstype){
GtkTreeIter iter;
//~ FNAME();
gtk_list_store_append(ima->store, &iter);
gtk_list_store_set(ima->store, &iter,
L_TYPENM, keytype,
L_KEY, keyname,
L_VAL, keyval,
L_COMM, keycomment,
L_TYPE, keyfitstype,
-1);// the last element must be -1
}
void init_keylist(IMAGE *ima){
/* Create a list model.
* We will have 4 columns: name of key type, key name, key value, comment, type
*/
if(ima->store) g_object_unref(G_OBJECT(ima->store)); // clear list, if it already exists
ima->store = gtk_list_store_new(L_MAX, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
}
GtkWidget *create_treeview(IMAGE *ima){
int i, i_max = sizeof(key_types)/sizeof(char*);;
GtkTreeIter iter;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
if(combo_)
gtk_list_store_clear(combo_);
combo_ = gtk_list_store_new(1, G_TYPE_STRING);
for(i = 0; i < i_max; i++){
gtk_list_store_append(combo_, &iter);
gtk_list_store_set(combo_, &iter, 0, key_types[i], -1);
}
// create list (treeview) according model store
Tree = (GtkTreeView*)gtk_tree_view_new_with_model(GTK_TREE_MODEL(ima->store));
// Create columns in list
for(i = 0; i < VISIBLE_COLS; i++){
if(i == 0){
renderer = gtk_cell_renderer_combo_new();
g_object_set(renderer, "text-column", 0,
"editable", TRUE, "has-entry", FALSE, "model",
GTK_TREE_MODEL(combo_), NULL);
}
else{
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "editable", TRUE, NULL);
}
g_object_set_data(G_OBJECT(renderer), "column_num", GINT_TO_POINTER(i));
column = gtk_tree_view_column_new_with_attributes(_(colnames[i]),
renderer, "text", i, NULL);
gtk_tree_view_append_column(Tree, column); // insert column
gtk_tree_view_column_set_sort_column_id(column, i);// it may be sorted
g_signal_connect(renderer, "edited", G_CALLBACK(edit_cell), ima);
// g_signal_connect(renderer, "editing-canceled", G_CALLBACK(cancel_edit), NULL);
}
/*
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("hidden",renderer, NULL);
g_object_set(column, "visible", FALSE, NULL);
gtk_tree_view_append_column(Tree, column);
*/
return GTK_WIDGET(Tree);
}
static void list_changed(GtkTreeSelection *sel){
GtkTreeModel *model;
GtkTreeIter cur_items;
if(gtk_tree_selection_get_selected(sel,&model, &cur_items))
gtk_widget_set_sensitive(del_button, TRUE);
else
gtk_widget_set_sensitive(del_button, FALSE);
/*
GtkTreeModel *model;
FNAME();
gboolean showdel = FALSE;
if(!GTK_IS_TREE_SELECTION(sel)){
showdel = TRUE;
if(GTK_IS_TREE_SELECTION(obj)) DBG("!!!");
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(sel), "column_num"));
DBG("curcol: %d", cur_column);
}
else if(gtk_tree_selection_get_selected(sel,&model, &cur_items)){
gint column = L_VAL;
gchar *value;
gtk_tree_model_get(model, &cur_items, column, &value, -1);
DBG("selected: %s", value);
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(model, &cur_items),
gtk_tree_view_get_column(Tree, cur_column), FALSE);
g_free(value);
showdel = TRUE;
}
gtk_widget_set_sensitive(del_button, showdel);
DBG("ret");*/
}
void newline(IMAGE *ima){
FNAME();
GtkTreeIter cur_items;
gtk_list_store_append(ima->store, &cur_items);
gtk_list_store_set(ima->store, &cur_items,
L_TYPENM, key_types[0],
L_KEY, "NEW_KEY",
L_VAL, "",
L_COMM, "",
L_TYPE, type_vals[0],
-1);
// Set cursor onto a first field
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(ima->store), &cur_items),
gtk_tree_view_get_column(Tree, 0), TRUE);
DBG("ret");
}
gboolean del_line(IMAGE *ima){
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(ima->store);
gboolean ret = FALSE;
FNAME();
GtkTreeSelection *selection = gtk_tree_view_get_selection(Tree);
GtkTreePath *path;
if(gtk_tree_selection_get_selected(selection, NULL, &iter)){
path = gtk_tree_model_get_path(model, &iter);
ret = gtk_list_store_remove(ima->store, &iter);
// check wherher &cur_items was last
if(gtk_list_store_iter_is_valid(ima->store, &iter)){
gtk_tree_path_free(path);
path = gtk_tree_model_get_path(model, &iter);
}
else gtk_tree_path_prev(path); // goto previous
if(path && gtk_tree_model_get_iter_first(model, &iter))
gtk_tree_view_set_cursor(Tree, path, NULL, FALSE);
else DBG("No entries");
gtk_tree_path_free(path);
}
DBG("ret");
return ret;
}
/*
void show_menu(){
GtkWidget *menu, *menuitem;
gchar *text;
int i;
menu = gtk_menu_new();
for(i = 0; i < 10; i++){
text = g_strdup_printf("%d", i);
menuitem = gtk_menu_item_new_with_label(text);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show(menuitem);
g_free(text);
}
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, 0);
}
gboolean treeview_button_press_cb(GtkWidget *wi, GdkEventButton *ev, gpointer user_data)
{
GtkTreeSelection *sel;
GtkTreeModel *model;
GtkTreePath *path, *oldpath;
GtkTreeViewColumn *column;
GtkTreeIter iter;
gchar *colname;
// if there's no path where the click occurred...
if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(wi), ev->x, ev->y, &path, &column, NULL, NULL))
return FALSE; // ...return FALSE to allow the event to continue
colname = strdup(gtk_tree_view_column_get_title(column));
// get the tree view's selection
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(wi));
switch(ev->button)
{
// LMB
case 1:
// check to see if this is already the selected path
if(gtk_tree_selection_get_selected(sel, &model, &iter))
{
oldpath = gtk_tree_model_get_path(model, &iter);
if(gtk_tree_path_compare(oldpath, path) == 0)
{
gtk_tree_path_free(oldpath);
gtk_tree_path_free(path);
return FALSE;
}
gtk_tree_path_free(oldpath);
}
// select the clicked path
gtk_tree_selection_select_path(sel, path);
return TRUE;
// RMB
case 3:
if(strncmp(colname, "No.", 3)){
gtk_tree_path_free(path);
return TRUE;
}
show_menu();
// gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, ev->time);
break;
}
// free our path
gtk_tree_path_free(path);
return FALSE; // allow it to continue
}
*/
void close_dlg(){
gtk_widget_destroy(window);
}
static gboolean press_key(IMAGE *ima, GdkEventKey * event){
gboolean ret = FALSE;
if(event->state & (GDK_CONTROL_MASK)){
switch(event->keyval){
// ctrl+n inserts a line
case 'n': case 'N':
newline(ima);
ret = TRUE;
break;
// ctrl+d deletes current line
case 'd': case 'D':
ret = del_line(ima);
break;
// ctrl+w closes the window
case 'w': case 'W':
close_dlg();
}
}
return ret;
}
void edit_headers(Window *parent){
VALS_N = sizeof(key_types) / sizeof(gchar *);
GtkWidget *treeview, *vbox, *hbox, *button;
if(!parent->image || !parent->image->store){
g_err(_("Open fits file first"));
return;
}
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "List");
gtk_signal_connect(GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(gtk_false),NULL);
gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC(close_dlg), NULL);
treeview = create_treeview(parent->image);
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(list_changed), NULL);
//g_signal_connect(G_OBJECT(treeview), "cursor-changed", G_CALLBACK(list_changed), NULL);
vbox = gtk_vbox_new(FALSE, 5);
gtk_container_add (GTK_CONTAINER (window), vbox);
GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(sw), treeview);
gtk_window_set_default_size(GTK_WINDOW (window), 640, 480);
//~ gtk_box_pack_start(GTK_BOX(vbox), treeview, TRUE, TRUE, 0);
hbox = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
button = gtk_button_new_with_label(_("New entry (ctrl+n)"));
g_signal_connect_swapped(G_OBJECT(button), "clicked",
G_CALLBACK(newline), parent->image);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
del_button = gtk_button_new_with_label(_("Delete entry (ctrl+d)"));
g_signal_connect_swapped(G_OBJECT(del_button), "clicked",
G_CALLBACK(del_line), parent->image);
gtk_box_pack_end(GTK_BOX(hbox), del_button, FALSE, FALSE, 0);
gtk_widget_set_sensitive(del_button, FALSE);
button = gtk_button_new_with_label(_("Close (ctrl+w)"));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_dlg), NULL);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
g_signal_connect_swapped(G_OBJECT(window), "key-press-event",
G_CALLBACK(press_key), parent->image);
run_modal_window(GTK_WINDOW(window), parent);
}

BIN
src/fitsview Executable file

Binary file not shown.

123
src/fitsview.c Normal file
View File

@ -0,0 +1,123 @@
// fitsview.c - main project file
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsview.h"
#include "fits.h"
#include "gtk.h"
#include "opengl.h"
#include "open_dialog.h"
#include "filelist.h"
#define RED "\033[1;31;40m"
#define GREEN "\033[1;32;40m"
#define OLDCOLOR "\033[0;0;0m"
int (*red)(const char *fmt, ...);
int (*green)(const char *fmt, ...);
int r_pr_(const char *fmt, ...){
va_list ar; int i;
printf(RED);
va_start(ar, fmt);
i = vprintf(fmt, ar);
va_end(ar);
printf(OLDCOLOR "\n");
return i;
}
int g_pr_(const char *fmt, ...){
va_list ar; int i;
printf(GREEN);
va_start(ar, fmt);
i = vprintf(fmt, ar);
va_end(ar);
printf(OLDCOLOR "\n");
return i;
}
Global_context *Global;
// init structures with zeros
void *init_struct(int size){
int8_t *ptr = calloc(1, size);
return (void*) ptr;
}
// copy structure
void *copy_struct(void *src, int size){
int8_t *ptr = calloc(1, size);
memcpy(ptr, src, size);
return (void*)ptr;
}
// current time (for random number generator initialisation & for debug purposes)
double dtime(){
double t;
struct timeval tv;
gettimeofday(&tv, NULL);
t = tv.tv_sec + ((double)tv.tv_usec)/1e6;
return t;
}
// if we'll save preferences global context should be read fom file
void init_contexts(){
// limit spot size
Global->maxSpotH = 100;
Global->minSpotH = 5;
Global->maxSpotW = 100;
Global->minSpotW = 5;
}
// here will be a function to process command line arguments
int main(int argc, char *argv[]){
gchar *filename = NULL, *str;
if(isatty(STDOUT_FILENO)){ // make color output in tty
red = r_pr_; green = g_pr_;
}else{ // no colors in case of pipe
red = printf; green = printf;
}
#ifdef CUDA_FOUND
DBG("CUDA found");
#else
DBG("CUDA not found, using CPU instead");
#endif
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
textdomain(GETTEXT_PACKAGE);
INIT(Global, Global_context);
init_contexts();
init_main_window(&argc, &argv);
getGLinfo(mainWindow->drawingArea);
setlocale(LC_NUMERIC, "C"); // for right strto[df] working
// Show all for begin
mainWindow->context->visualMode = SHOW_ALL;
mainWindow->image->data = NULL;
if(argc > 1){
str = basename(argv[1]);
saved_path = get_current_dir_name();
filename = g_strdup_printf("%s/%s", saved_path, str);
fill_filelist(filename, mainWindow);
change_image(filename, mainWindow);
g_free(filename);
}else
gen_texture(mainWindow->image, mainWindow, FALSE);
gtk_main();
return 0;
}

1358
src/fitsview.glade Normal file

File diff suppressed because it is too large Load Diff

1281
src/fitsview.ui Normal file

File diff suppressed because it is too large Load Diff

378
src/gauss.c Normal file
View File

@ -0,0 +1,378 @@
// gauss.c - funtions for gaussian approximation
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "gtk.h"
#include "gauss.h"
#ifdef GSL_FOUND // cmake found GSL library
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_multifit_nlin.h>
#undef DBG
#undef EBUG
#define DBG(...)
/*
* Returns value of gaussian value in point x
* x0, C, A, sigma - are parameters (middle, vertical shift, amplitude and std)
*/
double gaussian_pt(double C, double A, double sigma, double x0, double x){
double s2 = sigma*sigma;
double ss = A/sqrt(2*M_PI*s2);
double E = x-x0;
return C + ss*exp(-E*E/2/s2);
}
/*
* Fills the ordinates of structure Points by Gaussian
* (Abscissa does not move, so that this structure can be used
* for multiple rendering of the same area but with different
* parameters)
*/
void gaussian_v(double C, double A, double sigma, double x0, Points *pts){
int i, n = pts->n;
Point *pt = pts->data;
double s2 = sigma*sigma * 2;
double ss = A/sqrt(2*M_PI*s2);
double E;
for(i = 0; i < n; i++, pt++){
E = pt->x - x0;
pt->y = C + ss*exp(-E*E/s2);
//DBG("pt %d (%g, %g)", i, pt->x, pt->y);
}
}
int gauss_f(const gsl_vector *parms, void *data, gsl_vector *f){
size_t n = ((struct data *)data)->n;
double *x = ((struct data *)data)->x;
double *y = ((struct data *)data)->y;
double *dy = ((struct data *)data)->dy;
double x0 = gsl_vector_get(parms, 3);
double sigma = gsl_vector_get(parms, 2);
double A = gsl_vector_get(parms, 1) * 0.65; // without 0.65 A would be wrong (WTF?)
double C = gsl_vector_get(parms, 0);
size_t i;
double mul = A/fabs(sigma)/sqrt(2.*M_PI);
for (i = 0; i < n; i++){
/* Model Yi = C + A/sqrt(2*pi*sigma^2) * exp(-(x-b)^2/2/sigma^2) */
double E = (x[i]-x0)/sigma;
double Yi = C + mul * exp(-E * E/2.);
gsl_vector_set(f, i, (Yi - y[i])/dy[i]);
}
return GSL_SUCCESS;
}
int gauss_df(const gsl_vector *parms, void *data, gsl_matrix *J){
size_t n = ((struct data *)data)->n;
double *x = ((struct data *)data)->x;
double *dy = ((struct data *) data)->dy;
double A = gsl_vector_get(parms, 1);
double sigma = gsl_vector_get(parms, 2);
double x0 = gsl_vector_get(parms, 3);
double S2PI = 1. / sqrt(2.*M_PI);
size_t i;
for(i = 0; i < n; i++){
/* Jacobian matrix J(i,j) = dfi / dxj, */
/* and the xj are the parameters (C, A,sigma,x0) */
double s = dy[i];
double xbs = (x[i]-x0)/sigma;
double y = exp(-xbs*xbs/2.) *S2PI / fabs(sigma) / s;
double Ays = A*y/sigma;
// df/dx0 = f * (x-x0)/sigma^2
gsl_matrix_set(J, i, 3, Ays*xbs);
// df/dsigma = -f(1-(x-x0)^2/sigma^2)/sigma
gsl_matrix_set(J, i, 2, Ays*(xbs*xbs-1.));
gsl_matrix_set(J, i, 1, y); // df/dA
gsl_matrix_set(J, i, 0, 1. / s); // df/dC
}
return GSL_SUCCESS;
}
int gauss_fdf(const gsl_vector *parms, void *data, gsl_vector *f, gsl_matrix *J){
gauss_f(parms, data, f);
gauss_df(parms, data, J);
return GSL_SUCCESS;
}
// pseudorandom numbers generator initialisation
void urandom_ini(){
double tt = dtime() * 1e6;
double mx = (double)LONG_MAX;
srand48((long)(tt - mx * floor(tt/mx)));
}
#define FIT(i) gsl_vector_get(s->x, i)
#define ERR(i) sqrt(gsl_matrix_get(covar,i,i))
#define FN(i) gsl_vector_get(s->f, i)
/*
* Gaussian parameters calculation y=A/sqrt(2*pi*sigma^2) exp(-(x-x_0)^2/2/sigma^2),
* which approximates the points set pts
* Parameters A_, sigma_, x0_ may be NULL (if you don't need any of them)
*/
void gauss_fit(Points *pts, double *C_, double *A_, double *sigma_, double *x0_){
// VVVV lower parameters may be formed as a structure to change as function argument
double
epsabs = 1e-8,// absolute error
epsrel = 1e-5,// relative error
chi_max = 0.01;// max chi value for iterations criteria
int max_iter = 300; // limit iterations number of gsl_multifit_fdfsolver
size_t N_MIN = 10; // minimum points for approximation
double x_init[4];
// AAAA upper parameters may be formed as a structure to change as function argument
/* x_init, the best approximations:
* x0 - not far from real (the nearest is the better)
* sigma - not far from real (the nearest is the better)
* A - not large ~10 (it has a weak effect)
*/
const gsl_multifit_fdfsolver_type *T;
gsl_multifit_fdfsolver *s;
int status;
#ifdef EBUG
int appNo = 0;
#endif
int iter;
size_t i, j, n = pts->n, oldn;
const size_t p = 4;
gsl_matrix *covar = gsl_matrix_alloc (p, p);
#ifdef EBUG
double t0;
#endif
double *x, *y, *dy, chi, C, A, sigma, x0;
if(n < 1) return;
x = malloc(n * sizeof(double));
y = malloc(n * sizeof(double));
dy = malloc(n * sizeof(double));
struct data d = {n, x, y, dy};
gsl_multifit_function_fdf f;
gsl_vector_view xx = gsl_vector_view_array(x_init, p);
const gsl_rng_type *type;
gsl_rng *r;
gsl_rng_env_setup();
type = gsl_rng_default;
r = gsl_rng_alloc (type);
f.f = &gauss_f;
f.df = &gauss_df;
f.fdf = &gauss_fdf;
f.n = n;
f.p = p;
f.params = &d;
// fill data structure. Don't forget Okkam's razor!!!
{
Point *pt = pts->data;
double *px = x, *py = y, *pdy = dy, sum = 0.;
for(i = 0; i < n; i++, pt++){
*pdy++ = 1.; // I have no idea what is it, so init by 1
*px++ = pt->x;
*py++ = pt->y;
sum += pt->y;
//DBG("point %d: (%g, %g)", i, pt->x, pt->y);
}
// fill x_init: x0, sigma, C, A (it can be a funtion parameter)
x_init[3] = (*(--px) + *x) / 2.;
x_init[2] = fabs((*x - *px) / 4.);
x_init[0] = sum/(double)n;
x_init[1] = sum;
DBG("\nInitial parameters: x0=%.1f, sigma=%.1f, A=%.1f, C=%.1f",
x_init[3], x_init[2], x_init[1], x_init[0]);
}
T = gsl_multifit_fdfsolver_lmder; // or also gsl_multifit_fdfsolver_lmsder
s = gsl_multifit_fdfsolver_alloc(T, n, p);
#ifdef EBUG
t0 = dtime();
#endif
do{
double dof, tres, c;
DBG("\n************ Approximation %d ******************\n", appNo++);
iter = 0;
gsl_multifit_fdfsolver_set(s, &f, &xx.vector);
do{
iter++;
status = gsl_multifit_fdfsolver_iterate(s);
if(status)
break;
status = gsl_multifit_test_delta(s->dx, s->x, epsabs, epsrel);
}while(status == GSL_CONTINUE && iter < max_iter);
DBG("time=%g\n", dtime()-t0);
gsl_multifit_covar(s->J, 0.0, covar);
chi = gsl_blas_dnrm2(s->f);
dof = n - p;
tres = chi;
c = chi / sqrt(dof); // GSL_MAX_DBL(1., chi / sqrt(dof));
C = FIT(0), A = FIT(1), sigma = FIT(2), x0 = FIT(3);
DBG("Number of iteratons = %d\n", iter);
DBG("chi = %g, chi/dof = %g\n", chi, chi / sqrt(dof));
DBG("C = %.5f +/- %.5f\n", C, c*ERR(0));
DBG("A = %.5f +/- %.5f\n", A, c*ERR(1));
DBG("sigma = %.5f +/- %.5f\n", sigma, c*ERR(2));
DBG("x0 = %.5f +/- %.5f\n", x0, c*ERR(3));
j = 0;
oldn = n;
if(c < chi_max) break;
// throw out bad (by chi) data
for(i = 0; i < n; i++){
if(fabs(FN(i)) < tres){
if(i != j){
x[j] = x[i];
y[j] = y[i];
dy[j] = dy[i];
}
j++; continue;
}
}
if(j != n){
DBG("Chi tresholding %g, %zd points of %zd\n", tres, j, n);
n = j;
d.n = n;
}
}while(chi > chi_max && n != oldn && n > N_MIN);
if(C_) *C_ = C;
if(A_) *A_ = A;
if(sigma_) *sigma_ = sigma;
if(x0_) *x0_ = x0;
//printf ("status = %s\n", gsl_strerror (status));
gsl_multifit_fdfsolver_free(s);
gsl_matrix_free(covar);
gsl_rng_free(r);
free(x); free(y); free(dy);
}
/*
int circle_f(const gsl_vector *parms, void *data, gsl_vector *f){
size_t n = ((struct data *)data)->n;
double *x = ((struct data *)data)->x;
double *y = ((struct data *)data)->y;
//double *dy = ((struct data *)data)->dy;
double X = gsl_vector_get(parms, 0);
double Y = gsl_vector_get(parms, 1);
double R = gsl_vector_get(parms, 2);
double sign, Yi, DX;
size_t i;
for (i = 0; i < n; i++){
sign = (y[i] > Y) ? 1. : -1.;
DX = x[i] - X;
// Model Yi = Yc +- sqrt[R^2-(x-Xc)^2]
Yi = Y + sign * sqrt(R*R - DX*DX);
gsl_vector_set(f, i, (Yi - y[i]));
}
return GSL_SUCCESS;
}
int circle_df(const gsl_vector *parms, void *data, gsl_matrix *J){
size_t n = ((struct data *)data)->n;
double *x = ((struct data *)data)->x;
double *y = ((struct data *)data)->y;
//double *dy = ((struct data *)data)->dy;
double X = gsl_vector_get(parms, 0);
double Y = gsl_vector_get(parms, 1);
double R = gsl_vector_get(parms, 2);
double sign, Yi, DX;
size_t i;
for(i = 0; i < n; i++){
// Jacobian matrix J(i,j) = dfi / dxj,
// and the xj are the parameters (Xc, Yc, R)
DX = x[i] - X;
sign = (y[i] > Y) ? 1. : -1.;
Yi = sign / sqrt(R*R - DX*DX);
gsl_matrix_set(J, i, 2, R*Yi); // df/dR
gsl_matrix_set(J, i, 1, 1.); // df/dYc
gsl_matrix_set(J, i, 0, DX * Yi); // df/dXc
}
return GSL_SUCCESS;
}
int circle_fdf(const gsl_vector *parms, void *data, gsl_vector *f, gsl_matrix *J){
circle_f(parms, data, f);
circle_df(parms, data, J);
return GSL_SUCCESS;
}
void circle_fit(struct data *d, double *Xcenter,
double *Ycenter, double *Radius){
double
epsabs = 1e-8,
epsrel = 1e-5;
int max_iter = 30;
double x_init[3], c, chi;
const gsl_multifit_fdfsolver_type *T;
gsl_multifit_fdfsolver *s;
int status;
int iter = 0;
size_t n = d->n;
const size_t p = 3;
FNAME();
gsl_matrix *covar = gsl_matrix_alloc (p, p);
gsl_multifit_function_fdf f;
gsl_vector_view xx = gsl_vector_view_array(x_init, p);
const gsl_rng_type *type;
gsl_rng *r;
gsl_rng_env_setup();
type = gsl_rng_default;
r = gsl_rng_alloc(type);
f.f = &circle_f;
f.df = &circle_df;
f.fdf = &circle_fdf;
f.n = n;
f.p = p;
f.params = d;
T = gsl_multifit_fdfsolver_lmder;
s = gsl_multifit_fdfsolver_alloc(T, n, p);
gsl_multifit_fdfsolver_set(s, &f, &xx.vector);
x_init[0] = *Xcenter;
x_init[1] = *Ycenter;
x_init[2] = *Radius;
do{
iter++;
status = gsl_multifit_fdfsolver_iterate(s);
if(status) break;
DBG("iter %d", iter);
status = gsl_multifit_test_delta(s->dx, s->x, epsabs, epsrel);
}while(status == GSL_CONTINUE && iter < max_iter);
DBG("iter end");
gsl_multifit_covar(s->J, 0.0, covar);
chi = gsl_blas_dnrm2(s->f);
c = chi / sqrt((double)(n - p));
*Xcenter = FIT(0);
*Ycenter = FIT(1);
*Radius = FIT(2);
DBG("Number of iteratons = %d\n", iter);
DBG("chi = %g, chi/dof = %g\n", chi, c);
DBG("Xc = %.5f +/- %.5f\n", *Xcenter, c*ERR(0));
DBG("Yc = %.5f +/- %.5f\n", *Ycenter, c*ERR(1));
DBG("R = %.5f +/- %.5f\n", *Radius, c*ERR(2));
gsl_multifit_fdfsolver_free(s);
gsl_matrix_free(covar);
gsl_rng_free(r);
}
*/
#else // GSL not found
//double gaussian_pt(double C, double A, double sigma, double x0, double x){
// GSLERR; return A+C+sigma+x+x0;}
void gaussian_v(double C __attribute__((unused)), double A __attribute__((unused)),
double sigma __attribute__((unused)), double x0 __attribute__((unused)),
Points *pts __attribute__((unused))){GSLERR;}
void gauss_fit(Points *pts __attribute__((unused)), double *C_ __attribute__((unused)),
double *A_ __attribute__((unused)), double *sigma_ __attribute__((unused)),
double *x0_ __attribute__((unused))){ GSLERR;}
#endif // GSL_FOUND

1204
src/gtk.c Normal file

File diff suppressed because it is too large Load Diff

638
src/imtools.c Normal file
View File

@ -0,0 +1,638 @@
// imtools.c - different image transforms
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsheaders.h"
#include "imtools.h"
#include "contours.h"
#include "opengl.h"
#include "gauss.h"
#include "CUtools.h"
// Histogram size, 256 by default
const int HIST_SIZE = 256;
const int MIN_SPOT_NO = 200; // minimum amount of spots on a hartmanogram
const int MAX_SPOT_NO = 258; // max amount -//-
void destroy_image(IMAGE *image){
if(!image) return;
_FREE(image->data);
g_free(image->filename);
if(image->store) // clear parameters from FITS header
gtk_list_store_clear(image->store);
_FREE(image->cLevels); // isolines' intensity
free_contours(image); // isolines itself
_FREE(image);
}
#ifdef LEPTONICA_FOUND
/*
* Convert an image ima (after tresholding)
* into a pixmap for pixConnComp from leptonica
*/
PIX *conv_image2pix(IMAGE *ima){
PIX *pixs;
l_uint32 byte, *pptr;
GLfloat *iptr, *idata;
int w, h, wpl, i, j, k, N;
w = ima->width;
h = ima->height;
pixs = pixCreate(w, h, 1);
wpl = pixGetWpl(pixs);
pptr = pixGetData(pixs);
idata = ima->data;
for(i = h - 1; i > -1; i--){ // lines cycle
N = 0;
iptr = &idata[i * w];
for(j = 0; j < wpl; j++){ // cycle by 32 columns
byte = 0;
for(k = 0; k < 32; k++){ // cycle by a 32-bit word bits
byte <<= 1;
// if string still doesn't end & intensity higher 0.1%
if(N++ < w && *iptr++ > 0.001)
byte |= 1; // set lesser bit
}
*pptr++ = byte;
}
}
return pixs;
}
gboolean check_box(Window *window, BOX *box, int *X0, int *Y0, int *X, int *Y){
if(!window->image->data) return FALSE;
int WD = window->image->width, HT = window->image->height - 1;
if(!(box && X0 && Y0 && X && Y)) return FALSE;
*X0 = box->x;
*Y0 = box->y;
*X = *X0 + box->w;
*Y = *Y0 + box->h; // coordinates of a box corners
if(box->w < 5 || box->h < 5) return FALSE;
if(*X0 < 0 || *X0 > WD || *X < 0 || *X > WD ||
*Y0 < 0 || *Y0 > HT || *Y < 0 || *Y > HT)
return FALSE;
return TRUE;
}
//Calculation of min & max intensity by picture to show it
void compute_minmax(IMAGE *ima){
float min, max, *dst = ima->data;
int i, j, w=ima->width, h=ima->height;
max = min = *dst;
for(i=0; i<h; i++)
for(j=0; j<w; j++, dst++){
float tmp = *dst;
if(tmp > max) max = tmp;
else if(tmp < min) min = tmp;
}
ima->stat.max = max;
ima->stat.min = min;
//ima->width = w; ima->height = h;
DBG("stat: max=%.2f, min=%.2f", max, min);
}
/*
* Calculation of the center of box for image ima
* by inscribing a Gaussian to a vector obtained
* by addition of box columns (for X) and rows (for Y)
*/
gboolean get_gaussian_center(Window *window, BOX *box, Coordinates *crds){
int WD = window->image->width, HT = window->image->height - 1;
int x, y;
GLfloat *data;
Points row, col;
Point *rptr, *cptr;
double x0, sigma;
int X0, Y0, X, Y;
#ifndef GSL_FOUND
return FALSE;
#endif
if(!check_box(window, box, &X0, &Y0, &X, &Y)) return FALSE;
if(!crds) return FALSE;
row.data = calloc(box->w, sizeof(Point));
row.n = box->w;
col.data = calloc(box->h, sizeof(Point));
col.n = box->h;
cptr = col.data;
for(y = Y0; y < Y; y++, cptr++){
data = &window->image->data[(HT - y) * WD + X0];
cptr->x = (double)y;
rptr = row.data;
for(x = X0; x < X; x++, data++, rptr++){
double d = (double) *data;
rptr->y += d;
cptr->y += d;
}
}
rptr = row.data;
for(x = X0; x < X; x++, rptr++) rptr->x = (double)x;
// we inscribe Gaussian and change the the coordinates and half-widths of the spot
gauss_fit(&row, NULL, NULL, &sigma, &x0);
crds->x = x0; crds->rx = sigma;
gauss_fit(&col, NULL, NULL, &sigma, &x0);
crds->y = x0; crds->ry = sigma;
_FREE(row.data); _FREE(col.data);
return TRUE;
}
// count of bits amount in word i
// http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
// http://en.wikipedia.org/wiki/Hamming_weight
int count_bits(l_uint32 i){
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return ((i + ((i >> 4) & 0xF0F0F0F)) * 0x1010101) >> 24;
}
gboolean get_circle_center(Window *window, BOX *box, PIX *pix, Coordinates *crds){
int WD = window->image->width, HT = window->image->height - 1;
int N = 0, X0, Y0, X, Y, n;
l_uint32 *pixdata, *pixptr;
l_int32 w, h, d, wpl, sz, i, x, y;
double Xcenter, Ycenter, Radius, xx, yy, Intens, X2, Y2;
GLfloat *data;
if(!check_box(window, box, &X0, &Y0, &X, &Y)) return FALSE;
if(!(pix && crds)) return FALSE;
pixGetDimensions(pix, &w, &h, &d);
if(d != 1) return FALSE;
wpl = pixGetWpl(pix);
pixdata = pixGetData(pix);
pixptr = pixdata;
sz = wpl * h;
for(i = 0; i < sz; i++) N += count_bits(*pixptr++);
if(N < 200000) return FALSE;
n = 0;
Xcenter = (double)(X0 + X) / 2.;
Ycenter = (double)(Y0 + Y) / 2.;
Radius = (double)(w + h) / 4.;
DBG("x0=%g, y0=%g, r0=%g, N=%d", Xcenter, Ycenter, Radius, N);
Xcenter = Ycenter = X2 = Y2 = Intens = 0.;
AxisX = 0.; AxisY = (double)HT;
for(y = 0; y < h; y++){
yy = (double)(HT - y - Y0);
data = &window->image->data[(HT - y - Y0) * WD + X0];
l_uint32 *line = pixdata + y * wpl;
for(x = 0; x < w; x++, data++){
if(GET_DATA_BIT(line, x)){
double d = *data;
xx= (X0 + x);
n++;
Intens += d;
Xcenter += xx * d;
Ycenter += yy * d;
X2 += xx*xx*d;
Y2 += yy*yy*d;
}
}
}
xx = Xcenter / Intens;
yy = Ycenter / Intens;
crds->x = xx;
crds->y = yy;
crds->rx= sqrt(X2 / Intens - xx*xx);
crds->ry= sqrt(Y2 / Intens - yy*yy);
DBG("xx: %g, yy: %g, rx:%g, ry: %g", xx, yy, crds->rx, crds->ry);
return TRUE;
}
// Detection of spots' center, yet lousy through gaussians
void get_spots_coords(Window *window, IMAGE *ima){
BOXA *boxa;
BOX *box;
PIX *pixs;
Spot spot;
Spots *spots;
Coordinates scrds;
int n, i, k;
float xc, yc;
if(!( /*(window->context->transformMask & TRANS_TRES) &&*/ window->image_transformed->data)){
g_err(_("Define treshold limits first"));
window->context->graphMode = GR_HISTOGRAM;
show_histogram(window);
return;
}
pixs = conv_image2pix(ima);
boxa = pixConnComp(pixs, NULL, 8);
n = boxaGetCount(boxa);
red("Number of boxes: %d", n);
spots_free(&window->spots);
if(!(window->spots = spots_alloc(n))) return;
spots = window->spots;
#ifndef GSL_FOUND
g_err(_("No GSL library found, don't calculate centroids"));
#endif
for(i = 0, k = 0; i < n; i++){
if((box = boxaGetBox(boxa, i, L_CLONE)) == NULL){
g_err(_("Box not found"));
continue;
}
if( box->w < Global->minSpotW ||
box->h < Global->minSpotH ||
box->w > Global->maxSpotW ||
box->h > Global->maxSpotH){
boxDestroy(&box);
continue;
}
if(!get_gaussian_center(window, box, &scrds)){
scrds.rx = ((double)box->w-1.)/2.;
scrds.ry = ((double)box->h-1.)/2.;
xc = box->x + scrds.rx;
yc = box->y + scrds.ry;
scrds.x = xc; scrds.y = yc;
}
spot.c = scrds; spot.id = k++;
spot.box.x = box->x;
spot.box.y = box->y;
spot.box.w = box->w;
spot.box.h = box->h;
spots_add(spots, &spot, BY_COPY);
boxDestroy(&box);
}
red("Number of spots: %d", k);
boxaDestroy(&boxa);
gdk_window_invalidate_rect(window->drawingArea->window,
&window->drawingArea->allocation, FALSE);
DBG("return");
}
void hartmann_spots(Window *window){ // 'r'
Spots *spots = window->spots;
if(!spots){
g_err(_("Find spots first"));
return;
}
int n = spots->n;
if(n > MAX_SPOT_NO){
g_err(_("Too many points"));
return;
}
if(n < MIN_SPOT_NO){
g_err(_("Not enough points"));
return;
}
sort_spots(window);
window->context->transformMask |= TRANS_HARTMANN; // points are sorted
gdk_window_invalidate_rect(window->drawingArea->window,
&window->drawingArea->allocation, FALSE);
}
void get_circles_params(Window *window, IMAGE *ima){
BOXA *boxa;
PIXA *pixa;
BOX *box;
PIX *pixs, *pix;
Spot spot;
Coordinates scrds;
Spots *spots;
int n, i;
if(!( (window->context->transformMask & TRANS_TRES) && window->image_transformed->data)){
g_err(_("Define treshold limits first"));
window->context->graphMode = GR_HISTOGRAM;
show_histogram(window);
return;
}
pixs = conv_image2pix(ima);
boxa = pixConnComp(pixs, &pixa, 8);
n = boxaGetCount(boxa);
red("Number of boxes: %d", n);
spots_free(&window->spots);
if(!(window->spots = spots_alloc(n))) return;
spots = window->spots;
for(i = 0; i < n; i++){
if((box = boxaGetBox(boxa, i, L_CLONE)) == NULL){
g_err(_("Box not found"));
continue;
}
if((pix = pixaGetPix(pixa, i, L_CLONE)) == NULL){
g_err(_("Pix not found"));
continue;
}
if(get_circle_center(window, box, pix, &scrds)){
spot.c = scrds; spot.id = i;
spot.box.x = box->x;
spot.box.y = box->y;
spot.box.w = box->w;
spot.box.h = box->h;
spots_add(spots, &spot, BY_COPY);
}
boxDestroy(&box);
}
boxaDestroy(&boxa);
pixaDestroy(&pixa);
gdk_window_invalidate_rect(window->drawingArea->window,
&window->drawingArea->allocation, FALSE);
DBG("return");
}
#else // LEPTONICA_FOUND not defined
void get_spots_coords(Window *window __attribute__((unused)),
IMAGE *ima __attribute__((unused))){
LEPTERR;
}
gboolean get_gaussian_center(Window *window __attribute__((unused)),
BOX *box __attribute__((unused)), Coordinates *crds __attribute__((unused))){
LEPTERR;
return FALSE;
}
PIX *conv_image2pix(IMAGE *ima __attribute__((unused))){
LEPTERR;
return NULL;
}
void get_circles_params(Window *window __attribute__((unused)),
IMAGE *ima __attribute__((unused))){
LEPTERR;
}
#endif // LEPTONICA_FOUND
/*
* Build the histogram of picture ima
* The default is 256 shades of gray
*/
gboolean make_histogram(IMAGE *ima){
FNAME();
ssize_t i, sz;
int j, pt, *hdata, max, min; // hmax
GLfloat *ptr, hsize, datamin = ima->stat.min, datawd = ima->stat.max - datamin;
#ifdef EBUG
double t0 = dtime();
#endif
if(!ima->data){
g_err(_("Image is empty"));
return FALSE;
}
_FREE(ima->stat.histogram.data);
// to change the size of the histogram replace HIST_SIZE by a variable value
ima->stat.histogram.data = calloc(HIST_SIZE+1, sizeof(int));
if(!ima->stat.histogram.data){
g_err(_("Can't allocate memory for histogram"));
return FALSE;
}
hdata = ima->stat.histogram.data;
ima->stat.histogram.size = HIST_SIZE;
hsize = (GLfloat) HIST_SIZE / datawd;
ima->stat.histogram.scale = 1. / hsize;
//hmax = HIST_SIZE - 1;
ptr = ima->data;
sz = (ssize_t)(ima->width) * (ssize_t)(ima->height);
DBG("datamin=%g, hsize=%g, sz=%zd, scale=%g", datamin, hsize, sz, ima->stat.histogram.scale);
#pragma omp parallel for private(i, pt) shared(hdata)
for(i = 0; i < sz; i++){
pt = (int)((ptr[i] - datamin) * hsize);
#pragma omp atomic
hdata[pt]++;
}
/* for(i = 0; i < sz; i++, ptr++){
pt = (int)(*ptr * hsize);
if(pt < 0) pt = 0;
else if(pt > hmax) pt = hmax;
hdata[pt]++;
}*/
max = min = hdata[0];
for(j = 1; j < HIST_SIZE; j++){
pt = hdata[j];
if(max < pt) max = pt;
else if(min > pt) min = pt;
}
ima->stat.histogram.max = max;
ima->stat.histogram.min = min;
ima->stat.histogram.bot_tres = -1.;
ima->stat.histogram.top_tres = -1.;
DBG("max: %d, min: %d; time: %g", max, min, dtime() - t0);
return TRUE;
}
/*
* Fill the structure image_transformed by adjusted by the threshold values
* Bottom - the lower bound, top - upper bound (in units of image intensity)
* Brightness less than bottom, replaced by zero
* Brightness larger than top, replaced by a unit
* The rest - are calculated linearly (equalization histogram)
*/
void treshold_image(Window *window, GLfloat bottom, GLfloat top){
FNAME();
ssize_t i, sz;
IMAGE *ima = window->image;
IMAGE *i_new = window->image_transformed;
GLfloat *p_old, *p_new, pt, wd;
GLfloat scale = ima->stat.histogram.scale * (GLfloat)ima->stat.histogram.size;
#ifdef EBUG
double t0 = dtime();
#endif
if(!(p_old = ima->data)){
g_err(_("Image is empty"));
return;
}
_FREE(i_new->data);
if((top - bottom) < 0.01) top = bottom + 0.01;
sz = (ssize_t)(ima->width) * (ssize_t)(ima->height);
i_new->data = malloc(sz * sizeof(GLfloat));
i_new->width = ima->width; i_new->height = ima->height;
i_new->bZero = 0.; i_new->bScale = 1.;
i_new->maxVal = ima->maxVal;
i_new->stat.max = 1.;//top * ima->stat.max;
i_new->stat.min = 0.;
p_new = i_new->data;
DBG("top=%g, bottom=%g, scale=%g", top, bottom, ima->stat.histogram.scale);
top = top * scale+ima->stat.min;
bottom = bottom * scale+ima->stat.min;
DBG("top=%g, bottom=%g", top, bottom);
wd = top - bottom;
for(i = 0; i < sz; i++, p_new++, p_old++){
pt = *p_old;
if(pt < bottom) pt = 0.;
else if(pt > top) pt = 1.;
else pt = (pt - bottom) / wd;
*p_new = pt;
}
DBG("time: %g", dtime() - t0);
window->context->transformMask |= TRANS_TRES;
window->context->current_image = window->image_transformed;
gen_texture(window->image_transformed, window, TRUE);
}
const int SINCOSSIZE = 540; // size of the array of sines & cosines
// [-90 , +180) degrees
void hough_lines(Window *window){ // key 'i'
FNAME();
int w, h, Rmax, sz;
double hd, wd;
IMAGE *ima = window->image;
Window *outwin = init_window(window, OPENGL_WINDOW);
IMAGE *i_new = outwin->image;
if(!ima->data){
g_err(_("Image is empty"));
return;
}
_FREE(i_new->data);
w = ima->width; h = ima->height;
wd = (double)w; hd = (double)h;
Rmax = (int)(0.5 + sqrt(wd*wd + hd*hd));
sz = SINCOSSIZE * Rmax;
DBG("sz: %d", sz);
i_new->data = calloc(sz, sizeof(GLfloat));
if(!i_new->data){
g_err(_("Can't allocate memory for Hough transform"));
return;
}
i_new->width = Rmax; i_new->height = SINCOSSIZE;
i_new->bZero = 0.; i_new->bScale = 1.;
i_new->maxVal = 1.;
i_new->dtype = FLOAT_IMG;
gchar *str = malloc(256);
snprintf(str, 255, "Hough transform of file %s", ima->filename);
init_keylist(i_new);
add_key(i_new, "TSTRING", "OBJECT", "Hough", str, TSTRING);
_FREE(str);
#ifdef EBUG
double t0 = dtime();
#endif
if(!fill_hough_lines(ima->data, ima->stat.min, ima->stat.max, w, h, Rmax, SINCOSSIZE, i_new->data)){
g_err(_("Error in Hough transform module"));
destroy_window(outwin);
return;
}
DBG("Hough transform for lines, time: %gs", dtime() - t0);
//Context->transformMask |= TRANS_TRES;
//DBG("HOUGH: maxR=%d, size=%d, max: %g at R=%d, theta=%g",
//i_new->width, i_new->height, max, R%Rmax, ((double)(R/Rmax))/2.-90.);
outwin->context->current_image = outwin->image;
compute_minmax(outwin->image);
gen_texture(outwin->image, outwin, FALSE);
force_redraw(outwin->drawingArea);
outwin->image->imagetype = HOUGH;
CH_WIN_TITLE(outwin, "%s (Hough) %s", PRGNAME, window->image->filename);
}
/*
* respect to the coordinates (x, y) of a click in the window of a Hough transform
* we obtain the parameters of the line (R, phi) and display in the parent window
* the corresponding line
*/
void get_houg_line( Window *window,
double x, double y, // mouse click coordinates
double *phi_, double *R_ // Output: parameters of line
){
double R, phi, ang_sep, sinphi, cosphi;
double x1=0.,y1=0., x2=0.,y2=0., yl,yr, xu,xd, X1, X2, Y1, Y2;
if(!window || !window->parent) return;
double W = window->parent->image->width-1., H = window->parent->image->height-1.;
int rdy = 0;
IMAGE *image = window->image;
conv_mouse_to_image_coords(x, y, &R, &phi, window);
ang_sep = 270. / (double)image->height / 180. * M_PI;
phi = phi*ang_sep - M_PI/2.;
sinphi = sin(phi); cosphi = cos(phi);
phi *= 180./M_PI; // convert rad => deg
DBG("line: phi = %g, R = %g", phi, R);
*phi_ = phi; *R_ = R;
GdkDrawable *d = window->parent->drawingArea->window;
static GdkGC *gc = NULL;
if(!gc){
gc = gdk_gc_new(d);
gdk_gc_set_foreground(gc, &(window->parent->drawingArea->style->white));
gdk_gc_set_function(gc, GDK_XOR);
}
yl = R/sinphi; yr = (R-W*cosphi)/sinphi; // left & right bounds of a picture
xd = R/cosphi; xu = (R-H*sinphi)/cosphi; // down & up bounds
DBG("yl=%g, yr=%g, xd=%g, xu=%g",yl,yr,xd,xu);
if(yl < H && yl > -0.5){
DBG("left selected");
y1 = yl; x1 = 0.; rdy = 1;
}
if(yr < H && yr > -0.5){
DBG("right selected");
if(rdy == 0){
y1 = yr; x1 = W; rdy = 1;
}else{
y2 = yr; x2 = W; rdy = 2; goto lines_selected;
}
}
if(xd < W && xd > -0.5){
DBG("down selected");
if(rdy == 0){
x1 = xd; y1 = 0.; rdy = 1;
}else{
x2 = xd; y2 = 0.; rdy = 2; goto lines_selected;
}
}
if(xu < W && xu > -0.5){
DBG("up selected");
if(rdy == 0){
x1 = xu; y1 = H; rdy = 1;
}else{
x2 = xu; y2 = H; rdy = 2;
}
}
lines_selected:
if(rdy != 2) DBG("WTF? not two points");
else{
DBG("line in image CS: (%g, %g) - (%g, %g)", x1,y1,x2,y2);
conv_image_to_mouse_coords(x1, y1, &X1, &Y1, window->parent);
conv_image_to_mouse_coords(x2, y2, &X2, &Y2, window->parent);
gdk_draw_line(d, gc, X1,Y1, X2,Y2);
DBG("line in window CS: (%g, %g) - (%g, %g)", X1,Y1,X2,Y2);
}
}
// Filtering of an picture window->image by filter f
void filter_image( Window *window,
Filter *f
){
float *src, *dst;
int w,h;
gboolean res = FALSE;
// If picture already transformed
if(window->context->current_image == window->image_transformed){
src = window->image_transformed->data;
w = window->image_transformed->width;
h = window->image_transformed->height;
}else{
src = window->image->data;
w = window->image->width;
h = window->image->height;
if(window->image_transformed) _FREE(window->image_transformed->data);
// window->image_transformed = COPY(window->image, IMAGE);
// window->image_transformed->data = NULL;
}
switch(f->FilterType){
case MEDIAN:
res = MedFilter(src, &dst, f, w, h);
break;
case SIMPLEGRAD:
res = GradFilterSimple(src, &dst, f, w, h);
break;
case STEP: // "posterisation"
res = StepFilter(src, &dst, f, w, h, window->image->stat.min, window->image->stat.max, NULL);
break;
default: // differential filters
res = DiffFilter(src, &dst, f, w, h);
}
if(!res){ g_err(_("Error in image filter module")); return; }
window->context->transformMask |= TRANS_FILTER;
_FREE(src);
window->image->data = dst;
window->context->current_image = window->image;
compute_minmax(window->image);
//gen_texture(window->image_transformed, window, TRUE);
gen_texture(window->image, window, TRUE);
force_redraw(window->drawingArea);
}

51
src/include/CUtools.h Normal file
View File

@ -0,0 +1,51 @@
// CUtools.h - common definitions and functions for CUDA.c and NOCUDA.c
#ifndef _CUTOOLS_H_
#define _CUTOOLS_H_
#ifdef _CUDA_CU_ // file was included from CUDA.cu
#define EXTERN extern "C"
#else
#define EXTERN
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
typedef struct{
unsigned char FilterType; // filter type
int w; // filter width
int h; // height
double sx; // x half-width
double sy; // y half-width (sx, sy - for Gaussian-type filters)
} Filter;
// FilterType
enum{
LAPGAUSS // laplasian of gaussian
,GAUSS // gaussian
,SOBELH // Sobel horizontal
,SOBELV // -//- vertical
,SIMPLEGRAD // simple gradient (by Sobel)
,PREWITTH // Prewitt (horizontal) - simple derivative
,PREWITTV // -//- (vertical)
,MEDIAN // median
,STEP // "posterisation"
};
// f->h for STEP filter
enum{
UNIFORM
,LOG
,EXP
,SQRT
,POW
};
// export section
EXTERN int fill_hough_lines(float *ima, float min, float max, int imW, int imH, int Rmax, int angles, float *hough);
EXTERN int DiffFilter(float *ima, float **result, Filter *f, int sizex, int sizey);
EXTERN int MedFilter(float *ima, float **result, Filter *f, int sizex, int sizey);
EXTERN int fillIsoScale(Filter *f, float **scale, float min, float wd);
EXTERN int StepFilter(float *ima, float **result, Filter *f, int sizex, int sizey, float min, float max, float **scale);
EXTERN int GradFilterSimple(float *ima, float **result, Filter *f, int sizex, int sizey);
#endif // _CUTOOLS_H_

9
src/include/contours.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __CONTOURS_H__
#define __CONTOURS_H__
#include "fitsview.h"
#include "gtk.h"
#include "spots.h"
void find_contours(Window *window, int n, int type);
void free_contours(IMAGE *image);
int copy_contours(IMAGE *in, IMAGE *out, float dX, float dY);
#endif // __CONTOURS_H__

19
src/include/filelist.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef __FILELIST_H__
#define __FILELIST_H__
#include "fitsview.h"
#include "gtk.h"
enum{
CURRENT,
FIRST,
LAST,
PREVIOUS,
NEXT
};
void free_filelist(Window *window);
gint fill_filelist(gchar *filename, Window *window);
gchar *get_filename(guchar type, Window *window);
gboolean get_ext(gchar *filename);
#endif // __FILELIST_H__

11
src/include/fits.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _FITS_H_
#define _FITS_H_
#include "fitsview.h"
#define SCALE_MIN (0.01) // scale smaller than this is wrong
gboolean cmplx_conv(gchar *value, double *re, double *im);
gboolean readfits(gchar *filename, IMAGE *data);
gboolean writefits(gchar *filename, IMAGE *data);
gboolean try_open_file(gchar *filename, IMAGE *data);
gboolean guess_fits(gchar *filename);
#endif // _FITS_H_

20
src/include/fitsheaders.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef _FITSHEADERS_H_
#define _FITSHEADERS_H_
#include "fitsview.h"
#include "gtk.h"
enum{ // key parameters field
L_TYPENM, // type name
L_KEY, // key name
L_VAL, // key value
L_COMM, // comment
L_TYPE, // key type (invisible column, we need you?)
L_MAX // amount of parameters
};
void edit_headers(Window *parent);
void add_key( IMAGE *ima,
gchar *keytype, gchar *keyname, gchar *keyval,
gchar *keycomment, int keyfitstype);
void init_keylist(IMAGE *ima);
#endif

233
src/include/fitsview.h Normal file
View File

@ -0,0 +1,233 @@
#ifndef _FITSVIEW_H_
#define _FITSVIEW_H_
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <unistd.h>
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <math.h>
#include <GL/gl.h>
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <libintl.h> // for locale
#include <sys/time.h> // for dtime
#include <libgen.h> // for basename
#include <fitsio.h>
#include <errno.h>
#include <limits.h>
#include <strings.h>
#include <stdarg.h> // for color text in tty
#include "CUtools.h"
#ifndef GETTEXT_PACKAGE
#define GETTEXT_PACKAGE "fitsview"
#endif
#define PRGNAME GETTEXT_PACKAGE
#ifndef LOCALEDIR
#define LOCALEDIR "/home/eddy/locale"
#endif
#define _(String) gettext(String)
#define gettext_noop(String) String
#define N_(String) gettext_noop(String)
#ifndef THREAD_NUMBER
#define THREAD_NUMBER 2
#endif
#ifndef OMP_NUM_THREADS
#define OMP_NUM_THREADS THREAD_NUMBER
#endif
extern const int HIST_SIZE;
void *copy_struct(void *src, int size);
void *init_struct(int size);
#define INIT(var, type) var = (type*)init_struct(sizeof(type))
#define COPY(src, type) (type*)copy_struct((void*)src, sizeof(type))
#define _FREE(ptr) do{free(ptr); ptr = NULL;}while(0)
/*
* If the distance from the current position of the mouse to a certain point
* less than this value, it is believed that this kind of chosen point
*/
#define PT_TRESHOLD 5
// debug mode, -DEBUG
#ifdef EBUG
#define FNAME() fprintf(stderr, "\n%s (%s, line %d)\n", __func__, __FILE__, __LINE__)
#define DBG(...) do{fprintf(stderr, "%s (%s, line %d): ", __func__, __FILE__, __LINE__); \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "\n");} while(0)
#else
#define FNAME() do{}while(0)
#define DBG(...) do{}while(0)
#endif //EBUG
extern int (*red)(const char *fmt, ...);
extern int (*green)(const char *fmt, ...);
// contour poing
typedef struct _cPoint{
float x; // coordinates of the contour point in the CS picture
float y;
struct _cPoint *prev;// pointers to the previous and next points of the contour
struct _cPoint *next;
} cPoint;
// contour structure
typedef struct _Contour{
int N; // amount of points
cPoint *first; // the first point of the contour (list)
cPoint *last; // last -//-
// struct _Contour *prev; // pointers to the previous and following contour of the group
struct _Contour *next;
unsigned char closed;// == TRUE, if contour is closed
} Contour;
typedef struct{
int L; // level (at the lower boundary of 0, 1, etc)
int N; // number of units in the structure
Contour *first; // pointer to the first contour in the list
Contour *last; // pointer to the last contour in the list
}cList;
// image histogram
typedef struct{
int size; // it is possible that the size of the histogram can be changed
int max; // maximum and minimum values of the histogram (the amplitude)
int min;
double bot_tres; // upper and lower thresholds for equalization (normalized to 1)
double top_tres;
GLfloat scale; // the scale - the range of intensities per unit of histogram
int *data;
} ImageHistogram;
// image statistics
struct ImageStat{
GLfloat max;
GLfloat min;
ImageHistogram histogram;
/*GLfloat median;
GLfloat mean;
GLfloat std;*/
};
// image itself
typedef struct{
int width; // width
int height; // height
float bZero; // conditional zero point
float bScale; // scale
float maxVal; // the limiting value of the intensity (1<<BITTPIX)
double val_f; // the value of a focus in mm (if not defined - '-1e6')
int dtype; // data type
struct ImageStat stat; // image statistica
float *data; // picture data
GtkListStore *store;// list of options for each key
gchar *filename; // file name (basename) for the image
int imagetype; // type of image (for any changes)
cList **contours; // isolines array
int Ncontours; // dimension of the array of isolines
float *cLevels; // array of intensity levels corresponding to each contour
} IMAGE;
// imagetype
enum{
SIMPLE_IMAGE // normal (not converted) image
,HOUGH // Hough transform
};
// the context
typedef struct{
unsigned char drawingMode; // drawing mode: cross section area, etc.
unsigned char trackingMode; // drawing mode sections
unsigned char graphYaxis; // form the Y-axis chart: logarithmic, or common
unsigned char graphMode; // chart mode (chart, histogram, etc.)
unsigned char transformMask;// mask changes made to image_transformed
unsigned char visualMode; // display mode (additional areas, etc.)
IMAGE *current_image; // displayed image (window->image or window->image_transformed)
} Context;
typedef struct{
unsigned char previewMode; // preview settings
gboolean add_all; // try to add all the files (or only .fts / .fit [s])
int minSpotW; // the limit sizes of points
int maxSpotW;
int minSpotH;
int maxSpotH;
} Global_context;
extern Global_context *Global;
/*
* description fields of the structure of context
* the first (zero) should go the default values
*/
// drawingMode:
enum{
DRAW_TRACK // tracking mode
,DRAW_SELECTION // and so on
,DRAW_QUAD
,GAME_MODE
};
// trackingMode:
enum{
TRACK_ANY
,TRACK_VERT
,TRACK_HORIZ
,TRACK_DIAG
};
// graphYaxis, may be masking: LSB - the graph, Sr. - histogram
enum{
Y_LINEAR = 0
,Y_LOGBOTH = 3
,Y_LOGHIST = 2
,Y_LOGGRAPH= 1
};
// graphMode - what is shown on graph (histogram or track)
enum{
GR_GRAPH
,GR_HISTOGRAM
};
// transformMask - bit components
enum{
TRANS_NONE = 0
,TRANS_TRES = 1
,TRANS_ROT = 2
,TRANS_MOVE = 4
,TRANS_SCALE = 8
,TRANS_FILTER = 16
,TRANS_HARTMANN = 32
};
// visualMode - bit components
enum{
SHOW_ONLY_IMAGE = 0
,SHOW_POTBOXES = 1
,SHOW_POTSELECT = 2
,SHOW_ISOLINES = 4
,SHOW_ALL = 255
};
// previewMode - bit components, masks
enum{
PREVIEW_DEFAULT // (512x512, sqrt)
,PREVIEW_128
,PREVIEW_256
,PREVIEW_512
,PREVIEW_768
,PREVIEW_1024
,PREVIEW_SIZEMASK = 7
,PREVIEW_LINEAR = 8
,PREVIEW_LOG = 16
,PREVIEW_SQRT = 24
,PREVIEW_COLFNMASK = 24
};
double dtime();
#endif // _FITSVIEW_H_

20
src/include/gauss.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef _GAUSS_H_
#define _GAUSS_H_
#include "fitsview.h"
#include "tracking.h"
#ifndef GSL_FOUND
#define GSLERR g_err(_("Install GSL library and remake sources for this functions"))
#endif
struct data {
size_t n;
double *x;
double *y;
double *dy;
};
double gaussian_pt(double C, double A, double sigma, double x0, double x);
void gaussian_v(double C, double A, double sigma, double x0, Points *pts);
void gauss_fit(Points *pts, double *C_, double *A_, double *sigma_, double *x0_);
/*void circle_fit(struct data *data, double *Xcenter,
double *Ycenter, double *Radius);*/
#endif // _GAUSS_H_

92
src/include/gtk.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef _GTK_H_
#define _GTK_H_
#include "fitsview.h"
#define EVENT_METHOD(i, x) GTK_WIDGET_GET_CLASS(i)->x
#define CH_WIN_TITLE(wndw, ...) do{ \
gchar *title = g_strdup_printf(__VA_ARGS__); \
gtk_window_set_title(GTK_WINDOW(wndw->window), title);\
g_free(title); \
}while(0)
// Maximum number of cells lines the status window
#define SBAR_MAX 4
// Status bar fields
enum{
StatusText
,StatusState
,StatusCoords
,StatusAdd
};
typedef struct{
GLuint tex; // texture itself
GLuint w; // its size
GLuint h;
} TEXTURE;
typedef struct{
double x;
double y;
} XY;
// List of files in the current directory for this window
typedef struct{
gint list_length;
GList *files_list;
GList *list_current;
GList *list_end;
} FITSlist;
struct Spots;
// window description structure
struct Window{
int id; // identificator: MAIN_WINDOW, GRAPH_WINDOW, OPENGL_WINDOW
GtkWidget *window; // pointer to a window
Context *context; // context of a window
struct Window *parent; // parent window (NULL for the main)
struct Window *graphWindow; // window's graph (while there's no - NULL)
GtkWidget *drawingArea; // drawing area (openGL)
GtkWidget *hRule; // rulers
GtkWidget *vRule;
GtkEntry *SBars[SBAR_MAX]; // an array of pointers to the cells of the status bar
guint statusBlocks; // status bar blocks amount
IMAGE *image; // image for a window
IMAGE *image_transformed; // and its transformation
struct Spots *spots; // spots array for a window
TEXTURE *texture; // texture or VBO buffers
GtkRadioAction *LinLogMenu[2]; // scale lin/log
double Zoom; // zoom scale
double Daspect; // scale in image region (in window)
XY move; // image motions (in window)
XY mouse; // .x, .y - the beginning of the window coordinates in SC of a picture
GLfloat Xangle; // angles rotation relative to the X and Z axes
GLfloat Zangle;
FITSlist files; // files in current directory for this window
};
typedef struct Window Window;
// window id
enum{
MAIN_WINDOW,
GRAPH_WINDOW,
OPENGL_WINDOW,
GL3D_WINDOW
};
extern Window *mainWindow;
void change_image(gchar *filename, Window *window);
void init_main_window(int *argc, char ***argv);
Window *init_window(Window *parent, int winId);
void destroy_window(Window* window);
void run_modal_window(GtkWindow *w, Window *parent);
gint run_modal_dialog(GtkDialog *dialog, Window *parent);
void g_err(gchar *text);
void set_Drulers(double x0, double y0, double xm, double ym, Window *window);
void set_Grulers(double y0, double xm, double ym, Window *window);
void show_histogram(Window *window);
void set_status_text(guint barName, gchar *text, Window *window);
void refresh_state(Window *window);
gchar *get_open_filename(Window *window);
void get_prefocal(Window *window, gboolean *prefocal);
#endif // _GTK_H_

19
src/include/imtools.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef _IMTOOLS_H_
#define _IMTOOLS_H_
#include "fitsview.h"
#include "gtk.h"
#include "spots.h"
void destroy_image(IMAGE *image);
void free_contours(IMAGE *image);
gboolean make_histogram(IMAGE *ima);
void treshold_image(Window *window, GLfloat bottom, GLfloat top);
void get_spots_coords(Window *window, IMAGE *ima);
void hartmann_spots(Window *window);
gboolean get_gaussian_center(Window *window, BOX *box, Coordinates *crds);
PIX *conv_image2pix(IMAGE *ima);
void get_circles_params(Window *window, IMAGE *ima);
void hough_lines(Window *window);
void get_houg_line(Window *window, double x, double y, double *phi_, double *R_);
void filter_image(Window *window, Filter *f);
#endif // _IMTOOLS_H_

View File

@ -0,0 +1,8 @@
#ifndef _OPEN_DIALOG_H_
#define _OPEN_DIALOG_H_
#include "gtk.h"
extern gchar *saved_path;
GtkFileChooser *open_fits_dialog();
void gray2rgb(float gray, guchar *rgb);
#endif

21
src/include/opengl.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef _OPENGL_H_
#define _OPENGL_H_
#define GL_GLEXT_PROTOTYPES
#include "fitsview.h"
#include <GL/glu.h>
#include <GL/glext.h>
#include <GL/glut.h>
#include "gtk.h"
extern gboolean useVBO;
void initGl(GtkWidget *drawingArea);
void gen_texture(IMAGE *image, Window *window, gboolean redraw);
void force_redraw(GtkWidget *Area);
void pickRegion(double x, double y);
void conv_mouse_to_image_coords(double x, double y, double *X, double *Y, Window *window);
void conv_image_to_mouse_coords(double x, double y, double *X, double *Y, Window *window);
void init3D(Window *window);
void getGLinfo(GtkWidget *Area);
void freeGLmemory(Window *window);
#endif // _OPENGL_H_

61
src/include/spots.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef _SPOTS_H_
#define _SPOTS_H_
#include "fitsview.h"
#ifndef LEPTONICA_FOUND
#define LEPTERR g_err(_("Install Leptonica library and remake sources for this functions"))
typedef struct{double x; double y; double w; double h;} BOX;
typedef void* PIX;
#else
#include <leptonica/allheaders.h> // from leptonica
#endif // LEPTONICA_FOUND
/*
* Coordinates of the center of area and its sizes was originally initiated
* based on the box-structures, then adjusted to a coordinate system relative
* to the optical axis, further refined by a Gaussian
* (x and y - as the central parameters of Gaussians,
* rx and ry - as the standard deviation of a Gaussian
*/
typedef struct{
double x;
double y; // center coordinates
double rx;
double ry; // half-sizes
} Coordinates;
// spot parameters
typedef struct{
BOX box; // bounding box
Coordinates c; // dimensions and coordinates in CS relative to the optical axis, without rotation
double r; // module of the radius vector in the CS of the center of spots
double phi; // angular coordinate (from OX counter-clockwise)
double xC; // coordinates of spots in the CS of an optical axis, taking into account the rotation
double yC;
int id; // number, id etc.
} Spot;
// spots array
struct Spots{
int size; // array size
int n; // amount of spots in array
int memflag;// BY_COPY or BY_PTR - method of spots allocation
Spot **spot;// spots themselves
};
typedef struct Spots Spots;
Spots* spots_alloc(int n);
Spot* spots_add(Spots *spots, Spot *spot, int copy);
enum { // definition for the function spots_add
BY_COPY, // copy spot
BY_PTR // only insert pointer to a spot
};
void spots_free(Spots **spots);
struct Window;
void sort_spots(struct Window *window);
void spots_save(Spots *spots, gchar *filename);
extern double AxisX, AxisY;
#endif // _SPOTS_H_

9
src/include/terrain.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __TERRAIN_H__
#define __TERRAIN_H__
#include "fitsview.h"
#include "gtk.h"
void terrain_3D(Window *window, double x0, double y0, double x, double y, gboolean from_file);
#endif // __TERRAIN_H__

31
src/include/tracking.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef _TRACKING_H_
#define _TRACKING_H_
#include "fitsview.h"
#include "gtk.h"
typedef struct{
double x;
double y;
} Point;
typedef struct{
int n;
Point *data;
} Points;
extern double gYmax, gXmax;
extern double xScale, yScale;
void do_tracking(double x, double y, gboolean start, Window *window);
void do_histogram(Window *window);
gboolean graph_mouse_btn(int x, Window *window);
gboolean isYlog(Window *window);
void gen_tres_texture(Window *window);
gboolean Gexpose(Window *window);
gboolean Gconfigure(Window *window);
gboolean fit_gaussian(Window *window);
void get_image_crds(double x, double *xx, double *yy);
void get_sel_region(double *x1, double *y1, double *x2, double *y2);
#endif // _TRACKING_H_

Binary file not shown.

596
src/locale/ru/messages.po Normal file
View File

@ -0,0 +1,596 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-10 17:22+0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: gtk.c:58
#, c-format
msgid "Error: %s\n"
msgstr ""
#: gtk.c:174 fits.c:191
msgid "Can't read fits file"
msgstr ""
#: gtk.c:185
msgid "Can't save file"
msgstr ""
#: gtk.c:367
msgid "Selected line"
msgstr ""
#: gtk.c:473
msgid "Both max values are less than min"
msgstr ""
#: gtk.c:476
msgid "Max height value less than min"
msgstr ""
#: gtk.c:478
msgid "Max width value less than min"
msgstr ""
#: gtk.c:525
msgid "No recognized spots"
msgstr ""
#: gtk.c:1090
msgid "Quit"
msgstr ""
#: gtk.c:1093
msgid "Close"
msgstr ""
#: fitsheaders.c:53
msgid "Type"
msgstr ""
#: fitsheaders.c:53
msgid "Name"
msgstr ""
#: fitsheaders.c:53
msgid "Value"
msgstr ""
#: fitsheaders.c:53
msgid "Comment"
msgstr ""
#: fitsheaders.c:101
msgid "Error in value range!"
msgstr ""
#: fitsheaders.c:120
msgid "Invalid double number!"
msgstr ""
#: fitsheaders.c:132
msgid "Format error: complex number must be in format"
msgstr ""
#: fitsheaders.c:486
msgid "Open fits file first"
msgstr ""
#: fitsheaders.c:513
msgid "New entry (ctrl+n)"
msgstr ""
#: fitsheaders.c:518
msgid "Delete entry (ctrl+d)"
msgstr ""
#: fitsheaders.c:524
msgid "Close (ctrl+w)"
msgstr ""
#: tracking.c:57 tracking.c:62 tracking.c:350
msgid "Can't allocate memory for track points"
msgstr ""
#: tracking.c:483
#, c-format
msgid "Selected region from %.1f to %.1f, %d datapoints"
msgstr ""
#: tracking.c:518
msgid "No parent window or image"
msgstr ""
#: tracking.c:526
msgid "No data points"
msgstr ""
#: tracking.c:531
msgid "Bad amount of data points"
msgstr ""
#: tracking.c:536
msgid "Invalid number of first point"
msgstr ""
#: tracking.c:543 contours.c:394 contours.c:400 contours.c:449 contours.c:453
#: contours.c:456 terrain.c:264 terrain.c:270
msgid "Can't allocate memory"
msgstr ""
#: tracking.c:559
#, c-format
msgid "Fit gaussian, x0=%.2f I(%.2f, %.2f), s=%.2f, A=%.1f, C=%.3f"
msgstr ""
#: spots.c:63
msgid "Too many markers"
msgstr ""
#: spots.c:64
msgid "Not enough markers"
msgstr ""
#: spots.c:65
msgid "Bad markers angle"
msgstr ""
#: spots.c:66
msgid "Too many rings"
msgstr ""
#: spots.c:83
msgid "Can't allocate memory for new spots"
msgstr ""
#: spots.c:88
msgid "Can't allocate memory for pointers in spots array"
msgstr ""
#: spots.c:122
msgid "Spots array is full, can't place new spot\n"
msgstr ""
#: spots.c:126
msgid "Zero pointer to spot\n"
msgstr ""
#: spots.c:132
msgid "Can't allocate memory for new spot"
msgstr ""
#: spots.c:137
msgid "Can't copy spot to new one"
msgstr ""
#: spots.c:499
msgid "Can't open file"
msgstr ""
#: spots.c:511
msgid "Can't write to file"
msgstr ""
#: imtools.c:222 imtools.c:302
msgid "Define treshold limits first"
msgstr ""
#: imtools.c:235
msgid "No GSL library found, don't calculate centroids"
msgstr ""
#: imtools.c:239 imtools.c:318
msgid "Box not found"
msgstr ""
#: imtools.c:274
msgid "Find spots first"
msgstr ""
#: imtools.c:279
msgid "Too many points"
msgstr ""
#: imtools.c:283
msgid "Not enough points"
msgstr ""
#: imtools.c:322
msgid "Pix not found"
msgstr ""
#: imtools.c:377 imtools.c:439 imtools.c:481
msgid "Image is empty"
msgstr ""
#: imtools.c:384
msgid "Can't allocate memory for histogram"
msgstr ""
#: imtools.c:492
msgid "Can't allocate memory for Hough transform"
msgstr ""
#: imtools.c:508
msgid "Error in Hough transform module"
msgstr ""
#: imtools.c:628
msgid "Error in image filter module"
msgstr ""
#: NOCUDA.c:809
msgid "No memory left"
msgstr ""
#: fits.c:284
msgid "Can't read HDU"
msgstr ""
#: fits.c:345
msgid "Not an image? (dimensions != 2)"
msgstr ""
#: opengl.c:34
msgid "OpenGL not supported"
msgstr ""
#: opengl.c:55
msgid ""
"\n"
"OpenGL info:\n"
msgstr ""
#: opengl.c:60
msgid "Color bits"
msgstr ""
#: opengl.c:65
msgid "Depth bits"
msgstr ""
#: opengl.c:66
msgid "Stencil bits"
msgstr ""
#: opengl.c:66
msgid "Max amount of lights"
msgstr ""
#: opengl.c:69
msgid "Max texture size"
msgstr ""
#: opengl.c:70
msgid "Max clip planes"
msgstr ""
#: opengl.c:75
msgid "Max stack depths"
msgstr ""
#: opengl.c:76
msgid "modelview"
msgstr ""
#: opengl.c:76
msgid "projection"
msgstr ""
#: opengl.c:76
msgid "attrib"
msgstr ""
#: opengl.c:77
msgid "texture"
msgstr ""
#: opengl.c:80
msgid "VBO is supported\n"
msgstr ""
#: opengl.c:288
msgid "Empty region"
msgstr ""
#: opengl.c:295
msgid "Selected spot[s]:\n"
msgstr ""
#: opengl.c:297
msgid "Selected spot: "
msgstr ""
#: opengl.c:299
msgid "Selected spots: "
msgstr ""
#: opengl.c:483 terrain.c:321
msgid "Can't allocate memory for texture"
msgstr ""
#: terrain.c:227
msgid "Selected area too small"
msgstr ""
#: terrain.c:233
msgid "Error occured when tried to add contours"
msgstr ""
#: terrain.c:256
msgid "No image in parent window"
msgstr ""
#: terrain.c:332
msgid "Can't generate VBO / GL list"
msgstr ""
#: open_dialog.c:79
msgid "linear"
msgstr ""
#: open_dialog.c:79
msgid "log"
msgstr ""
#: open_dialog.c:79
msgid "square root"
msgstr ""
#: open_dialog.c:96 open_dialog.c:354
msgid "Preview size"
msgstr ""
#: open_dialog.c:105
msgid "Colormap function"
msgstr ""
#: open_dialog.c:142
msgid "Select fits file to open"
msgstr ""
#: open_dialog.c:155
msgid "FITS files"
msgstr ""
#: open_dialog.c:164
msgid "All files"
msgstr ""
#: open_dialog.c:188
msgid "Preview settings"
msgstr ""
#: open_dialog.c:353
msgid "Image size"
msgstr ""
#: open_dialog.c:355
msgid "Preview scale"
msgstr ""
#: fitsview.glade:16 fitsview.glade:448 fitsview.glade:679
msgid "_File"
msgstr ""
#: fitsview.glade:40
msgid "Open in _new window"
msgstr ""
#: fitsview.glade:55
msgid "Open in _3D window"
msgstr ""
#: fitsview.glade:92 fitsview.glade:474 fitsview.glade:713
msgid "_Edit"
msgstr ""
#: fitsview.glade:99 fitsview.glade:481 fitsview.glade:720
msgid "_View"
msgstr ""
#: fitsview.glade:107
msgid "Show _histogram"
msgstr ""
#: fitsview.glade:115
msgid "Show _headers"
msgstr ""
#: fitsview.glade:128
msgid "3D view of full image"
msgstr ""
#: fitsview.glade:136
msgid "3D view of subframe"
msgstr ""
#: fitsview.glade:144
msgid "Select _spots"
msgstr ""
#: fitsview.glade:152
msgid "Draw _tracks"
msgstr ""
#: fitsview.glade:165
msgid "_Zoom frame"
msgstr ""
#: fitsview.glade:173 fitsview.glade:834
msgid "_Restore image"
msgstr ""
#: fitsview.glade:181
msgid "Zoom _in"
msgstr ""
#: fitsview.glade:189
msgid "Zoom _out"
msgstr ""
#: fitsview.glade:201 fitsview.glade:525 fitsview.glade:846
msgid "_Math"
msgstr ""
#: fitsview.glade:209
msgid "Find and enumerate spots"
msgstr ""
#: fitsview.glade:210
msgid "_Spots"
msgstr ""
#: fitsview.glade:218
msgid "Choose minimal & maximal spot size"
msgstr ""
#: fitsview.glade:219
msgid "Size _tresholds"
msgstr ""
#: fitsview.glade:227
msgid "Identify _spots"
msgstr ""
#: fitsview.glade:235
msgid "So_rt hartmann spots"
msgstr ""
#: fitsview.glade:243
msgid "Sa_ve spots"
msgstr ""
#: fitsview.glade:255
msgid "Identify _circles"
msgstr ""
#: fitsview.glade:263
msgid "_Hough transform"
msgstr ""
#: fitsview.glade:271
msgid "_Filter"
msgstr ""
#: fitsview.glade:283
msgid "_Help"
msgstr ""
#: fitsview.glade:489
msgid "Y axis scale"
msgstr ""
#: fitsview.glade:497
msgid "Linear"
msgstr ""
#: fitsview.glade:507
msgid "Log"
msgstr ""
#: fitsview.glade:533
msgid "Approximate by _gaussian"
msgstr ""
#: fitsview.glade:728
msgid "_Move"
msgstr ""
#: fitsview.glade:736
msgid "Rotate X CW"
msgstr ""
#: fitsview.glade:744
msgid "Rotate X CCW"
msgstr ""
#: fitsview.glade:752
msgid "Rotate Z CCW"
msgstr ""
#: fitsview.glade:760
msgid "Rotate Z CW"
msgstr ""
#: fitsview.glade:768
msgid "Move right"
msgstr ""
#: fitsview.glade:776
msgid "Move left"
msgstr ""
#: fitsview.glade:784
msgid "Move down"
msgstr ""
#: fitsview.glade:792
msgid "Move up"
msgstr ""
#: fitsview.glade:800
msgid "Move backward"
msgstr ""
#: fitsview.glade:808
msgid "Move forward"
msgstr ""
#: fitsview.glade:825
msgid "Mouse and arrow keys navigation"
msgstr ""
#: fitsview.glade:826
msgid "_Game mode"
msgstr ""
#: fitsview.glade:942
msgid "Spots' size treshold"
msgstr ""
#: fitsview.glade:986 fitsview.glade:1084
msgid "Min"
msgstr ""
#: fitsview.glade:1018 fitsview.glade:1116
msgid "Max"
msgstr ""
#: fitsview.glade:1050
msgid "<b>Width</b>"
msgstr ""
#: fitsview.glade:1148
msgid "<b>Height</b>"
msgstr ""
#: fitsview.glade:1248
msgid "<b>Focus value, mm</b>"
msgstr ""
#: fitsview.glade:1277
msgid "Prefocal"
msgstr ""
#: fitsview.glade:1290
msgid "Postfocal"
msgstr ""
#: fitsview.glade:1308
msgid "<b>Image type</b>"
msgstr ""

590
src/locale/ru/ru.po Normal file
View File

@ -0,0 +1,590 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-07-15 11:01+0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=koi8-r\n"
"Content-Transfer-Encoding: 8bit\n"
#: opengl.c:53
#, fuzzy
msgid "\n"
"OpenGL info:\n"
msgstr "éÎÆÏÒÍÁÃÉÑ Ï OpenGL:\n"
#: fitsview.glade:128
msgid "3D view of full image"
msgstr "ðÒÏÓÍÏÔÒ ×ÓÅÇÏ ÉÚÏÂÒÁÖÅÎÉÑ × 3D"
#: fitsview.glade:136
msgid "3D view of subframe"
msgstr "ðÒÏÓÍÏÔÒ ÞÁÓÔÉ ÉÚÏÂÒÁÖÅÎÉÑ × 3D"
#: fitsview.glade:1240
msgid "<b>Focus value, mm</b>"
msgstr "<b>æÏËÕÓ, ÍÍ</b>"
#: fitsview.glade:1140
msgid "<b>Height</b>"
msgstr "<b>÷ÙÓÏÔÁ</b>"
#: fitsview.glade:1300
msgid "<b>Image type</b>"
msgstr "<b>ôÉÐ ÉÚÏÂÒÁÖÅÎÉÑ</b>"
#: fitsview.glade:1042
msgid "<b>Width</b>"
msgstr "<b>ûÉÒÉÎÁ</b>"
#: opengl.c:76
msgid "All extensions"
msgstr "÷ÓÅ ÒÁÓÛÉÒÅÎÉÑ"
#: open_dialog.c:164
msgid "All files"
msgstr "÷ÓÅ ÆÁÊÌÙ"
#: fitsview.glade:525
msgid "Approximate by _gaussian"
msgstr "áÐÐÒÏËÓÉÍÁÃÉÑ _ÇÁÕÓÓÉÁÎÏÊ"
#. ÍÁÌÏ ÌÉÂÏ ÍÎÏÇÏ ÔÏÞÅË
#: tracking.c:523
msgid "Bad amount of data points"
msgstr "îÅ×ÅÒÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÔÏÞÅË Ó ÄÁÎÎÙÍÉ"
#: spots.c:68
msgid "Bad markers angle"
msgstr "îÅ×ÅÒÎÙÊ ÕÇÏÌ ÍÅÖÄÕ ÍÁÒËÅÒÁÍÉ"
#: gtk.c:461
msgid "Both max values are less than min"
msgstr "ïÂÅ ÍÁËÓÉÍÁÌØÎÙÅ ×ÅÌÉÞÉÎÙ ÍÅÎØÛÅ ÍÉÎÉÍÁÌØÎÏÊ"
#: imtools.c:217 imtools.c:292
msgid "Box not found"
msgstr "ïÂÌÁÓÔØ ÎÅ ÏÂÎÁÒÕÖÅÎÁ"
#: tracking.c:535 terrain.c:207 terrain.c:213
msgid "Can't allocate memory"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ"
#: imtools.c:445
msgid "Can't allocate memory for Hough transform"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÏÂÒÁÚÁ èÁÆÁ"
#: imtools.c:356
msgid "Can't allocate memory for histogram"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÇÉÓÔÏÇÒÁÍÍÙ"
#: spots.c:138
msgid "Can't allocate memory for new spot"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÎÏ×ÏÊ ÔÏÞËÉ"
#: spots.c:89
msgid "Can't allocate memory for new spots"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÎÏ×ÙÈ ÔÏÞÅË"
#: spots.c:94
msgid "Can't allocate memory for pointers in spots array"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÕËÁÚÁÔÅÌÅÊ × ÍÁÓÓÉ×Å ÔÏÞÅË"
#: opengl.c:425 terrain.c:258
msgid "Can't allocate memory for texture"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÔÅËÓÔÕÒÙ"
#: tracking.c:57 tracking.c:62 tracking.c:351
msgid "Can't allocate memory for track points"
msgstr "îÅ ÍÏÇÕ ×ÙÄÅÌÉÔØ ÐÁÍÑÔØ ÄÌÑ ÔÏÞÅË ÔÒÅËÁ"
#: spots.c:143
msgid "Can't copy spot to new one"
msgstr "îÅ ÍÏÇÕ ÓËÏÐÉÒÏ×ÁÔØ ÔÏÞËÕ"
#: terrain.c:269
msgid "Can't generate VBO / GL list"
msgstr "îÅ ÍÏÇÕ ÓÇÅÎÅÒÉÒÏ×ÁÔØ VBO / GL ÓÐÉÓËÉ"
#: spots.c:507
#, fuzzy
msgid "Can't open file"
msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÆÁÊÌ"
#: fits.c:287
msgid "Can't read HDU"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ HDU"
#: fits.c:192
msgid "Can't read fits file"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ fits ÆÁÊÌ"
#: gtk.c:183
msgid "Can't save file"
msgstr "îÅ ÍÏÇÕ ÓÏÈÒÁÎÉÔØ ÆÁÊÌ"
#: spots.c:519
#, fuzzy
msgid "Can't write to file"
msgstr "îÅ ÍÏÇÕ ÐÒÏÞÅÓÔØ fits ÆÁÊÌ"
#: fitsview.glade:218
msgid "Choose minimal & maximal spot size"
msgstr "÷ÙÂÅÒÉÔÅ ÐÒÅÄÅÌØÎÙÅ ÒÁÚÍÅÒÙ ÐÑÔÅÎ"
#: gtk.c:1084
msgid "Close"
msgstr "úÁËÒÙÔØ"
#: fitsheaders.c:524
msgid "Close (ctrl+w)"
msgstr "úÁËÒÙÔØ (ctrl+w)"
#: opengl.c:58
msgid "Color bits"
msgstr "âÉÔÙ Ã×ÅÔÁ"
#: open_dialog.c:105
msgid "Colormap function"
msgstr "ã×ÅÔÏ×ÁÑ ÆÕÎËÃÉÑ"
#: fitsheaders.c:54
msgid "Comment"
msgstr "ëÏÍÍÅÎÔÁÒÉÊ"
#. (window->context->transformMask & TRANS_TRES) &&
#: imtools.c:200 imtools.c:276
msgid "Define treshold limits first"
msgstr "ïÐÒÅÄÅÌÉÔÅ ÐÏÒÏÇÉ ÎÁ ÇÉÓÔÏÇÒÁÍÍÅ"
#: fitsheaders.c:518
msgid "Delete entry (ctrl+d)"
msgstr "õÄÁÌÉÔØ ÚÁÐÉÓØ (ctrl+d)"
#: opengl.c:63
msgid "Depth bits"
msgstr "âÉÔÙ ÇÌÕÂÉÎÙ"
#: fitsview.glade:152
msgid "Draw _tracks"
msgstr "ðÏÓÔÒÏÅÎÉÅ Ô_ÒÅËÏ×"
#: opengl.c:232
msgid "Empty region"
msgstr "ðÕÓÔÁÑ ÏÂÌÁÓÔØ"
#: imtools.c:462
msgid "Error in Hough transform module"
msgstr "ïÛÉÂËÁ × ÍÏÄÕÌÅ ÐÒÅÏÂÒÁÚÏ×ÁÎÉÑ èÁÆÁ"
#: fitsheaders.c:102
msgid "Error in value range!"
msgstr "ïÛÉÂËÁ × ÄÉÁÐÁÚÏÎÅ ÚÎÁÞÅÎÉÊ!"
#: gtk.c:58
#, c-format
msgid "Error: %s\n"
msgstr "ïÛÉÂËÁ: %s\n"
#: open_dialog.c:155
msgid "FITS files"
msgstr "FITS ÆÁÊÌÙ"
#: fitsview.glade:209
msgid "Find and enumerate spots"
msgstr "òÁÓÐÏÚÎÁ×ÁÎÉÅ ÐÑÔÅÎ"
#: tracking.c:551
#, c-format
msgid "Fit gaussian, x0=%.2f I(%.2f, %.2f), s=%.2f, A=%.1f, C=%.3f"
msgstr "áÐÐÒÏËÓÉÍÁÃÉÑ ÇÁÕÓÓÉÁÎÏÊ, x0=%.2f I(%.2f, %.2f), s=%.2f, A=%.1f, C="
"%.3f"
#: fitsheaders.c:133
msgid "Format error: complex number must be in format"
msgstr "ïÛÉÂËÁ ÆÏÒÍÁÔÁ: ËÏÍÐÌÅËÓÎÏÅ ÞÉÓÌÏ ÄÏÌÖÎÏ ÉÍÅÔØ ÆÏÒÍÁÔ"
#: fitsview.glade:255
msgid "Identify _circles"
msgstr "éÄÅÎÔÉÆÉËÁÃÉÑ _ÏËÒÕÖÎÏÓÔÅÊ"
#: fitsview.glade:227
msgid "Identify _spots"
msgstr "éÄÅÎÔÉÆÉËÁÃÉÑ _ÐÑÔÅÎ"
#: imtools.c:348 imtools.c:398 imtools.c:434
msgid "Image is empty"
msgstr "éÚÏÂÒÁÖÅÎÉÅ ÎÅ ÚÁÇÒÕÖÅÎÏ"
#: open_dialog.c:326
msgid "Image size"
msgstr "òÁÚÍÅÒ ÉÚÏÂÒÁÖÅÎÉÑ"
#: fitsheaders.c:121
msgid "Invalid double number!"
msgstr "îÅ×ÅÒÎÏÅ ÞÉÓÌÏ Ä×ÏÊÎÏÊ ÔÏÞÎÏÓÔÉ!"
#. ÎÏÍÅÒ ÐÅÒ×ÏÊ ÔÏÞËÉ
#. ÎÅ ÔÕÄÁ ÐÏÐÁÌÉ?
#: tracking.c:528
msgid "Invalid number of first point"
msgstr "îÅ×ÅÒÎÙÊ ÎÏÍÅÒ ÐÅÒ×ÏÊ ÔÏÞËÉ"
#: fitsview.glade:489
msgid "Linear"
msgstr "ìÉÎÅÊÎÙÊ"
#: fitsview.glade:499
msgid "Log"
msgstr "ìÏÇÁÒÉÆÍÉÞÅÓËÉÊ"
#: fitsview.glade:1010 fitsview.glade:1108
msgid "Max"
msgstr "íÁËÓÉÍÕÍ"
#: opengl.c:64
msgid "Max amount of lights"
msgstr "íÁËÓÉÍÁÌØÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÉÓÔÏÞÎÉËÏ× ÏÓ×ÅÝÅÎÉÑ"
#: opengl.c:68
msgid "Max clip planes"
msgstr "íÁËÓÉÍÁÌØÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÐÌÏÓËÏÓÔÅÊ ÏÔÓÅÞÅÎÉÑ"
#: gtk.c:464
msgid "Max height value less than min"
msgstr "íÁËÓÉÍÁÌØÎÁÑ ×ÙÓÏÔÁ ÐÑÔÎÁ ÍÅÎØÛÅ ÍÉÎÉÍÁÌØÎÏÊ"
#: opengl.c:73
msgid "Max stack depths"
msgstr "íÁËÓÉÍÁÌØÎÁÑ ÇÌÕÂÉÎÁ ÓÔÅËÁ"
#: opengl.c:67
msgid "Max texture size"
msgstr "íÁËÓÉÍÁÌØÎÙÊ ÒÁÚÍÅÒ ÔÅËÓÔÕÒÙ"
#: gtk.c:466
msgid "Max width value less than min"
msgstr "íÁËÓÉÍÁÌØÎÁÑ ÛÉÒÉÎÁ ÍÅÎØÛÅ ÍÉÎÉÍÁÌØÎÏÊ"
#: fitsview.glade:978 fitsview.glade:1076
msgid "Min"
msgstr "íÉÎÉÍÕÍ"
#: fitsview.glade:817
msgid "Mouse and arrow keys navigation"
msgstr "îÁ×ÉÇÁÃÉÑ ÍÙÛØÀ É ËÌÁ×ÉÁÔÕÒÏÊ"
#: fitsview.glade:792
msgid "Move backward"
msgstr "îÁÚÁÄ"
#: fitsview.glade:776
msgid "Move down"
msgstr "÷ÎÉÚ"
#: fitsview.glade:800
msgid "Move forward"
msgstr "÷ÐÅÒÅÄ"
#: fitsview.glade:768
msgid "Move left"
msgstr "÷ÌÅ×Ï"
#: fitsview.glade:760
msgid "Move right"
msgstr "÷ÐÒÁ×Ï"
#: fitsview.glade:784
msgid "Move up"
msgstr "÷×ÅÒÈ"
#: fitsheaders.c:54
msgid "Name"
msgstr "îÁÚ×ÁÎÉÅ"
#: fitsheaders.c:513
msgid "New entry (ctrl+n)"
msgstr "îÏ×ÁÑ ÚÁÐÉÓØ (ctrl+n)"
#: imtools.c:213
msgid "No GSL library found, don't calculate centroids"
msgstr "îÅ ÏÂÎÁÒÕÖÅÎÁ ÂÉÂÌÉÏÔÅËÁ GSL, ÎÅ ×ÙÞÉÓÌÑÀ ÃÅÎÔÒÏÉÄÙ"
#. ÔÏÞÅË ÎÅÔ? (ï_Ï, Á ÔÁËÏÅ ×ÏÏÂÝÅ ÍÏÖÅÔ ÂÙÔØ?)
#: tracking.c:518
msgid "No data points"
msgstr "ïÔÓÕÔÓÔ×ÕÀÔ ÔÏÞËÉ Ó ÄÁÎÎÙÍÉ"
#: terrain.c:199
msgid "No image in parent window"
msgstr "÷ ÒÏÄÉÔÅÌØÓËÏÅ ÏËÎÏ ÎÅ ÚÁÇÒÕÖÅÎÏ ÉÚÏÂÒÁÖÅÎÉÅ"
#: tracking.c:510
msgid "No parent window or image"
msgstr "ïÔÓÕÔÓÔ×ÕÅÔ ÒÏÄÉÔÅÌØÓËÏÅ ÏËÎÏ ÉÌÉ ÉÚÏÂÒÁÖÅÎÉÅ × ÎÅÍ"
#. "ïÔÓÕÔÓÔ×ÕÀÔ ÒÁÓÐÏÚÎÁÎÎÙÅ ÐÑÔÎÁ"
#: gtk.c:514
msgid "No recognized spots"
msgstr "ïÔÓÕÔÓÔ×ÕÀÔ ÒÁÓÐÏÚÎÁÎÎÙÅ ÐÑÔÎÁ"
#: fits.c:356
msgid "Not an image? (dimensions != 2)"
msgstr "FITS-ÆÁÊÌ ÎÅ Ñ×ÌÑÅÔÓÑ ÉÚÏÂÒÁÖÅÎÉÅÍ? (ÒÁÚÍÅÒÎÏÓÔØ ÎÅ ÒÁ×ÎÁ Ä×ÕÍ)"
#: spots.c:67
msgid "Not enough markers"
msgstr "îÅÄÏÓÔÁÔÏÞÎÏÅ ËÏÌÉÞÅÓÔ×Ï ÍÁÒËÅÒÏ×"
#: imtools.c:257
msgid "Not enough points"
msgstr "îÅÄÏÓÔÁÔÏÞÎÏ ÔÏÞÅË"
#: fitsheaders.c:486
msgid "Open fits file first"
msgstr "÷ÎÁÞÁÌÅ ÏÔËÒÏÊÔÅ fits-ÆÁÊÌ"
#: fitsview.glade:55
msgid "Open in _3D window"
msgstr "ïÔËÒÙÔØ × ÏËÎÅ _3D"
#: fitsview.glade:40
msgid "Open in _new window"
msgstr "ïÔËÒÙÔØ × _ÎÏ×ÏÍ ÏËÎÅ"
#: opengl.c:32
msgid "OpenGL not supported"
msgstr "OpenGL ÎÅ ÐÏÄÄÅÒÖÉ×ÁÅÔÓÑ ×ÁÛÅÊ ÓÉÓÔÅÍÏÊ"
#: imtools.c:296
msgid "Pix not found"
msgstr "íÁÓËÁ ÎÅ ÏÂÎÁÒÕÖÅÎÁ"
#: fitsview.glade:1282
msgid "Postfocal"
msgstr "úÁÆÏËÁÌØÎÏÅ"
#: fitsview.glade:1269
msgid "Prefocal"
msgstr "ðÒÅÄÆÏËÁÌØÎÏÅ"
#: open_dialog.c:328
msgid "Preview scale"
msgstr "íÁÓÛÔÁ ÐÒÅÄÐÒÏÓÍÏÔÒÁ"
#: open_dialog.c:188
msgid "Preview settings"
msgstr "îÁÓÔÒÏÊËÉ ÐÒÅÄÐÒÏÓÍÏÔÒÁ"
#: open_dialog.c:96 open_dialog.c:327
msgid "Preview size"
msgstr "òÁÚÍÅÒ ÐÒÅÄÐÒÏÓÍÏÔÒÁ"
#: gtk.c:1081
msgid "Quit"
msgstr "_÷ÙÈÏÄ"
#: fitsview.glade:736
msgid "Rotate X CCW"
msgstr "÷ÒÁÝÁÔØ ÐÒÏÔÉ× þó ×ÏËÒÕÇ ÏÓÉ X"
#: fitsview.glade:728
msgid "Rotate X CW"
msgstr "÷ÒÏÝÁÔØ ÐÏ þó ×ÏËÒÕÇ ÏÓÉ X"
#: fitsview.glade:744
msgid "Rotate Z CCW"
msgstr "÷ÒÁÝÁÔØ ÐÒÏÔÉ× þó ×ÏËÒÕÇ ÏÓÉ Z"
#: fitsview.glade:752
msgid "Rotate Z CW"
msgstr "÷ÒÏÝÁÔØ ÐÏ þó ×ÏËÒÕÇ ÏÓÉ Z"
#: fitsview.glade:243
msgid "Sa_ve spots"
msgstr "óÏ_ÈÒÁÎÉÔØ ÔÏÞËÉ"
#: fitsview.glade:144
msgid "Select _spots"
msgstr "÷ÙÂÏÒ _ÔÏÞÅË"
#: open_dialog.c:142
msgid "Select fits file to open"
msgstr "÷ÙÂÅÒÉÔÅ, ËÁËÏÊ fits-ÆÁÊÌ ÏÔËÒÙÔØ"
#: terrain.c:177
msgid "Selected area too small"
msgstr "÷ÙÄÅÌÅÎÎÁÑ ÏÂÌÁÓÔØ ÓÌÉÛËÏÍ ÍÁÌÁ"
#: gtk.c:358
msgid "Selected line"
msgstr "÷ÙÂÒÁÎÁ ÌÉÎÉÑ"
#: tracking.c:475
#, c-format
msgid "Selected region from %.1f to %.1f, %d datapoints"
msgstr "÷ÙÄÅÌÅÎÁ ÏÂÌÁÓÔØ ÏÔ %.1f ÄÏ %.1f, %d ÔÏÞÅË"
#: opengl.c:241
msgid "Selected spot: "
msgstr "÷ÙÂÒÁÎÏ ÐÑÔÎÏ: "
#: opengl.c:239
msgid "Selected spot[s]:\n"
msgstr "÷ÙÂÒÁÎÙ ÐÑÔÎÁ:\n"
#: opengl.c:243
msgid "Selected spots: "
msgstr "÷ÙÂÒÁÎÙ ÐÑÔÎÁ: "
#: fitsview.glade:115
msgid "Show _headers"
msgstr "ïÔÏÂÒÁÚÉÔØ _ÚÁÇÏÌÏ×ËÉ"
#: fitsview.glade:107
msgid "Show _histogram"
msgstr "ïÔÏÂÒÁÚÉÔØ _ÇÉÓÔÏÇÒÁÍÍÕ"
#: fitsview.glade:219
msgid "Size _tresholds"
msgstr "ïÇÒÁÎÉÞÅÎÉÅ ÒÁÚÍÅÒÏ×"
#: fitsview.glade:235
msgid "So_rt hartmann spots"
msgstr "óÏÒÔÉÒÏ×ÁÔØ ÐÑÔÎÁ ÇÁÒÔÍÁÎÏÇÒÁÍÍÙ"
#: spots.c:128
msgid "Spots array is full, can't place new spot\n"
msgstr "íÁÓÓÉ× ÔÏÞÅË ÐÏÌÏÎ, ÎÅ ÍÏÇÕ ÄÏÂÁ×ÉÔØ ÎÏ×ÕÀ ÔÏÞËÕ\n"
#: fitsview.glade:934
msgid "Spots' size treshold"
msgstr "ïÇÒÁÎÉÞÅÎÉÅ ÒÁÚÍÅÒÏ× ÐÑÔÅÎ"
#: opengl.c:64
msgid "Stencil bits"
msgstr "âÉÔÙ ÔÒÁÆÁÒÅÔÁ"
#: spots.c:66
msgid "Too many markers"
msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ÍÁÒËÅÒÏ×"
#: imtools.c:253
msgid "Too many points"
msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ÔÏÞÅË"
#: spots.c:69
msgid "Too many rings"
msgstr "óÌÉÛËÏÍ ÍÎÏÇÏ ËÏÌÅÃ"
#. ËÏÌÉÞÅÓÔ×Ï ÚÁÐÉÓÅÊ
#: fitsheaders.c:54
msgid "Type"
msgstr "ôÉÐ"
#: opengl.c:78
msgid "VBO is supported\n"
msgstr "ðÏÄÄÅÒÖÉ×ÁÀÔÓÑ ÂÕÆÅÒÙ ÏÂßÅËÔÏ×\n"
#: fitsheaders.c:54
msgid "Value"
msgstr "úÎÁÞÅÎÉÅ"
#: fitsview.glade:481
msgid "Y axis scale"
msgstr "íÁÓÛÔÁ ÏÓÉ Y"
#: spots.c:132
msgid "Zero pointer to spot\n"
msgstr "îÕÌÅ×ÏÊ ÕËÁÚÁÔÅÌØ ÎÁ ÔÏÞËÕ\n"
#: fitsview.glade:181
msgid "Zoom _in"
msgstr "õ_×ÅÌÉÞÉÔØ"
#: fitsview.glade:189
msgid "Zoom _out"
msgstr "õ_ÍÅÎØÛÉÔØ"
#: fitsview.glade:92 fitsview.glade:466 fitsview.glade:705
msgid "_Edit"
msgstr "_éÚÍÅÎÉÔØ"
#: fitsview.glade:16 fitsview.glade:440 fitsview.glade:671
msgid "_File"
msgstr "_æÁÊÌ"
#: fitsview.glade:818
msgid "_Game mode"
msgstr ""
#: fitsview.glade:275
msgid "_Help"
msgstr "_ðÏÍÏÝØ"
#: fitsview.glade:263
msgid "_Hough transform"
msgstr "ðÒÅÏÂÒÁÚÏ×ÁÎÉÅ _èÁÆÁ"
#: fitsview.glade:201 fitsview.glade:517 fitsview.glade:838
msgid "_Math"
msgstr "_íÁÔÅÍÁÔÉËÁ"
#: fitsview.glade:720
msgid "_Move"
msgstr "_ðÅÒÅÍÅÓÔÉÔØ"
#: fitsview.glade:173 fitsview.glade:826
msgid "_Restore image"
msgstr "_÷ÏÓÓÔÁÎÏ×ÉÔØ ÍÁÓÛÔÁÂ"
#: fitsview.glade:210
msgid "_Spots"
msgstr "_ðÑÔÎÁ"
#: fitsview.glade:99 fitsview.glade:473 fitsview.glade:712
msgid "_View"
msgstr "_÷ÉÄ"
#: fitsview.glade:165
msgid "_Zoom frame"
msgstr "_õ×ÅÌÉÞÅÎÉÅ ÒÁÍËÏÊ"
#: opengl.c:74
msgid "attrib"
msgstr "ÁÔÒÉÂÕÔÏ×ÙÊ"
#: open_dialog.c:79
msgid "linear"
msgstr "ÌÉÎÅÊÎÁÑ"
#: open_dialog.c:79
msgid "log"
msgstr "ÌÏÇÁÒÉÆÍÉÞÅÓËÁÑ"
#: opengl.c:74
msgid "modelview"
msgstr "ÍÏÄÅÌØÎÏ-×ÉÄÏ×ÙÊ"
#: opengl.c:74
msgid "projection"
msgstr "ÐÒÏÅËÃÉÊ"
#: open_dialog.c:79
msgid "square root"
msgstr "Ë×ÁÄÒÁÔÎÙÊ ËÏÒÅÎØ"
#: opengl.c:75
msgid "texture"
msgstr "ÔÅËÓÔÕÒ"
#~ msgid "You sure, you want exit?"
#~ msgstr "÷Ù Õ×ÅÒÅÎÙ, ÞÔÏ ÈÏÔÉÔÅ ×ÙÊÔÉ?"

2
src/locale/ru/update Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
msgmerge -Uis ru.po messages.po

433
src/open_dialog.c Normal file
View File

@ -0,0 +1,433 @@
// open_dialog.c - open file dialog with preview
// Copyright:
// Guillaume Chazarain <guichaz@gmail.com> (gliv project)
// 2011 Edward V. Emelianoff <eddy@sao.ru> (modification for fits open)
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsview.h"
#include "open_dialog.h"
gchar *saved_path = NULL;
typedef struct{
GtkLabel *label;
GtkWidget *image;
} fits_preview;
GdkPixbuf *getfits(char *filename, gchar **descr);
void update_preview( GtkFileChooser *file_chooser,
fits_preview *FP){
GtkWidget *preview = FP->image;
GtkLabel *ilabel = FP->label;
char *filename;
gchar *descr = NULL;
GdkPixbuf *pixbuf = NULL;
filename = gtk_file_chooser_get_preview_filename(file_chooser);
if (filename == NULL) return;
pixbuf = getfits(filename, &descr);
if(pixbuf){
gint w = gdk_pixbuf_get_width(pixbuf);
w = w - w/4;
if(w < 200) w = 200;
gtk_widget_set_size_request(GTK_WIDGET(ilabel), w, -1);
gtk_label_set_text(ilabel, descr);
}
g_free(filename);
gtk_image_set_from_pixbuf(GTK_IMAGE(preview), pixbuf);
if(pixbuf) g_object_unref(pixbuf);
gtk_file_chooser_set_preview_widget_active(file_chooser, (pixbuf != NULL));
}
void cb_changed(GtkComboBox *combo, GtkObject *chooser){
GtkTreeIter iter;
GtkTreeModel *model;
int val;
gtk_combo_box_get_active_iter(combo, &iter);
model = gtk_combo_box_get_model(combo);
gtk_tree_model_get(model, &iter, 1, &val, -1);
// clear needed config bits
if(val & PREVIEW_SIZEMASK) Global->previewMode &= ~PREVIEW_SIZEMASK;
else if(val & PREVIEW_COLFNMASK) Global->previewMode &= ~PREVIEW_COLFNMASK;
Global->previewMode |= val; // set new
gtk_signal_emit_by_name(chooser, "update-preview"); // redraw
}
// arguments of funtion which creates menu
enum{
COMBO_SIZE,
COMBO_COLORFN
};
GtkWidget *create_combo(unsigned char type, GtkObject *chooser){
#define SIZES 5
#define COLORFNS 3
gchar *sizes[] = {"128 x 128", "256 x 256", "512 x 512",
"768 x 768", "1024 x 1024"};
gchar *colorfns[] = {N_("linear"), N_("log"), N_("square root")};
gchar **ptr;
GtkWidget *label = NULL, *vbox = NULL;
gchar *label_text = NULL;
int curpos, i, n;
int startpos = 0; // bit position of appropriate numbers
GtkListStore *store;
GtkWidget *combo;
GtkCellRenderer *cell;
GtkTreeIter iter;
if(type == COMBO_SIZE){
ptr = sizes; n = SIZES;
if((curpos = Global->previewMode & PREVIEW_SIZEMASK))
curpos--;
else
curpos = PREVIEW_512 - 1;
startpos = 0;
label_text = g_strdup_printf(_("Preview size"));
}
else if(type == COMBO_COLORFN){
ptr = colorfns; n = COLORFNS;
if((curpos = (Global->previewMode & PREVIEW_COLFNMASK) >> 3))
curpos--;
else
curpos = (PREVIEW_SQRT>>3) - 1;
startpos = 3;
label_text = g_strdup_printf(_("Colormap function"));
}
else return NULL;
vbox = gtk_vbox_new(FALSE, 3);
label = gtk_label_new(label_text);
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
for(i = 0; i < n; i++){
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, 0, _(ptr[i]), 1, (i+1)<<startpos, -1);
}
combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
gtk_box_pack_start(GTK_BOX(vbox), combo, FALSE, FALSE, 0);
cell = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 0, NULL);
gtk_combo_box_set_active(GTK_COMBO_BOX(combo), curpos);
g_signal_connect(combo, "changed", G_CALLBACK(cb_changed), (gpointer)chooser);
g_object_unref(store);
free(label_text);
return vbox;
}
//GtkFileChooser *open_fits_dialog(gboolean select_dir){
GtkFileChooser *open_fits_dialog(){
GtkLabel *ilabel = NULL;
GtkImage *preview = NULL;
GtkWidget *vbox = NULL, *combo = NULL, *frame = NULL, *sbox = NULL;
GtkFileChooserAction action;
const gchar *label;
GtkFileChooser *chooser;
static fits_preview FP;
/* if (select_dir) {
action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
label = _("Select a folder to open");
} else { */
action = GTK_FILE_CHOOSER_ACTION_OPEN;
label = _("Select fits file to open");
//}
chooser = GTK_FILE_CHOOSER(
gtk_file_chooser_dialog_new(label, NULL,
action,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN,
GTK_RESPONSE_ACCEPT,
NULL)
);
// it CAN CAUSED MEMORY LEAKS: filter doesn't deletes
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, _("FITS files"));
gtk_file_filter_add_pattern(filter, "*.fit");
gtk_file_filter_add_pattern(filter, "*.FIT");
gtk_file_filter_add_pattern(filter, "*.fts");
gtk_file_filter_add_pattern(filter, "*.FTS");
gtk_file_filter_add_pattern(filter, "*.fits");
gtk_file_filter_add_pattern(filter, "*.FITS");
gtk_file_chooser_add_filter(chooser, filter);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, _("All files"));
gtk_file_filter_add_pattern(filter, "*");
gtk_file_chooser_add_filter(chooser, filter);
if(saved_path)
gtk_file_chooser_set_current_folder(chooser, saved_path);
// if (!select_dir) {
vbox = gtk_vbox_new(FALSE, 8);
ilabel = GTK_LABEL(gtk_label_new(NULL));
gtk_label_set_line_wrap(ilabel, TRUE);
gtk_label_set_max_width_chars(ilabel, 80);
gtk_label_set_line_wrap_mode(ilabel, PANGO_WRAP_WORD);
// either wrap or angle
//gtk_label_set_angle(ilabel, 30.);
preview = GTK_IMAGE(gtk_image_new());
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(preview),
FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(ilabel),
FALSE, FALSE, 0);
FP.label = ilabel; FP.image = GTK_WIDGET(preview);
//gtk_file_chooser_set_select_multiple(chooser, TRUE);
gtk_widget_show_all(GTK_WIDGET(vbox));
gtk_file_chooser_set_preview_widget(chooser, GTK_WIDGET(vbox));
vbox = gtk_hbox_new(FALSE, 10);
frame = gtk_frame_new(_("Preview settings"));
gtk_box_pack_end(GTK_BOX(vbox), frame, FALSE, FALSE, 0);
sbox = gtk_hbox_new(FALSE, 10);
gtk_container_add(GTK_CONTAINER(frame), sbox);
combo = create_combo(COMBO_SIZE, GTK_OBJECT(chooser));
gtk_box_pack_start(GTK_BOX(sbox), combo, FALSE, FALSE, 0);
combo = create_combo(COMBO_COLORFN, GTK_OBJECT(chooser));
gtk_box_pack_start(GTK_BOX(sbox), combo, FALSE, FALSE, 0);
gtk_widget_show_all(frame);
gtk_file_chooser_set_extra_widget(chooser, vbox);
g_signal_connect(chooser, "update-preview",
G_CALLBACK(update_preview), (gpointer)&FP);
// }
return chooser;
}
/*
// gray - intensity from 0.f to 1.f
void gray2rgb(float gray, guchar *rgb){
int i = (int)(gray * 4.);
float x = gray - (float)i * .25;
guchar r = 0, g = 0, b = 0;
switch(i){
case 0:
b = (guchar)(255. * x);
break;
case 1:
b = 255;
g = (guchar)(255. * x);
break;
case 2:
r = (guchar)(255. * x);
g = 255;
b = (guchar)(255. * (1. - x));
break;
case 3:
r = 255;
g = (guchar)(255. * (1. - x));
break;
default:
if(i>3) r=255;
}
*rgb++ = r;
*rgb++ = g;
*rgb++ = b;
}*/
void gray2rgb(float gray, guchar *rgb){
int i = (int)(gray * 4.);
float x = gray - (float)i * .25;
guchar r = 0, g = 0, b = 0;
switch(i){
case 0:
g = (guchar)(255. * x);
b = 255;
break;
case 1:
g = 255;
b = (guchar)(255. * (1. - x));
break;
case 2:
r = (guchar)(255. * x);
g = 255;
break;
case 3:
r = 255;
g = (guchar)(255. * (1. - x));
break;
default:
r = 255;
}
*rgb++ = r;
*rgb++ = g;
*rgb++ = b;
}
GdkPixbufDestroyNotify free_preview_data(guchar *pixels, gpointer data){
free(pixels);
free(data);
return FALSE;
}
// get preview from FITS file filename
GdkPixbuf *getfits(char *filename, gchar **descr){
gboolean status;
fitsfile *fp;
double (*colorfun)(double);
double linfun(double arg){ return arg; } // bung for PREVIEW_LINEAR
double logfun(double arg){ return log(1.+arg); } // for PREVIEW_LOG
gchar *description = NULL; // some keywords from FITS header
#define TRYFITS(f, ...) \
do{ status = FALSE; \
f(__VA_ARGS__, &status); \
if(status){ \
free(ima_data); free(pixbuf_data); \
free(description); free(pix); \
fits_close_file(fp, &status); \
return NULL;} \
}while(0)
void add_keyw(char *keyw){
char keyval[FLEN_VALUE], *ptr;
if(VALUE_UNDEFINED == fits_read_key(fp,
TSTRING, keyw, keyval, NULL, &status)) return;
if(status) return;
ptr = g_strdup_printf("%s;\t%s=%s", description, keyw, keyval);
free(description);
description = ptr;
}
int MAX_SIZE = 512; // max preview size
unsigned char cntxt;
GdkPixbuf *pixbuf = NULL;
float nullval = 0.;
int i, j, k, l, N, M, stat;
int naxis, w, h, pixScale, Ws, Hs, dtype;
int sz;
cntxt = Global->previewMode & PREVIEW_SIZEMASK; // get preview size
if(cntxt)
switch(cntxt){
case PREVIEW_128: MAX_SIZE = 128;
break;
case PREVIEW_256: MAX_SIZE = 256;
break;
case PREVIEW_512: MAX_SIZE = 512;
break;
case PREVIEW_768: MAX_SIZE = 768;
break;
case PREVIEW_1024:MAX_SIZE = 1024;
break;
}
// array for preview picture line
float *pix = malloc(MAX_SIZE * sizeof(float));
long naxes[4];
float *ima_data = NULL, *ptr, byte, n, m, max, min, wd, avr;
guchar *pptr, *pixbuf_data = NULL;
DBG("Try to open file %s\n", filename);
TRYFITS(fits_open_file, &fp, filename, READONLY);
TRYFITS(fits_get_img_param, fp, 4, &dtype, &naxis, naxes);
if(naxis != 2) return NULL;
w = naxes[0];
h = naxes[1];
sz = w * h;
ima_data = malloc(sz * sizeof(float));
pixbuf_data = malloc(3 * MAX_SIZE * MAX_SIZE * sizeof(guchar));
TRYFITS(fits_read_img, fp, TFLOAT, 1, sz, &nullval, ima_data, &stat);
ptr = ima_data;
min = max = *ptr; avr = 0.;
// get statistics:
for(i=0; i<h; i++)
for(j=0; j<w; j++, ptr++){
float tmp = *ptr;
if(tmp > max) max = tmp;
else if(tmp < min) min = tmp;
avr += tmp;
}
avr /= (float)sz;
wd = max - min;
i = (int)ceil((float)w / MAX_SIZE);
j = (int)ceil((float)h / MAX_SIZE);
DBG("i=%d, j=%d, ms=%d",i,j,MAX_SIZE);
pixScale = (i > j) ? i : j; // picture scale factor
Ws = w / pixScale; // picture width in pixScale blocks
Hs = h / pixScale; // -//- height pixScale
DBG("w=%d, h=%d, Ws=%d, Hs=%d, pixScale=%d",w,h,Ws,Hs,pixScale);
// prepare a comment to a prewiew:
description = g_strdup_printf(
"%s=%dx%d\n%s=%dx%d\n%s=%.1f%%;\tMax=%g,\tMin=%g,\tAvr=%g",
_("Image size"), w, h,
_("Preview size"), Ws, Hs,
_("Preview scale"), 100./(double)pixScale,
max, min, avr
);
add_keyw("BITPIX");
add_keyw("IMAGETYP");
add_keyw("OBJECT");
add_keyw("EXPTIME"); add_keyw("EXP");
add_keyw("AUTHOR");
add_keyw("DATE");
M = 0; // line number
for(i = 0; i < Hs; i++){ // cycle through a blocks by lines
//pptr = &pixbuf_data[i * Ws * 3];
for(j = 0; j < MAX_SIZE; j++) pix[j] = 0;
m = 0.; // amount of strings read in block
for(l = 0; l < pixScale; l++, m++){ // cycle through a block lines
ptr = &ima_data[M * w];
N = 0; // number of column
for(j = 0; j < Ws; j++){ // cycle through a blocks by columns
n = 0.; // amount of columns read in block
byte = 0.; // average intensity in block
for(k = 0; k < pixScale; k++, n++){ // cycle through block pixels
if(N++ < w) // row didn't end
byte += *ptr++; // sum[(pix-min)/wd]/n = [sum(pix)/n-min]/wd
else break;
}
pix[j] += byte / n;//(byte / n - min)/wd;
}
if(++M >= h) break;
}
// fill unused picture pixels
ptr = &ima_data[i*Ws];
for(l = 0; l < Ws; l++)
*ptr++ = pix[l] / m;
}
ptr = ima_data;
sz = Ws * Hs;
max = min = *ptr;
avr = 0;
for(i=0; i < sz; i++, ptr++){
float tmp = *ptr;
if(tmp > max) max = tmp;
else if(tmp < min) min = tmp;
avr += tmp;
}
avr /= (float)sz;
wd = max - min;
avr = (avr - min) / wd; // normal average by preview
avr = -log(avr); // scale factor
if(avr > 1.) wd /= avr;
ptr = ima_data;
colorfun = sqrt;
if((Global->previewMode & PREVIEW_COLFNMASK) == PREVIEW_LINEAR)
colorfun = linfun;
else if((Global->previewMode & PREVIEW_COLFNMASK) == PREVIEW_LOG)
colorfun = logfun;
for(i = Hs - 1; i > -1; i--){// fill pixbuf mirroring image by vertical
pptr = &pixbuf_data[Ws * i * 3];
for(j = 0; j < Ws; j++){
gray2rgb(colorfun((*ptr++ - min) / wd), pptr);
pptr += 3;
}
}
fits_close_file(fp, &status);
pixbuf = gdk_pixbuf_new_from_data(
pixbuf_data, // guchar* data
GDK_COLORSPACE_RGB, // only this supported
FALSE, // no alpha
8, // number of bits in byte (WTF? who is this idiot?)
Ws, Hs, // size
Ws * 3, // line length in bytes
(GdkPixbufDestroyNotify)free_preview_data, // function (*GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
(gpointer)description // pointer data
);
free(ima_data);
free(pix);
*descr = description;
DBG("OK, preview is ready, previewMode = %d", Global->previewMode);
return pixbuf;
}

731
src/opengl.c Normal file
View File

@ -0,0 +1,731 @@
// opengl.c - functions to work with openGL
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "opengl.h"
#include "spots.h"
#include "imtools.h"
#include "contours.h"
#include "open_dialog.h"
#define BUFSIZE 512 // select buffer sizw
GLdouble ObjX = 0., ObjY = 0.; // mouse click coordinates (in window CS)
gboolean forcesel = FALSE;
gboolean useVBO = FALSE; // use VBO or lists
// these functions are absent in headers
void glBlendEquation(GLenum);
void glBlendFuncSeparate(GLenum, GLenum, GLenum, GLenum);
#define BADOGL() do{g_err(_("OpenGL not supported")); \
g_assert_not_reached();}while(0)
gboolean queryExtension(char *extName){
char *p = (char *)glGetString(GL_EXTENSIONS);
char *end = p + strlen(p);
while(p < end){
size_t n = strcspn(p, " ");
if((strlen(extName)==n) && (strncmp(extName,p,n)==0))
return TRUE;
p += (n + 1);
}
return FALSE;
}
void getGLinfo(GtkWidget *Area){
GLint a,b,c,d;
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
g_print(_("\nOpenGL info:\n"));
glGetIntegerv(GL_RED_BITS, &a);
glGetIntegerv(GL_GREEN_BITS, &b);
glGetIntegerv(GL_BLUE_BITS, &c);
glGetIntegerv(GL_ALPHA_BITS, &d);
g_print("%s (R, G, B, alpha) = (%d, %d, %d, %d)\n", _("Color bits"),
a, b, c, d);
glGetIntegerv(GL_DEPTH_BITS, &a);
glGetIntegerv(GL_STENCIL_BITS, &b);
glGetIntegerv(GL_MAX_LIGHTS, &c);
g_print("%s = %d, %s = %d, %s = %d\n", _("Depth bits"), a,
_("Stencil bits"), b, _("Max amount of lights"), c);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &a);
glGetIntegerv(GL_MAX_CLIP_PLANES, &b);
g_print("%s = %d, %s = %d\n", _("Max texture size"), a,
_("Max clip planes"), b);
glGetIntegerv(GL_MAX_MODELVIEW_STACK_DEPTH, &a);
glGetIntegerv(GL_MAX_PROJECTION_STACK_DEPTH, &b);
glGetIntegerv(GL_MAX_ATTRIB_STACK_DEPTH, &c);
glGetIntegerv(GL_MAX_TEXTURE_STACK_DEPTH, &d);
g_print("%s: %s = %d, %s = %d, %s = %d, %s = %d\n", _("Max stack depths"),
_("modelview"), a, _("projection"), b, _("attrib"), c,
_("texture"), d);
//g_print("%s: %s\n", _("All extensions"), (char *)glGetString(GL_EXTENSIONS));
useVBO = queryExtension("GL_ARB_vertex_buffer_object");
if(useVBO) g_print(_("VBO is supported\n"));
gdk_gl_drawable_gl_end(glDrawable);
}
void freeGLmemory(Window *window){
if(!window->texture) return;
GLuint *tex = &window->texture->tex;
if(!*tex) return;
if(useVBO)
glDeleteBuffersARB(1, tex);
else
glDeleteLists(*tex, 1);
}
/*
* Coordinates transformation from CS of drawingArea into CS of picture
* x,y - pointer coordinates
* X,Y - coordinates of appropriate point at picture
*/
void conv_mouse_to_image_coords(double x, double y,
double *X, double *Y,
Window *window){
double a = window->Daspect / window->Zoom;
*X = x * a - window->mouse.x;
*Y = window->mouse.y - y * a;
}
void conv_image_to_mouse_coords(double X, double Y,
double *x, double *y,
Window *window){
double a = window->Zoom / window->Daspect;
*x = (X + window->mouse.x) * a;
*y = (window->mouse.y - Y) * a;
}
gboolean configure(GtkWidget *Area, GdkEventExpose *event, Window *window){
//FNAME();
double a, A, W, H, w, h;
double Zoom = window->Zoom;
if(event)
if(event->count) return FALSE;
TEXTURE *texture = (TEXTURE*)window->texture;
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
gdk_gl_drawable_wait_gdk(glDrawable);
// set position
double W0, H0, x0,y0, xm,ym;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
W = W0 = Area->allocation.width;
H = H0 = Area->allocation.height;
A = W / H;
glViewport(0, 0, W, H);
if(texture){
// compute the right dimensions for rectangle with picture
w = texture->w; h = texture->h;
a = w / h; w /= 2.; h /= 2.;
if(A > a){
W = h * A; H = h;
//window->mouse.rx = (W0 - H0*a)/2.; window->mouse.ry = 0.;
window->Daspect = h / H0 * 2.;
}
else{
H = w / A; W = w;
//window->mouse.ry = (H0 - W0/a)/2.; window->mouse.rx = 0.;
window->Daspect = w / W0 * 2.;
}
// recalculate limits for the rulers
x0 = W/Zoom - w + window->move.x / Zoom;
window->mouse.x = x0;
y0 = H/Zoom - h + window->move.y / Zoom;
xm = 2. * W / Zoom - x0;
ym = 2. * H / Zoom - y0;
window->mouse.y = ym;
//DBG("W=%g, w=%g, Zoom=%g, x0=%g, xm=%g, Da=%g", W, w, Zoom, x0, xm, Daspect);
set_Drulers(x0, y0, xm, ym, window);
glOrtho(-W,W, -H,H, -1., 1.);
}
glMatrixMode(GL_MODELVIEW);
glBlendEquation(GL_FUNC_ADD);
glEnable(GL_BLEND);
gdk_gl_drawable_gl_end(glDrawable);
refresh_state(window);
return FALSE;
}
/*
* Ellipse with center at (x,y) and radii (rx,ry)
* plots as polyline with stride of 10 degr
*/
void draw_ellipce(GLfloat x, GLfloat y, GLfloat rx, GLfloat ry){
GLfloat pi2 = M_PI * 2., step = pi2 / 36., angle;
glBegin(GL_LINE_LOOP);
for(angle = 0.; angle < pi2; angle += step)
glVertex2f(x+rx*cos(angle), y+ry*sin(angle));
glEnd();
}
/*
* Graphical indication of recognized spots
* window - the window with picture
* w, h - half-width & half-height of picture (texture)
* CX0, CY0 - zero point coordinates in picture CS (in center)
* box == TRUE - draw region, == FALSE - set selection region
*/
void draw_squares(Window *window, double w, double h, double CX0, double CY0, gboolean box){
int i, n;
double x0, y0, x1, y1;
BOX *bbox; Spot **spot; Coordinates *crds;
Spots *spots = window->spots;
if(!spots) return;
n = spots->n;
spot = spots->spot;
glPushMatrix();
for(i = 0; i < n; i++){
bbox = &spot[i]->box;
crds = &spot[i]->c;
x0 = bbox->x - w; y0 = h - bbox->y;
x1 = x0 + bbox->w; y1 = y0 - bbox->h;
if(box){
glColor3f(0., 1., 0.); // green is box borders
glBegin(GL_LINE_LOOP);
glVertex2f(x0, y0); glVertex2f(x1, y0);
glVertex2f(x1, y1); glVertex2f(x0, y1);
glEnd();
if(window->context->transformMask & TRANS_HARTMANN){
x0 = crds->x + CX0;
y0 = crds->y + CY0;
glColor3f(1., 0., 0.); // red is spot half-widths
draw_ellipce(x0, y0, crds->rx, crds->ry);
//glColor3f(0., 1., 0.);
}
}else{ // box == FALSE - set frames for selection
glLoadName(i);
glRectf(x0, y0, x1, y1);
}
}
if(box){ // draw cross - CS zero
glColor3f(1., 0., 1.);
glBegin(GL_LINES);
glVertex2f(CX0-15., CY0-15.); glVertex2f(CX0+15., CY0+15.);
glVertex2f(CX0-15., CY0+15.); glVertex2f(CX0+15., CY0-15.);
glEnd();
}
glPopMatrix();
}
/*
* graphical indication of isolines
* window - the window
* w, h - half-width & half-height of picture (texture)
* CX0, CY0 - zero point coordinates in picture CS (in center)
* _2D == TRUE for 2D images
*/
void draw_isolines(Window *window, double w, double h, gboolean _2D){
//FNAME();
IMAGE *ima = window->image;
if(!ima) return;
Contour *c;
cPoint *p;
cList **cl = window->image->contours;
int i, n = window->image->Ncontours;
if(!cl || n < 1) return;
float m = ima->stat.min, wd = ima->stat.max - m;
float *lvls = ima->cLevels, curLevel;
guchar colr[3] = {255,0,0}; // isolines' colors
glPushMatrix();
for(i = 0; i < n; i++, cl++){ // cycle by isolines
if(!*cl) continue; // given level doesn't have isolines
c = (*cl)->first; // first contour for given level
if(!c || (*cl)->N < 1) continue;
gray2rgb((lvls[(*cl)->L]-m)/wd, colr); // convert level into color
glColor3ubv(colr);
if(_2D) curLevel = 0.;
else curLevel = lvls[(*cl)->L] * ((GLfloat)w + (GLfloat)h) / 2./ wd;
//DBG("curLevel=%g, gray=%g, color=(%d,%d,%d)", curLevel, (lvls[(*cl)->L]-m)/wd, colr[0],colr[1],colr[2]);
while(c){ // cycle by contours
p = c->first;
if(!p || c->N < 1){ c = c->next; continue;}
if(c->closed)
glBegin(GL_LINE_LOOP);
else
glBegin(GL_LINE_STRIP);
while(p){ // cycle by contour points
glVertex3f(p->x-w, p->y-h, curLevel);
p = p->next;
}
glEnd();
c = c->next;
}
}
glPopMatrix();
}
void processHits(Window *window, GLint hits, GLuint buffer[]){
unsigned int i, j;
GLuint names, *ptr;
Spot *spot;
//Coordinates crds;
gchar buf[256];
int l = 255;
Spots *spots = window->spots;
if(!spots) return;
printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
if(hits < 1){
set_status_text(StatusText, _("Empty region"), window);
return;
}
for (i = 0; i < (unsigned int)hits; i++){
names = *ptr;
DBG("number of names for this hit = %d\n", names);
ptr += 3;
g_print(_("Selected spot[s]:\n"));
if(names == 1)
g_sprintf(buf, _("Selected spot: "));
else
g_sprintf(buf, _("Selected spots: "));
l -= strlen(buf);
for (j = 0; j < names; j++){
if(*ptr < (GLuint)spots->n){
spot = spots->spot[*ptr];
g_print("id: %d, xC=%.2f, yC=%.2f, r=%.2f, phi=%.2f, ",
spot->id, spot->xC, spot->yC, spot->r, spot->phi);
g_print("cn: (%.1f, %.1f), dr:(%.1f, %.1f)\n",
spot->c.x, spot->c.y, spot->c.rx, spot->c.ry);
g_print("box X: [%d, %d], box Y: [%d, %d], cn: [%d, %d]\n",
spot->box.x, spot->box.x+spot->box.w,
spot->box.y, spot->box.y+spot->box.h,
spot->box.x+spot->box.w/2, spot->box.y+spot->box.h/2);
/* get_gaussian_center(&spot->box, &crds);
g_print("\tCenter: (%.1f, %.1f), wd: (%.1f, %.1f)",
crds.x, (double)image->height-crds.y-1., crds.rx, crds.ry);
*/
l -= g_snprintf(&buf[strlen(buf)], l, "%d ", spot->id);
}
ptr++;
}
g_print("\n");
set_status_text(StatusText, buf, window);
}
}
gboolean expose(GtkWidget *Area, GdkEventExpose *event, Window *window){
TEXTURE *texture = (TEXTURE*)window->texture;
double w, h;
GLfloat tred[4] = {1.,0.,0.,1.};
GLfloat twhite[4] = {1.,1.,1.,1.};
if(event)
if(event->count) return FALSE;
//FNAME();
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
glClearColor(.1, .1, .1, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3fv(tred);
if(texture){
w = texture->w / 2.; h = texture->h / 2.;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(window->move.x, window->move.y, 0.);
glScalef(window->Zoom, window->Zoom, 1.);
//DBG("moveXY=%g, %g; Zoom=%g, texture: id=%d, w=%d",
// window->move.x, window->move.y, window->Zoom, texture->tex, texture->w);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->tex);
//DBG("texture: %d", texture->tex);
//DBG("w=%g, h=%g\n", w, h);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, twhite);
glDisable(GL_BLEND);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(-w, -h );
glTexCoord2f(1.0f, 0.0f); glVertex2f( w, -h );
glTexCoord2f(1.0f, 1.0f); glVertex2f( w, h );
glTexCoord2f(0.0f, 1.0f); glVertex2f(-w, h );
glEnd();
glDisable(GL_TEXTURE_2D);
/*glBegin(GL_LINE_LOOP);
glTexCoord2f(0.0f, 0.0f); glVertex2f(-w, -h );
glTexCoord2f(1.0f, 0.0f); glVertex2f( w, -h );
glTexCoord2f(1.0f, 1.0f); glVertex2f( w, h );
glTexCoord2f(0.0f, 1.0f); glVertex2f(-w, h );
glEnd();*/
glEnable(GL_BLEND);
// draw addition information
if(window->context->visualMode &
(SHOW_POTBOXES|SHOW_POTSELECT|SHOW_ISOLINES)){
GLuint selectBuf[BUFSIZE];
GLint hits;
double CX0 = AxisX - w, CY0 = h - AxisY;
gboolean showiso = window->context->visualMode & SHOW_ISOLINES;
gboolean showbox = window->context->visualMode & SHOW_POTBOXES;
gboolean showsel = window->context->visualMode & SHOW_POTSELECT;
GLint viewport[4];
if(showsel && forcesel){ // get coordinates from selection buffer
glPushMatrix();
glGetIntegerv(GL_VIEWPORT, viewport);
glSelectBuffer(BUFSIZE, selectBuf);
glRenderMode(GL_SELECT);
glInitNames();
glPushName(0);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
/* create pixel picking region near cursor location */
DBG("x=%g, y=%g", ObjX, ObjY);
gluPickMatrix(ObjX, (viewport[3] - ObjY), 5.0, 5.0, viewport);
//gluPickMatrix(ObjX, ObjY, 10.0, 10.0, viewport);
double W = Area->allocation.width;
double H = Area->allocation.height;
double A = W / H;
if(A > w / h){
W = h * A; H = h;}
else{
H = w / A; W = w;}
glOrtho(-W,W, -H,H, -1., 1.);
glMatrixMode(GL_MODELVIEW);
if(showbox) draw_squares(window, w, h, CX0, CY0, FALSE);
if(showiso) draw_isolines(window, w, h, TRUE);
glMatrixMode(GL_PROJECTION);
glPopMatrix();
hits = glRenderMode(GL_RENDER);
processHits(window, hits, selectBuf);
forcesel = FALSE;
glPopMatrix();
}
glMatrixMode(GL_MODELVIEW);
if(showbox) draw_squares(window, w, h, CX0, CY0, TRUE);
if(showiso) draw_isolines(window, w, h, TRUE);
}
/*
// layers mix ===>
glLoadIdentity();
//glTranslatef(moveX,moveY,0);
glScalef(window->Zoom, window->Zoom, 1.);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, tred);
glBlendFunc(GL_ONE, GL_ONE);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex2f(-w, -h );
glTexCoord2f(1.0f, 0.0f); glVertex2f( w, -h );
glTexCoord2f(1.0f, 1.0f); glVertex2f( w, h );
glTexCoord2f(0.0f, 1.0f); glVertex2f(-w, h );
glEnd();
glDisable(GL_TEXTURE_2D);
// <====
*/
}
else DBG("No texture");
gdk_gl_drawable_swap_buffers(glDrawable);
gdk_gl_drawable_wait_gl(glDrawable);
gdk_gl_drawable_gl_end(glDrawable);
return FALSE;
}
/*
gboolean idle(gpointer userData){
GtkWidget *Area = GTK_WIDGET(userData);
gdk_window_invalidate_rect(Area->window, &Area->allocation, FALSE);
return TRUE;
}*/
void initGl(GtkWidget *Area){
FNAME();
GdkGLConfig *glConfig;
GdkGLConfigMode mode = GDK_GL_MODE_RGB |
GDK_GL_MODE_DEPTH |
GDK_GL_MODE_ALPHA |
GDK_GL_MODE_DOUBLE;
if(!gdk_gl_query_extension())
BADOGL();
glConfig = gdk_gl_config_new_by_mode(mode);
if(!glConfig)
glConfig = gdk_gl_config_new_by_mode(mode & ~GDK_GL_MODE_ALPHA);
if(!glConfig)
BADOGL();
if(!gtk_widget_set_gl_capability(Area, glConfig, NULL, TRUE,
GDK_GL_RGBA_TYPE))
BADOGL();
}
/*
* generation of textures to show in Area widget of image
* if redraw == TRUE texture substitutes previous otherwise generate the new one
*/
void gen_texture(IMAGE *image, Window *window, gboolean redraw){
//~ FNAME();
TEXTURE *texture = window->texture;
GtkWidget *Area = window->drawingArea;
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
glEnable(GL_TEXTURE_2D);
if(!texture){
INIT(window->texture, TEXTURE);
texture = window->texture;
if(!texture){
g_err(_("Can't allocate memory for texture"));
return;
}
//~ g_print(_("Generating texture\n"));
glGenTextures(1, &texture->tex);
//~ DBG("%s, tex=%d\n", gluErrorString(glGetError()), texture->tex);
}
//~ DBG("Image size: w=%d, h=%d", image->width, image->height);
glBindTexture(GL_TEXTURE_2D, texture->tex);
if(image && image->data){
int i,j, k, w = image->width, h = image->height;
GLfloat *tex = NULL;
GLfloat min = image->stat.min, wd = image->stat.max - min;
GLfloat *ptri = image->data, *ptro;
DBG("min = %g, wd = %g", min, wd);
if(image->stat.max > 1. || min < 0.){
tex = malloc(w * h * sizeof(GLfloat));
ptro = tex;
#ifdef EBUG
double t0 = dtime();
#endif
#pragma omp parallel for
for(i=0; i<h; i++){
k = i*w;
for(j=0; j<w; j++, k++){//, ptri++, ptro++){
//*ptro = (*ptri - min) / wd;
ptro[k] = (ptri[k] - min) / wd;
}
}
DBG("time: %g", dtime() - t0);
}else ptro = image->data;
if(redraw)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h,
GL_LUMINANCE, GL_FLOAT, ptro); // s/image->data/tex/
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY, w, h,
0, GL_LUMINANCE, GL_FLOAT, ptro);
//DBG("%s\n", gluErrorString(glGetError()));
//DBG("W: %d, H:%d, im[100]=%f\n", image->width, image->height, image->data[100]);
texture->w = image->width; texture->h = image->height;
free(tex);
}
else{
DBG("Empty texture");
glTexImage2D(GL_TEXTURE_2D, 0, GL_INTENSITY, 10., 10.,
0, GL_INTENSITY, GL_FLOAT, NULL);
}
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_CONSTANT);
//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glDisable(GL_TEXTURE_2D);
gdk_gl_drawable_gl_end(glDrawable);
/*
* Events:
* expose - redraw part of an image
* configure - redraw window when size/position changed
* if handler returns TRUE signal processing stops
* otherwise it can be process by other handlers
*/
g_signal_connect(Area, "expose-event",
G_CALLBACK(expose), window);
g_signal_connect(Area, "configure-event",
G_CALLBACK(configure), window);
//~ DBG("end");
force_redraw(Area);
}
void force_redraw(GtkWidget *Area){
gtk_widget_queue_resize(Area);
gtk_widget_queue_draw(Area);
}
void pickRegion(double x, double y){
FNAME();
forcesel = TRUE;
ObjX = x;
ObjY = y;
}
gboolean configure3D(GtkWidget *Area, GdkEventExpose *event, Window *window){
FNAME();
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
double w, h;
GLfloat ratio;
if(!window)return FALSE;
//~ GLfloat H = (GLfloat)window->texture->h;
if(event)
if(event->count) return FALSE;
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
w = Area->allocation.width;
h = Area->allocation.height;
ratio = w / h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45., ratio, 0.1, 1e10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gdk_gl_drawable_gl_end(glDrawable);
return FALSE;
}
gboolean expose3D(GtkWidget *Area, GdkEventExpose *event, Window *window){
FNAME();
TEXTURE *texture = window->texture;
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
GLfloat H = (GLfloat)window->image->height;
if(event)
if(event->count) return FALSE;
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
GLfloat spotDir[] = {0.,0.,-1.,1.};
GLfloat pos[] = {0.,0.,0.,1.};
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glLightfv(GL_LIGHT1,GL_POSITION,pos);
glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION, spotDir);
if(window->context->drawingMode == GAME_MODE){ // mouse navigation
GLfloat lx, ly, lz, za = window->Zangle, xa = window->Xangle;
GLfloat x = window->move.x, y = window->move.y, z = H * window->Zoom;
lx = cosf(za)*sinf(xa);
ly = -sinf(za);
lz = -cosf(za)*cosf(xa);
gluLookAt(x, y, z,
x+lx, y+ly, z+lz,
0., 1., 0.);
//~ GLfloat w = Area->allocation.width, h = Area->allocation.height;
//GLfloat z = H * window->Zoom;
//~ GLfloat yangle = 180. / M_PI * atan2f(window->Xangle - w/2., z);
//~ GLfloat xangle = 180. / M_PI * atan2f(window->Zangle - h/2., z);
//~ glRotatef(yangle, 0.,1.,0.);
//~ glRotatef(xangle, 1.,0.,0.);
//~ glTranslatef(window->move.x, 0., -z);
//glRotatef(window->Xangle, 0.,1.,0.);
//~ glRotatef(window->Zangle, 1.,0.,0.);
//glRotatef(window->Zangle, cosf(window->Xangle*M_PI/180.),0.,sinf(window->Xangle*M_PI/180.));
//glTranslatef(window->move.x, 0., -z);
}else{ // key navigation
glTranslatef(window->move.x, window->move.y, -H * window->Zoom);
glRotatef(window->Xangle, 1.,0.,0.);
glRotatef(window->Zangle, 0.,0.,1.);
}
glPushMatrix();
glEnable(GL_LIGHTING);
if(useVBO){
GLint y, w = window->image->width, h = window->image->height;
GLint pts = w*2;
size_t sz = w * h * 3 * sizeof(GLfloat);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texture->tex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture->w);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_INDEX_ARRAY);
glNormalPointer(GL_FLOAT, 0, (GLfloat*)sz);
glIndexPointer(GL_UNSIGNED_INT, 0, 0 );
glVertexPointer(3, GL_FLOAT, 0, 0);
h--;
sz = 0; w = pts * sizeof(GLfloat);
for(y = 0; y < h; y++, sz += w)
glDrawElements(GL_TRIANGLE_STRIP, pts, GL_UNSIGNED_INT, (GLvoid*)(sz));
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_INDEX_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
}else
glCallList(texture->tex);
glPushMatrix();
glDisable(GL_LIGHTING);
glLineWidth(3.0);
draw_isolines(window, ((double)window->image->width)/2.+.5,
((double)window->image->height)/2.+.5, FALSE);
/*glColor3f(0.,0.,1.);
glBegin(GL_QUADS);
glVertex3f(-100.f, -100.f, -12.f );
glVertex3f(-100.f, 100.f, -12.f );
glVertex3f( 100.f, 100.f, -12.f );
glVertex3f( 100.f, -100.f, -12.f );
glEnd();
glBegin(GL_LINES);
glVertex3fv(pos);
glColor3f(1.f,0.f,0.f);
glVertex3fv(spotDir);
glEnd();*/
glPopMatrix();
gdk_gl_drawable_swap_buffers(glDrawable);
gdk_gl_drawable_wait_gl(glDrawable);
gdk_gl_drawable_gl_end(glDrawable);
return FALSE;
}
void init3D(Window *window){
FNAME();
GtkWidget *Area = window->drawingArea;
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
GLfloat lAmbient[] = {0.3,0.3,0.3,1.0};
GLfloat lDiffuse[] = {1.0,1.0,1.0,1.0};
GLfloat lSpecular[]= {1.0,1.0,1.0,1.0};
GLfloat l2Ambient[] = {0.0,0.0,0.1,1.0};
GLfloat l2Diffuse[] = {0.0,0.0,0.3,1.0};
GLfloat l2Specular[]= {0.0,0.0,0.5,1.0};
GLfloat mShininess[] = {110.0};
GLfloat mSpecular[] = {0.8,0.8,1.0,1.0};
GLfloat mDiffuse[] = {0.8,0.8,1.0,1.0};
GLfloat mAmbient[] = {0.2,0.2,0.2,1.0};
if (!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
glEnable(GL_RESCALE_NORMAL);
glEnable(GL_DEPTH_TEST);
//~ glEnable(GL_CULL_FACE);
//~ glCullFace(GL_BACK);
GLfloat spotDir[] = {0.,0.,-1.,1.};
GLfloat pos[] = {0.,0.,0.,1.};
glLightfv(GL_LIGHT1,GL_POSITION,pos);
glLightfv(GL_LIGHT1,GL_SPOT_DIRECTION, spotDir);
glLightf(GL_LIGHT1,GL_SPOT_CUTOFF, 90.);
glLightf(GL_LIGHT1,GL_SPOT_EXPONENT, 20.);
glLightfv(GL_LIGHT0,GL_AMBIENT,l2Ambient);
glLightfv(GL_LIGHT0,GL_DIFFUSE,l2Diffuse);
glLightfv(GL_LIGHT0,GL_SPECULAR,l2Specular);
glLightfv(GL_LIGHT1,GL_AMBIENT,lAmbient);
glLightfv(GL_LIGHT1,GL_DIFFUSE,lDiffuse);
glLightfv(GL_LIGHT1,GL_SPECULAR,lSpecular);
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,GL_TRUE);
glMaterialfv(GL_FRONT, GL_SPECULAR, mSpecular);
glMaterialfv(GL_FRONT, GL_SHININESS,mShininess);
glMaterialfv(GL_FRONT, GL_AMBIENT, mAmbient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mDiffuse);
//~ glMaterialfv(GL_BACK, GL_SPECULAR, mSpecular);
//~ glMaterialfv(GL_BACK, GL_SHININESS,mShininess);
//~ glMaterialfv(GL_BACK, GL_AMBIENT, mGray);
//~ glMaterialfv(GL_BACK, GL_DIFFUSE, mWhite);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHT1);
gdk_gl_drawable_gl_end(glDrawable);
g_signal_connect(Area, "expose-event",
G_CALLBACK(expose3D), window);
g_signal_connect(Area, "configure-event",
G_CALLBACK(configure3D), window);
force_redraw(Area);
}

3
src/scripts/genh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "const gchar *ui = `sed -r 's/^[[:space:]]*//;s/\"/\\\\\"/g;s/(^.*$$)/\"&\"/' $1` ;" > ui.h
echo "Wrote ui.h"

534
src/spots.c Normal file
View File

@ -0,0 +1,534 @@
// spots.c - functions to work with spots (sorting, enumerating & so on)
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
/*
* Since our image is mirrored, to establish the direction of the axis
* of symmetry it is necessary to know the position angle of the camera, etc.
* It is easier to work in a "mirror" coordinates. Similarly, the coordinates
* of the mask should also be given the "mirror".
*/
#include "gtk.h"
#include "spots.h"
#include "gauss.h"
const double M_2PI = 2. * M_PI;
// about a quarter of 11.25 degrees - the threshold for the identification of radial points and frames
const double PHI_STEP = 0.05;
// angle between the markers (for identification), 56.25 degrees
const double DPHI_MARKERS = 0.98;
const double aStep = M_PI / 16.; // 11.25 degrees, the angle between adjacent rays
const int N_ANGLES = 16; // number of lines on hartmanogramm (half of number of rays)
const int N_RINGS = 8; // number of rings on hartmanogramm
// angle of the Y-axis of the matrix relative to the Y-axis mask
double SlopeAngle;
// angular position of the marker (near the center) for a zero slope, -101.25 degrees
const double Slope0 = -1.767146;
/*
* these constants are the position of the axis of rotation of the table.
* In the coordinate system of the image (ie, the Y axis goes from top to bottom)
* means that it coincides with the optical axis. Naturally, in the work
* program these coordinates should be calculated
*/
double AxisX = 1523., AxisY = 1533.;
// declaration of the flags of errors leading to the exit from auto mode
enum{
TOO_MANY_MARKERS // discovered more than two markers
,NOT_ENOUGH_MARKERS // not found the two markers
,BAD_MARKERS_ANGLE // angle between the markers over DPHI_MARKERS +- PHI_STEP
,TOO_MANY_RINGS // found more rings than it should be
};
// messages appropriate to flags
char *errmsg[] = {
N_("Too many markers")
,N_("Not enough markers")
,N_("Bad markers angle")
,N_("Too many rings")
};
#ifdef LEPTONICA_FOUND
/*
* Violated any restrictions on the automatic mode
* In theory the function should exit the batch mode with the mapping errors
*/
void bad_circumstances(int errcode){
red("\nerror, need to stop\n");
g_err(_(errmsg[errcode]));
}
// memory allocation for n spots array
Spots* spots_alloc(int n){
Spots *ptr = malloc(sizeof(Spots));
if(!ptr){
g_err(_("Can't allocate memory for new spots"));
return NULL;
}
ptr->spot = malloc(n * sizeof(Spot*));
if(!ptr->spot){
g_err(_("Can't allocate memory for pointers in spots array"));
free(ptr);
return NULL;
}
ptr->size = n;
ptr->n = 0;
// originally put the flag BY_COPY, which is removed by the first copy BY_PTR,
// order to do not try to free one area several times
ptr->memflag = BY_COPY;
return ptr;
}
void spots_free(Spots **spots){
FNAME();
int i, n;
if(!(*spots)) return;
n = (*spots)->n;
Spot **spot = (*spots)->spot;
if((*spots)->memflag != BY_PTR) // check whether spot-regions were allocated
for(i = 0; i < n; i++, spot++)
free(*spot);
free((*spots)->spot);
free(*spots);
*spots = NULL;
}
/*
* Add point spot into array spots by copy.
* If array is full return NULL
*
*/
Spot* spots_add(Spots *spots, Spot *spot, int copy){
Spot *ptr = NULL;
if(spots->size <= spots->n){
g_err(_("Spots array is full, can't place new spot\n"));
return NULL; // there's no enough space
}
if(!spot){
g_err(_("Zero pointer to spot\n"));
return NULL;
}
if(copy == BY_COPY){
ptr = malloc(sizeof(Spot));
if(!ptr){
g_err(_("Can't allocate memory for new spot"));
return NULL;
}
if(!memcpy(ptr, spot, sizeof(Spot))){
free(ptr);
g_err(_("Can't copy spot to new one"));
return NULL;
}
}
else if(copy == BY_PTR){
spots->memflag = BY_PTR; // change flag to not do free()
ptr = spot;
}
spots->spot[spots->n] = ptr; // jam into array
spots->n++; // increment points number
return ptr;
}
/*
* alignment of the coordinate system spot.c to CS relative to the optical axis,
* approximate calculation of the angular and radial coordinates of each spot in
* the coordinates with respect to the center of gravity of spots (for further identification)
*/
void spots_center(Spots *spots){
int i, n;
double X = 0., Y = 0., N, x0, y0;
double *xx, *yy, *px, *py;
Spot **spot;
n = spots->n; N = (double) n;
if(n == 0) return;
xx = malloc(n * sizeof(double)); px = xx;
yy = malloc(n * sizeof(double)); py = yy;
spot = spots->spot;
for(i = 0; i < n; i++, px++, py++, spot++){
//x0 = (*spot)->c.x; y0 = (*spot)->c.y;
x0 = (*spot)->c.x - AxisX;
//y0 = (*spot)->c.y - AxisY;
y0 = AxisY - (*spot)->c.y; // flip into right coordinates
(*spot)->c.x = x0; (*spot)->c.y = y0;
*px = x0;
*py = y0;
X += *px; Y += *py;
}
X /= N; Y /= N;
green("\nspots center: %g, %g", X, Y);
px = xx; py = yy;
spot = spots->spot;
for(i = 0; i < n; i++, px++, py++, spot++){
x0 = *px - X; y0 = *py - Y;
(*spot)->phi = atan2(y0, x0);
(*spot)->r = sqrt(x0*x0 + y0*y0);
}
free(xx); free(yy);
}
int cmp_spots_r(const void *spot1, const void *spot2){
return ((*(Spot**)spot1)->r > (*(Spot**)spot2)->r);
}
void sort_spots_by_r(Spots *spots){
qsort(spots->spot, spots->n, sizeof(Spot*), cmp_spots_r);
}
int cmp_spots_phi(const void *spot1, const void *spot2){
return ((*(Spot**)spot1)->phi > (*(Spot**)spot2)->phi);
}
void sort_spots_by_phi(Spots *spots){
qsort(spots->spot, spots->n, sizeof(Spot*), cmp_spots_phi);
}
int cmp_spots_id(const void *spot1, const void *spot2){
return ((*(Spot**)spot1)->id > (*(Spot**)spot2)->id);
}
void sort_spots_by_id(Spots *spots){
qsort(spots->spot, spots->n, sizeof(Spot*), cmp_spots_id);
}
/*
* Sorting the points in R and phi we find the markers.
* After sorting, id of markers is set to <Beam number> + 100 * <ring number>.
* The markers are numbered as 100 * <ring number> + <number of the next clockwise beam>
*/
void sort_spots(Window *window){
FNAME();
Spots *spots = window->spots;
gboolean Prefocal = TRUE;
int i, n;
int step = 0, // a step - goto next group
num = 0, // number of ray or ring
mZero = 0; // id of zero ray
Spot **spot = spots->spot;
Spots *markers = spots_alloc(2); // allocate space for markers
double *angles_pre, *angles_post; // angle coordinate of each ray in "normal" orientation
const int N_RAY_MAX = 2 * N_ANGLES - 1; // max ray number
const int N_RAYS = 2 * N_ANGLES;
const int N_ANGLE_MAX = N_ANGLES - 1; // max line number
double r_aver = 0.; // mid radius of spots
n = spots->n;
// fill the array angles_0 (for obtaining further angle of inclination of hartmanogramm)
angles_pre = calloc(N_ANGLES, sizeof(double));
angles_post = calloc(N_ANGLES, sizeof(double));
{
angles_post[0] = M_PI_2 - aStep/2.;
angles_pre[0] = M_PI_2 + aStep/2.;
for(i = 1; i < N_ANGLES; i++){
angles_pre[i] = angles_pre[i-1] + aStep;
angles_post[i] = angles_post[i-1] - aStep;
green("angles[%d]: pre=%g, post=%g",
i, angles_pre[i]*180/M_PI, angles_post[i]*180/M_PI);
}
}
// find the center of gravity of points on hartmanogramm and determine
// for each point the polar coordinates in the system of the center of gravity
spots_center(spots);
// sort points by phi to find markers and number by rays
{
sort_spots_by_phi(spots);
double phi0 = (*spot)->phi, phi, dphi;
green("\nSort by phi\n");
for(i = 0; i < n; i++, spot++){
phi = (*spot)->phi;
dphi = fabs(phi-phi0);
phi0 = phi;
// If we two consecutive jump occurs, the previous point has to be a marker.
// If we we find two more markers, generate an error
if(dphi > PHI_STEP){
num++;
if(!step){
step = 1;
}else{
num--;
red("FOUND MARKER");
(*(spot-1))->id = 99 - (*(spot-1))->id;
DBG("spot[%d]: r=%g, phi=%g\n", (*(spot-1))->id,
(*(spot-1))->r, (*(spot-1))->phi);
if(!spots_add(markers, *(spot-1), BY_PTR)){
bad_circumstances(TOO_MANY_MARKERS);
goto ret;
}
}
}
else step = 0;
(*spot)->id = num;
//DBG("spot[%d]: r=%g, phi=%g, dphi=%g\n", (*spot)->id, (*spot)->r,
// phi, dphi);
}
}
// check markers
{
if(markers->n != 2){
if(markers->n == 0){
bad_circumstances(NOT_ENOUGH_MARKERS);
goto ret; // no marker found
}
spot = spots->spot;
for(i = 0; i < n; i++, spot++)
if((*spot)->id < N_RAYS) r_aver += (*spot)->r;
r_aver /= (double)n;
green("R_aver = %.2f, R=%.2f", r_aver, markers->spot[0]->r);
if(window->image->val_f < 0.){
// obtain the value of Prefocal from human as VAL_F is unknown
get_prefocal(window, &Prefocal);
}else Prefocal = (window->image->val_f < 100.) ? TRUE : FALSE;
int m_id = markers->spot[0]->id;
if(!Prefocal){
if(markers->spot[0]->r < r_aver){ // nearest, zero marker
mZero = 99 - m_id;
}else{ // far, fifth marker
mZero = 104 - m_id; // 5 rays clockwise
}
}else{
if(markers->spot[0]->r < r_aver){
mZero = 98 - m_id;
}else{
mZero = 93 - m_id;
}
}
if(markers->spot[0]->r < r_aver)
markers->spot[0]->id = 299; // finally change markers' numbers
else
markers->spot[0]->id = 794;
if(mZero > N_RAY_MAX) mZero -= N_RAYS;
else if(mZero < 0) mZero += N_RAYS;
}else{
sort_spots_by_r(markers);
spot = markers->spot;
for(i = 0; i < 2; i++)
green("marker #%d: r=%g, phi=%g, tmp_id=%d, x=%g, y=%g", i,
spot[i]->r, spot[i]->phi, spot[i]->id, spot[i]->c.x, spot[i]->c.y);
double dphi = spot[0]->phi - spot[1]->phi;
DBG("Angle between markers: %g degr\n", dphi*180./M_PI);
if(fabs(dphi - DPHI_MARKERS) > PHI_STEP){
bad_circumstances(BAD_MARKERS_ANGLE);
goto ret;
}
if(dphi > 0.){
Prefocal = FALSE;
mZero = 99 - spot[0]->id; // number of ray to be zero
}else{
mZero = 98 - spot[0]->id; // number of ray to be zero
if(mZero > N_RAY_MAX) mZero -= N_RAYS;
}
spot[0]->id = 299;
spot[1]->id = 794;
}
}
// renumbers points in a clockwise direction, starting from the scheme
// EEERRRRRRROOOOOOORRRRR!!!!
// ÓÍÅÎÙ ÎÁÐÒÁ×ÌÅÎÉÑ ÏÔÓÞÅÔÏ× ÎÅÔ: É ÎÁ ÐÒÅÄ- É ÎÁ ÚÁÆÏËÁÌØÎÏÍ ÏÄÉÎÁËÏ×Ï!
// The numbering starts from zero!
spot = spots->spot;
green("\nmZero = %d\n", mZero);
if(!Prefocal){ // postfocal image
red("\nPostfocal image\n");
for(i = 0; i < n; i++, spot++){
if((*spot)->id > N_RAY_MAX) continue; // don't convert markers' numbers
int sid = mZero - (*spot)->id; // change the numbering and its direction
if(sid < 0) sid += N_RAYS;
(*spot)->id = sid;
}
}else{ // prefocal image
red("\nPrefocal image\n");
for(i = 0; i < n; i++, spot++){
if((*spot)->id > N_RAY_MAX) continue;
int sid = (*spot)->id - mZero; // only change the numbering
if(sid < 0) sid += N_RAYS;
(*spot)->id = sid;
}
}
/* by the method of least squares we are counting angle of inclination
* y=a+bx, b=tg(slope+alpha_i0)
* b = [\sum x_i \sum y_i - n\sum (x_i y_i)] /
* [ (\sum x_i)^2 - n\sum (x_i)^2 ]
*
* Let S1 = \sum x_i, S2 = \sum y_i, S3 = \sum(x_i)^2,
* S4 = \sum(x_i y_i), then
* b = (S1*S2 - n*S4) / (S1*S1 - n*S3)
*
* Set up arrays four elements Sj of 16 (for each line by an item)
* In the loop over all spots (except markers) fill the array.
* Then we are counting the coefficients b and fill the matrix of slope angles
*/
{
double *S1, *S2, *S3, *S4, *N, *angles;
double x, y, *angles_0;
int idx;
angles_0 = (Prefocal) ? angles_pre : angles_post;
S1 = calloc(N_ANGLES, sizeof(double));
S2 = calloc(N_ANGLES, sizeof(double));
S3 = calloc(N_ANGLES, sizeof(double));
S4 = calloc(N_ANGLES, sizeof(double));
N = calloc(N_ANGLES, sizeof(double));
angles = calloc(N_ANGLES, sizeof(double));
SlopeAngle = 0.;
spot = spots->spot;
for(i = 0; i < n; i++, spot++){
idx = (*spot)->id;
if(idx > N_RAY_MAX) continue;
if(idx > N_ANGLE_MAX) idx -= N_ANGLES; // convert the ray number to the number of line
x = (*spot)->c.x; y = (*spot)->c.y;
S1[idx] += x; // \sum x_i
S2[idx] += y; // \sum y_i
S3[idx] += x*x; // \sum (x_i)^2
S4[idx] += x*y; // \sum (x_i y_i)
N[idx] += 1.;
}
for(i = 0; i < N_ANGLES; i++){
double ang;
// calculate an shift for i-th line
angles[i] = atan2((S1[i]*S2[i] - N[i]*S4[i]) ,
(S1[i]*S1[i] - N[i]*S3[i]));
DBG("Angle: %g", angles[i]*180./M_PI);
// check whether there's no jumps near +-pi
if(i){
ang = angles[i] - angles[i-1];
DBG("Ang: %g", ang*180./M_PI);
if(!Prefocal){
if(ang > M_PI) angles[i] -= M_2PI;
else if(ang > 0.) angles[i] -= M_PI;
else if(ang < -M_2PI) angles[i] += M_2PI;
else if(ang < -M_PI) angles[i] += M_PI;
}else{
if(ang < -M_PI) angles[i] += M_2PI;
else if(ang < 0.) angles[i] += M_PI;
else if(ang > M_2PI) angles[i] -= M_2PI;
else if(ang > M_PI) angles[i] -= M_PI;
}
}
ang = angles[i] - angles_0[i];
if(ang > M_2PI) ang -= M_2PI;
else if(ang < 0.) ang += M_2PI;
SlopeAngle += ang;
DBG("angle[%d] = %g; ang=%g; a0=%g\n", i, angles[i]*180./M_PI,
ang*180./M_PI, angles_0[i]*180./M_PI);
}
SlopeAngle /= (double)N_ANGLES; // average
red("Slope Angle: %g degr", SlopeAngle * 180. / M_PI);
free(S1); free(S2); free(S3); free(S4); free(N); free(angles);
}
// sort spots by r to enumerate by rings
{
sort_spots_by_r(spots);
spot = spots->spot;
double r0 = (*spot)->r, r;
// rtres == 1/7 of average distance between rings
double rtres = (spot[n-1]->r - r0) / ((double)N_RINGS - 1.) / 7.;
num = 0;
green("\nSort by r\n");
for(i = 0; i < n; i++, spot++){
if((*spot)->id > N_RAY_MAX) continue; // we have already counts markers
r = (*spot)->r;
if(fabs(r-r0) > rtres){
num+=100;
}
(*spot)->id += num;
// DBG("spot[%d]: r=%g, phi=%g, dr=%g\n", (*spot)->id, r,
// (*spot)->phi, r - r0);
r0 = r;
}
if((num/100 + 1) > N_RINGS)
bad_circumstances(TOO_MANY_RINGS);
}
/*
* At this stage it would be nice to display the picture boxes,
* numbered points, detected angle and center.
*/
/*
* Recalculate the the coordinates of centers points' for the translation
* to the coordinate system relative to the optical axis
* (rotation + reflection from the vertical axis).
* (\beta == SlopeAngle)
* x^0_i = -x_i\cos\beta - y_i\sin\beta;
* y^0_i = -x_i\sin\beta + y_i\cos\beta
*/
{
double cosSA = cos(SlopeAngle);
double sinSA = sin(SlopeAngle);
double x,y;
spot = spots->spot;
if(!Prefocal){
for(i = 0; i < n; i++, spot++){
x = (*spot)->c.x; y = (*spot)->c.y;
(*spot)->xC = -x*cosSA - y*sinSA;
(*spot)->yC = -x*sinSA + y*cosSA;
}
}else{
for(i = 0; i < n; i++, spot++){
x = (*spot)->c.x; y = (*spot)->c.y;
(*spot)->xC = x*cosSA + y*sinSA;
(*spot)->yC = -x*sinSA + y*cosSA;
}
}
}
ret:
free(angles_pre); free(angles_post);
}
void spots_save(Spots *spots, gchar *filename){
Spot **spot, *s;
FILE *f;
if(! spots || ! spots->spot || ! filename) return;
int i, n = spots->n;
sort_spots_by_id(spots);
spot = spots->spot;
f = fopen(filename, "w");
if(!f){
g_err(_("Can't open file"));
return;
}
fprintf(f, "\t***** Bounding boxes *****\t** Coords w/o rot. comp. **\tCoords with rot. comp.\n");
fprintf(f, "id\tx0\ty0\tw\th\txc\tyc\tw2\th2\txc\tyc\n");
for(i = 0; i < n; i++, spot++){
s = *spot;
if(fprintf(f, "%03d\t%d\t%d\t%d\t%d\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\t%.2f\n",
s->id,
s->box.x, s->box.y, s->box.w, s->box.h,
s->c.x, s->c.y, s->c.rx, s->c.ry,
s->xC, s->yC) <= 0){
g_err(_("Can't write to file"));
break;
}
}
fclose(f);
}
#else // LEPTONICA_FOUND not set
Spots* spots_alloc(int n __attribute__((unused))){
LEPTERR;
return NULL;
}
Spot* spots_add(Spots *spots __attribute__((unused)),
Spot *spot __attribute__((unused)), int copy __attribute__((unused))){
LEPTERR;
return NULL;
}
void spots_free(Spots **spots __attribute__((unused))){
LEPTERR;
}
void sort_spots(Spots *spots __attribute__((unused))){
LEPTERR;
}
#endif // LEPTONICA_FOUND

423
src/terrain.c Normal file
View File

@ -0,0 +1,423 @@
// terrain.c - functions for 3D view
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "opengl.h"
#include "terrain.h"
#include "contours.h"
#include "fits.h"
#include "imtools.h" // for destroy_image
#define MINSZ 5 // minimal size of a selection region
/*
* Calculate the normals for the point (x, y) of the image.
* First, fill arrays of normals for the the lower right and upper
* left triangles with one vertex at a given point.
* Then for each point the total normal is calculated
*
* Principle
* *---B---*---* (rd is the lower right triangle, ul is upper left)
* | / | / | / | Normal at the point A at the map of triangles (as shown)
* E---A---C---* is composed of normal Aul + Bdr + Cul + Adr + Dul + Edr
* | / | / | / | The normals for individual triangles (X - intensity at a point):
* *---D---*---* (normal = vertical vector x horizontal vector)
* Not normalized Aul = (E-A, A-B, 1)
* Adr = (A-C, D-A, 1)
*/
/*
* Computing of the normals UL & DR
* REWRITE FOR CUDA
*/
GLfloat *mkTrNormals(IMAGE *image){
FNAME();
int W = image->width, H = image->height, x, y, i;
/*
* Normal for the the lower right and upper left triangles coming out of a given point
* Component 0 - UL, 1 - DR
* To simplify further calculations, supplemented by a frame in one node (zeros)
*/
GLfloat *tr_norms = calloc((W+2)*(H+2), 6*sizeof(GLfloat));
int Wstride = (W + 2) * 6;
int ww = W - 1, hh = H - 1;
GLfloat *heights = image->data;
GLfloat *ptr;
#ifdef EBUG
double t0 = dtime();
#endif
inline void soft_normalize(GLfloat *v){
GLfloat l,x,y;
x = v[0];
y = v[1];
l = sqrtf((x*x) + (y*y) + 1.f);
v[0] = x / l;
v[1] = y / l;
v[2] = 1.f/ l;
}
inline void calc_UL(GLfloat *height, GLfloat *ptr){
GLfloat A = *height;
if(A){};
ptr[0] = height[-1] - A; // E - A
ptr[1] = height[-W] - A; // B - A
ptr[2] = 1.f;
soft_normalize(ptr);
}
inline void calc_DR(GLfloat *height, GLfloat *ptr){
GLfloat A = *height;
if(A){};
ptr += 3; // move to DR
ptr[0] = A - height[1]; // A - C
ptr[1] = A - height[W]; // A - D
ptr[2] = 1.f;
soft_normalize(ptr);
}
inline void calc_two_norms(GLfloat *height, GLfloat *ptr){
calc_UL(height, ptr);
calc_DR(height, ptr);
}
ptr = &tr_norms[Wstride + 1];
#pragma omp parallel for
for(x = 0; x < ww; x++){
calc_DR(heights+x, ptr + 6*x); // upper border without right point, only DR
}
i = 0;
#pragma omp parallel for private(y, ptr, x, i)
for(y = 1; y < hh; y++){
ptr = &tr_norms[Wstride*(y+1)+1];
i = y * W;
calc_DR(heights+(i++), ptr); // left border - only DR
ptr += 6;
for(x = 1; x < ww; x++, ptr += 6){
calc_two_norms(heights+(i++), ptr);
}
calc_UL(heights+i, ptr); // right border - only UL
}
ptr = &tr_norms[Wstride * H + 1];
heights = &image->data[hh * W];
#pragma omp parallel for
for(x = 1; x < W; x++)
calc_UL(heights, ptr+6*x); // down border - onlyUL
/*
ptr = &tr_norms[Wstride + 1];
for(x = 0; x < ww; x++, ptr += 6, heights++)
calc_DR(heights, ptr);
heights++;
for(y = 1; y < hh; y++){
ptr = &tr_norms[Wstride*(y+1)+1];
calc_DR(heights++, ptr);
ptr += 6;
for(x = 1; x < ww; x++, ptr += 6, heights++){
calc_two_norms(heights, ptr);
}
calc_UL(heights++, ptr);
}
heights++;
ptr = &tr_norms[Wstride * H + 2];
for(x = 1; x < W; x++, heights++, ptr += 6)
calc_UL(heights, ptr);
*/
DBG("time: %g", dtime() - t0);
return tr_norms;
}
void computeNormals(Window *window){
FNAME();
int x, y;
IMAGE *verts = window->image;
IMAGE *norms = window->image_transformed;
int W = verts->width, H = verts->height;
int Wstride = (W + 2) * 6;
#ifdef EBUG
double t0 = dtime();
#endif
GLfloat *tr_norms = mkTrNormals(verts);
GLfloat *ptr;
GLfloat *normal = norms->data;
inline void addVectors(GLfloat *norm, GLfloat *dbl){
GLfloat *Aul, *Adr, *Bdr, *Cul, *Dul, *Edr;
Aul = dbl; Adr = dbl + 3;
Bdr = dbl - Wstride + 3;
Cul = dbl + 6;
Dul = dbl + Wstride;
Edr = dbl - 3;
*norm++ = (*Aul++) + (*Bdr++) + (*Cul++) + (*Adr++) + (*Dul++) + (*Edr++);
*norm++ = (*Aul++) + (*Bdr++) + (*Cul++) + (*Adr++) + (*Dul++) + (*Edr++);
*norm = (*Aul) + (*Bdr) + (*Cul) + (*Adr) + (*Dul) + (*Edr);
}
inline void normalize(GLfloat *v){
GLfloat l,x,y,z;
x = v[0];
y = v[1];
z = v[2];
l = sqrtf((x*x) + (y*y) + (z*z));
v[0] = x / l;
v[1] = y / l;
v[2] = z / l;
}
/*
int WW = W*3;
#pragma omp parallel for private(y, ptr, normal)
for(y = 0; y < H; y++){
ptr = &tr_norms[Wstride*(y+1)+1];
normal = &norms->data[y*WW];
for(x = 0; x < W; x++, normal+=3, ptr+=6){
addVectors(normal, ptr);
normalize(normal);
}
}*/
for(y = 0; y < H; y++){
ptr = &tr_norms[Wstride*(y+1)+1];
for(x = 0; x < W; x++, normal+=3, ptr+=6){
addVectors(normal, ptr);
normalize(normal);
}
}
free(tr_norms);
DBG("time: %g", dtime() - t0);
}
/*
* A simple 3D-display selected in window->parent field
* Image corners coordinates (x0, y0), (x, y)
* Coordinates in CS IMAGES, ANGLES are outermost, not known
* window - a window with the 3D image
* X0, y0, x, y - the image area of the parent window, from which information will be taken
* from_file - TRUE to load the file into the same window (then the coordinates do not care)
* FALSE for download from the [sub] the image of the parent window
*/
gboolean loadTerrain(Window *window,
double x0, double y0, double x, double y,
gboolean from_file){
FNAME();
int i, j, imW;
IMAGE *verts, *norms; // vertexes & normals (pointers to w->im, w->im_tr)
IMAGE *image = NULL; // pointer to the parent image or a new image
int X0,Y0, X1,Y1; // here will be stored the coordinates of the upper left and lower right corners
int W, H; // Here are the dimensions of the selected area
GLfloat *in, *out; // Pointers to the points in the image
GLfloat scale; // scale (1/8 of the perimeter of selected region)
#ifdef EBUG
double t0 = dtime();
#endif
if(!from_file){
if(x0 < x){ X0 = (int)(x0+0.5); X1 = (int)(x+0.5);}
else{ X0 = (int)(x+0.5); X1 = (int)(x0+0.5);}
if(y0 < y){ Y0 = (int)(y0+0.5); Y1 = (int)(y+0.5);}
else{ Y0 = (int)(y+0.5); Y1 = (int)(y0+0.5);}
W = X1 - X0; H = Y1 - Y0;
DBG("subimage size: %dx%d", W, H);
DBG("subimage @ (%d,%d)-(%d,%d)", X0, Y0, X1, Y1);
if(W < MINSZ || H < MINSZ){
g_err(_("Selected area too small"));
return FALSE;
}
image = window->parent->image;
if(!copy_contours(image, window->image,
(float)-X0, (float)-Y0))
g_err(_("Error occured when tried to add contours"));
window->image->cLevels = image->cLevels;
window->image->stat.min = image->stat.min;
window->image->stat.max = image->stat.max;
}else{ // load to the same window from file
gchar *filename;
INIT(image, IMAGE);
if( !(filename = get_open_filename(window)) ||
!readfits(filename, image)){
g_free(filename);
destroy_image(image);
return FALSE;
}
g_free(filename);
X1 = W = image->width;
Y1 = H = image->height;
X0 = Y0 = 0;
}
// check if there are shoals with the filling of the structure of points and normals
free(window->image->data); window->image->data = NULL;
free(window->image_transformed->data); window->image_transformed->data = NULL;
if(!image || !image->data){
g_err(_("No image in parent window"));
goto abnormal_exit;
}
imW = image->width;
verts = window->image;
norms = window->image_transformed;
verts->data = calloc(W*H, sizeof(GLfloat));
if(!verts->data){
g_err(_("Can't allocate memory"));
goto abnormal_exit;
}
// normals for each point
norms->data = calloc(W*H*3, sizeof(GLfloat));
if(!norms->data){
g_err(_("Can't allocate memory"));
free(verts->data);
verts->data = NULL;
goto abnormal_exit;
}
verts->width = W; verts->height = H;
norms->width = W; norms->height = H;
scale = ((GLfloat)W + (GLfloat)H) / 4.f / (image->stat.max-image->stat.min);
// Copy the selected piece of image with scaling
#pragma omp parallel for
for(j = Y0; j < Y1; j++){
//int l = (j - Y0)*W;
in = &image->data[j * imW + X0];
out = &verts->data[(j - Y0)*W];
for(i = 0; i < W; i++)
(*out++) = (*in++) * scale;
}
if(from_file)
destroy_image(image);
DBG("time: %g", dtime() - t0);
return TRUE;
abnormal_exit:
if(from_file)
destroy_image(image);
return FALSE;
}
gboolean createList(Window *window){
FNAME();
TEXTURE *texture = window->texture; // number of list stores in texture->tex
GLfloat *Verts = window->image->data;
GLfloat *Norms = window->image_transformed->data;
int W = window->image->width, H = window->image->height;
int x, y, stride = 3 * W;
GLfloat dX = -(GLfloat)W / 2.f, dY = -(GLfloat)H / 2.f;
GtkWidget *Area = window->drawingArea;
if(!Area){
g_err("???");
return FALSE;
}
GdkGLContext *glContext = gtk_widget_get_gl_context(Area);
GdkGLDrawable *glDrawable = gtk_widget_get_gl_drawable(Area);
if(!gdk_gl_drawable_gl_begin(glDrawable, glContext))
g_assert_not_reached();
#ifdef EBUG
double t0 = dtime();
#endif
if(!texture){
INIT(window->texture, TEXTURE);
texture = window->texture;
if(!texture){
g_err(_("Can't allocate memory for texture"));
return FALSE;
}
if(useVBO){
glGenBuffersARB(1, &texture->tex);
glGenBuffersARB(1, &texture->w);
}else{
texture->tex = glGenLists(1);
texture->w = W;
}
if(texture->tex == 0){
g_err(_("Can't generate VBO / GL list"));
return FALSE;
}
}
texture->h = H;
if(useVBO){
GLint sz = W*H*3*sizeof(GLfloat), szi = W*(H-1)*2*sizeof(GLuint);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, texture->tex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, texture->w);
glBufferDataARB(GL_ARRAY_BUFFER_ARB, sz*2,
0, GL_STATIC_DRAW_ARB);
GLfloat *vertexes = malloc(sz);
GLuint *indexes = malloc(szi);
GLfloat *ptr = vertexes;
#pragma omp parallel for
for(y = 0; y < H; y++){
ptr = &vertexes[y*W*3];
Verts = &window->image->data[y*W];
for(x = 0; x < W; x++, Verts++){
*ptr++ = dX + x;
*ptr++ = dY + y;
*ptr++ = *Verts;
}
}
H--;
#pragma omp parallel for
for(y = 0; y < H; y++){
GLuint *iptr = &indexes[y*W*2];
GLuint idx = y*W;
for(int x = 0; x < W; x++, idx++){
*iptr++ = idx + W;
*iptr++ = idx;
}
}
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, szi, 0, GL_STATIC_DRAW_ARB);
glBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER, 0, szi, indexes);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, 0, sz, vertexes);
glBufferSubDataARB(GL_ARRAY_BUFFER_ARB, sz,
sz, window->image_transformed->data);
free(vertexes);
free(indexes);
free(window->image->data);
window->image->data = NULL;
free(window->image_transformed->data);
window->image_transformed->data = NULL;
//~ free(window->image_transformed); window->image_transformed = NULL;
}else{
glNewList(texture->tex, GL_COMPILE);
H--;
for(y = 0 ; y < H; y++){
glBegin(GL_TRIANGLE_STRIP);
for(x = 0; x < W; x++, Verts++, Norms+=3){
glNormal3fv(Norms);
glVertex3f(dX + x, dY + y, *Verts);
glNormal3fv(&Norms[stride]);
glVertex3f(dX + x, dY + (y + 1), Verts[W]);
}
glEnd();
}
glEndList();
}
gdk_gl_drawable_gl_end(glDrawable);
DBG("time: %g", dtime() - t0);
return TRUE;
}
/*
* External function for the preparation of [[section] [parental]] picture
* to download into 3D-box
* window - 3D-window
* x0, y0, x, y - the corners of the selected area from picture of the parent window
* from_file - TRUE to load from the called file selection window
* FALSE for download from the parent window
* If from_file == TRUE, the coordinates of the corners of subimage are ignored
*/
void terrain_3D(Window *window,
double x0, double y0, double x, double y,
gboolean from_file){
FNAME();
if(!window) return;
if(!loadTerrain(window, x0, y0, x, y, from_file)){
if(!from_file) destroy_window(window);
return;
}
computeNormals(window);
if(!createList(window)){
destroy_window(window);
return;
}
init3D(window); // initialisation of openGL window
}

565
src/tracking.c Normal file
View File

@ -0,0 +1,565 @@
// tracking.c - funcions for cutting tracking
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// 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 2 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, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "tracking.h"
#include "imtools.h"
#include "gauss.h"
#include "opengl.h"
const double LHWD = 5.; // length of the cross-mark bar
double x_1, y_1, x_2, y_2; // coordinates the selection frame or line
//double GW, GH; // height and width of the graph
double gY0, gYm, gXm; // range of values of Y, the maximum value of X
double gYmax; // limits to indicate the coordinates in the status bar
double xScale, yScale; // scale by axes
double bot_X = -1., top_X = -1.; // values (in units of X) of the lower and upper bounds on the abscissa
gint g_botX, g_topX; // the same, but in the coordinates of the window
/* the data for translation of the x coordinate from Pdata into coordinates x, y of image
* X = x0 + x*cx, Y= y0 + x*cy (cx - the cosine of the angle of the cutting, cy - sine) */
struct{
double x0; // starting poing
double y0;
double cx; // cosine of the angle of inclination
double cy; // sine -//-
} Pdata2image;
Points *Pdata = NULL; // array of data points
GdkPoint *gPoints = NULL; // points in window coordinates
Points selection = {0, NULL}; // the same for the selected region
GdkPoint *selPoints = NULL;
gint Npts; // number of data points
Point *alloc_Pdata(int N){
if(Pdata){
free(Pdata->data);
Pdata->data = NULL;
}
else
Pdata = malloc(sizeof(Pdata));
if(!Pdata){
g_err(_("Can't allocate memory for track points"));
return NULL;
}
Pdata->data = malloc(N*sizeof(Point));
if(!Pdata->data){
g_err(_("Can't allocate memory for track points"));
return NULL;
}
return Pdata->data;
}
// returns the coordinates xx and yy on the image (in CS of the window!)
// for the point with coordinate x in the graph
void get_image_crds(double x, double *xx, double *yy){
*xx = Pdata2image.x0 + x * Pdata2image.cx;
*yy = Pdata2image.y0 + x * Pdata2image.cy;
}
// reset the sample
void clear_selection(){
free(selection.data);
selection.data = NULL;
selection.n = 0;
}
/*
* filling an array Pdata based on the values of coordinates initial (X0, Y0)
* and final (X, Y) points
*/
void get_data(double X0, double X, double Y0, double Y, Window *window){
int n = 0, N; // new array size
IMAGE *image = window->image;
int x0, y0, yi, WD = image->width, HT = image->height;
double x, y, A, xstep, ystep;
Point *ptr;
gboolean Vert = FALSE;
double tgXY, cosXY;
inline void minmax(double amp){
if(gY0 > amp) gY0 = amp;
else if(gYm < amp) gYm = amp;
}
inline double dist(double dx, double dy){
return sqrtf(dx*dx + dy*dy);
}
gXm = dist(X-X0, Y-Y0);
N = (int)(gXm + 1.);
if(N < 2) return;
if(!image->data)
return;
if( X0 < 0. || X0 > WD || X < 0. || X > WD ||
Y0 < 0. || Y0 > HT || Y < 0. || Y > HT) return;
if(!(ptr = alloc_Pdata(N))) return;
Pdata2image.x0 = X0;
Pdata2image.y0 = Y0; //HT - 1. - Y0;
Pdata2image.cx = (X-X0)/gXm;
Pdata2image.cy = (Y-Y0)/gXm;
//DBG("N=%d, ", N);
x0 = (int)(X0 + 0.5);
y0 = (int)(Y0 + 0.5);
if(fabs(X-X0) < fabs(Y-Y0)){ // closer to the vertical
Vert = TRUE;
tgXY = (X-X0) / (Y-Y0);
cosXY = fabs(cos(atan(tgXY)));
}
else{
tgXY = (Y-Y0) / (X-X0);
cosXY = fabs(cos(atan(tgXY)));
}
yi = x0 + WD * y0;
gY0 = gYm = image->data[yi];// * wd + min;
if(Vert){
ystep = (Y > Y0) ? cosXY : -cosXY;
for(y = Y0; ; y+=ystep, n++, ptr++){
if(n > N) break;
if(ystep > 0.){
if(y > Y) break;
}
else{
if(y < Y) break;
}
ptr->x = (double)n;
x = X0 + tgXY * (y - Y0);
//yi = (int)(x+0.5) + WD * (HT - (int)(y+0.5));
yi = (int)(x+0.5) + WD * ((int)(y+0.5));
A = image->data[yi];// * wd + min;
ptr->y = A;
minmax(A);
}
}
else{
xstep = (X > X0)? cosXY : -cosXY;
for(x = X0; ; x+=xstep, n++, ptr++){
if(n > N) break;
if(xstep > 0.){
if(x > X) break;
}
else{
if(x < X) break;
}
y = Y0 + tgXY * (x-X0);
ptr->x = (double)n;
//yi = (int)(x+0.5) + WD * (HT - (int)(y+0.5));
yi = (int)(x+0.5) + WD * ((int)(y+0.5));
A = image->data[yi];// * wd + min;
ptr->y = A;
minmax(A);
}
}
//DBG("Ymin=%g, Ymax=%g\n", gY0, gYm);
Pdata->n = n - 1;
}
void draw_cross(GdkDrawable *d, GdkGC *gc, double x, double y){
gdk_draw_line(d, gc, x-LHWD,y, x+LHWD, y);
gdk_draw_line(d, gc, x,y-LHWD, x, y+LHWD);
}
// draw rectangle (gdk_draw_rectangle don't match because it require x,y > x0,y0
void draw_rect(GdkDrawable *d, GdkGC *gc, double x0, double y0, double x, double y){
gdk_draw_line(d, gc, x0,y0, x0,y);
gdk_draw_line(d, gc, x0,y0, x,y0);
gdk_draw_line(d, gc, x,y, x, y0);
gdk_draw_line(d, gc, x,y, x0, y);
}
// get coordinates of selected region
void get_sel_region(double *x0, double *y0, double *x, double *y){
*x0 = x_1; *y0 = y_1; *x = x_2; *y = y_2;
}
void do_tracking(double xx, double yy, gboolean start, Window *window){
static double xold, yold, X0, Y0;
double X, Y;
double imW = (double)window->texture->w;
double imH = (double)window->texture->h;
Context *c = window->context;
gboolean conv = FALSE;
x_2 = xx; y_2 = yy;
conv_mouse_to_image_coords(x_2, y_2, &X, &Y, window);
//FNAME();
if(!window->image->data) return;
if(X > imW){ X = imW; conv = TRUE;}
else if(X < 0){ X = 0; conv = TRUE;}
if(Y > imH){ Y = imH; conv = TRUE;}
else if(Y < 0){ Y = 0; conv = TRUE;}
if(conv) conv_image_to_mouse_coords(X, Y, &x_2, &y_2, window);
//DBG("X=%g, Y=%g, x=%g, y=%g", X, Y, x_2, y_2);
if(start){
clear_selection();
X0 = X; Y0 = Y;
x_1 = xold = x_2;
y_1 = yold = y_2;
return;
}
if(c->trackingMode == TRACK_VERT){
X = X0; x_2 = x_1;
}
else if(c->trackingMode == TRACK_HORIZ){
Y = Y0; y_2 = y_1;
}
else if(c->trackingMode == TRACK_DIAG){
double s, adx, ady;
gboolean changed = FALSE;
adx = fabs(X-X0); ady = fabs(Y-Y0);
void corrY(){
s = (Y > Y0) ? 1. : -1.;
Y = Y0 + s * adx;
y_2 = y_1 + s * fabs(x_2-x_1);
if(Y > imH || Y < 0.){
if(Y > imH) Y = imH;
else Y = 0.;
conv_image_to_mouse_coords(X, Y, &x_2, &y_2, window);
changed = TRUE;
}
/*
if(Y > imH){ Y = imH; y = Y/Daspect + Dy; changed = TRUE;}
else if(Y < 0){ Y = 0; y = Dy; changed = TRUE;}*/
ady = fabs(Y-Y0);
//DBG("Y=%g, Y0=%g", Y, Y0);
}
void corrX(){
s = (X > X0) ? 1. : -1;
X = X0 + s * ady;
x_2 = x_1 + s * fabs(y_2-y_1);
if(X > imW || X < 0.){
if(X > imW) X = imW;
else X = 0.;
conv_image_to_mouse_coords(X, Y, &x_2, &y_2, window);
changed = TRUE;
}
adx = fabs(X-X0);
//DBG("X=%g, X0=%g", X, X0);
}
if(adx > ady){
corrY();
if(changed) corrX();
}
else{
corrX();
if(changed) corrY();
}
}
GdkDrawable *d = window->drawingArea->window;
static GdkGC *gc = NULL;
if(!gc){
gc = gdk_gc_new(d);
gdk_gc_set_foreground(gc, &(window->drawingArea->style->white));
gdk_gc_set_function(gc, GDK_XOR);
}
if(c->drawingMode == DRAW_QUAD){// draw rectangle
draw_rect(d, gc, x_1,y_1, xold,yold);
draw_rect(d, gc, x_1,y_1, x_2,y_2);
}
else{ // draw lint
// first erase old one
gdk_draw_line(d, gc, x_1,y_1, xold,yold);
draw_cross(d, gc, xold, yold);
// then draw new one
gdk_draw_line(d, gc, x_1,y_1, x_2,y_2);
draw_cross(d, gc, x_2, y_2);
//DBG("Xima=%g, Yima=%g (X0=%g, Y0=%g)", X, Y, X0, Y0);
get_data(X0, X, Y0, Y, window);
bot_X = 0.; top_X = gXm;
//DBG("X=%g, X0=%g, Y=%g, Y0=%g", X, X0, Y, Y0);
gchar str[80];
g_snprintf(str, 79, "length=%.1f, angle=%.1fdegr",
sqrt((X-X0)*(X-X0)+(Y-Y0)*(Y-Y0)),
atan2(Y-Y0,X-X0)/M_PI*180.);
set_status_text(StatusText, str, window->parent);
set_status_text(StatusText, "", window);
Gconfigure(window->graphWindow);
gdk_window_invalidate_rect(window->graphWindow->drawingArea->window,
NULL, TRUE);
}
xold = x_2; yold = y_2;
}
gboolean Gexpose(Window *window){//, GdkEventExpose *event, gpointer userData){
cairo_t *cr = NULL;
int i;
GtkWidget *Area = window->drawingArea;
int H = (int)(Area->allocation.height + 0.5);
GdkPoint *gptr = &gPoints[1];
if(!cr){
cr = gdk_cairo_create(Area->window);
cairo_set_line_width (cr, 1.);
}
cairo_set_source_rgb(cr, 0., 0., 0.);
if(!Pdata || !gPoints)
return FALSE;
if(!Pdata->data || Pdata->n < 2)
return FALSE;
cairo_move_to(cr, gPoints->x, gPoints->y);
for(i = 1; i < Npts; i++, gptr++)
cairo_line_to(cr, gptr->x, gptr->y);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 0., 1., 0.);
cairo_move_to(cr, g_botX, 0);
cairo_line_to(cr, g_botX, H);
cairo_stroke(cr);
cairo_set_source_rgb(cr, 1., 0., 0.);
cairo_move_to(cr, g_topX, 0);
cairo_line_to(cr, g_topX, H);
cairo_stroke(cr);
if(selPoints && selection.n){
int n = selection.n;
cairo_set_source_rgb(cr, 0., 0., 1.);
cairo_move_to(cr, selPoints->x, selPoints->y);
gptr = &selPoints[1];
//DBG("n=%d", n);
for(i = 1; i < n; i++, gptr++)
cairo_line_to(cr, gptr->x, gptr->y);
cairo_stroke(cr);
}
cairo_destroy(cr);
return FALSE;
}
// calculation of the chart points in window coordinates
gboolean Gconfigure(Window *window){//, GdkEventConfigure *event, gpointer none){
int i, n;
Point *ptr;
//FNAME();
double W, H, ym, y0;
GdkPoint *gptr;
gboolean log_scale = isYlog(window);
if(!Pdata)
return FALSE;
if(!Pdata->data || Pdata->n < 2)
return FALSE;
GtkWidget *Area = window->drawingArea;
free(gPoints);
gPoints = NULL;
Npts = Pdata->n;
gPoints = malloc((Npts + 1)* sizeof(GdkPoint));
if(!gPoints){
g_err(_("Can't allocate memory for track points"));
return FALSE;
}
ptr = Pdata->data;
if(!ptr) return FALSE;
gptr = gPoints;
W = Area->allocation.width;
H = Area->allocation.height;
ym = gYm; y0 = gY0;
if(log_scale){
if(y0 < 0.) y0 = -log(-y0 + 1.);
else y0 = log(y0 + 1.);
if(ym < 0.) ym = -log(-ym + 1.);
else ym = log(ym + 1.);
}
xScale = W / gXm;
yScale = H / (ym - y0);
gYmax = ym;
for(i = 0; i < Npts; i++, ptr++, gptr++){
gptr->x = (int)(ptr->x * xScale + 0.5);
if(log_scale){
double yy = ptr->y;
if(yy < 0.)
yy = -log(-yy + 1.);
else
yy = log(yy + 1.);
gptr->y = (int)(H - (yy - y0) * yScale + 0.5);
}
else
gptr->y = (int)(H - (ptr->y - y0) * yScale + 0.5);
}
g_botX = (int)(bot_X * xScale + 0.5);
g_topX = (int)(top_X * xScale + 0.5);
// Long live code monkeys of all time!
if(selection.data) do{
n = selection.n;
free(selPoints);
selPoints = malloc((n+1)* sizeof(GdkPoint));
if(!selPoints) break;
gptr = selPoints;
ptr = selection.data;
for(i = 0; i < n; i++, ptr++, gptr++){
gptr->x = (int)(ptr->x * xScale + 0.5);
if(log_scale){
double yy = ptr->y;
if(yy < 0.)
yy = -log(-yy + 1.);
else
yy = log(yy + 1.);
gptr->y = (int)(H - (yy - y0) * yScale + 0.5);
}
else
gptr->y = (int)(H - (ptr->y - y0) * yScale + 0.5);
// DBG("g: (%g, %g), w: (%d, %d)", ptr->x, ptr->y, gptr->x, gptr->y);
}
}while(0);
/*
if(ym > 1000.)
set_Grulers(y0/1000, gXm, ym/1000);
else*/
set_Grulers(y0, gXm, ym, window);
return FALSE;
}
// display mode of the histogram in the field of graph
void do_histogram(Window *window){
FNAME();
IMAGE *image = window->context->current_image;
if(!image || !image->data) image = window->image;
if(!image || !image->data) return;
int i, hsize = image->stat.histogram.size;
int *hptr = image->stat.histogram.data;
Point *ptr;
double histsiz = (double)image->stat.histogram.size;
double scale = image->stat.histogram.scale * (double)image->stat.histogram.size;
//double imin = image->stat.min;
if(!(ptr = alloc_Pdata(hsize))) return;
gXm = 1.;//scale+imin;
scale /= histsiz;
gYm = (double)(image->stat.histogram.max);
gY0 = (double)(image->stat.histogram.min);
for(i = 0; i < hsize; i++, ptr++, hptr++){
ptr->x = (double) i / histsiz;
//ptr->x = (double) i * scale + imin;
ptr->y = (double) *hptr;
}
Pdata->n = hsize;
DBG("bot: %g, top: %g", image->stat.histogram.bot_tres, image->stat.histogram.top_tres);
if(image->stat.histogram.bot_tres > 0.)
bot_X = image->stat.histogram.bot_tres;
else
bot_X = 0.;
if(image->stat.histogram.top_tres > 0.)
top_X = image->stat.histogram.top_tres;
else
top_X = gXm;
Gconfigure(window->graphWindow);
gdk_window_invalidate_rect(window->graphWindow->drawingArea->window, NULL, TRUE);
}
/*
* will try to move boundaries when clicking near graph borders
* when clicked near a curve - something else can be done ...
* Window - graphics window
* Return value: TRUE, if will need to handle mouse motion events further
*/
gboolean graph_mouse_btn(int x, Window *window){
gboolean redraw = FALSE;
gboolean ret = FALSE;
double W, xScale, tmp;
GtkWidget *graphArea = window->drawingArea;
int dist = (g_topX - g_botX) / 2; // half of a distance between borders
if(dist < PT_TRESHOLD) dist = PT_TRESHOLD;
W = graphArea->allocation.width;
xScale = W / gXm;
if(abs(x - g_botX) < dist || x < g_botX){ // select down border
tmp = (double) x / xScale;
if(tmp < top_X) bot_X = tmp;
if(bot_X < 0.) bot_X = 0.;
ret = TRUE;
redraw = TRUE;
}
else if(abs(x - g_topX) < dist || x > g_topX){ // select up border
tmp = (double) x / xScale;
if(tmp > bot_X) top_X = tmp;
if(top_X > gXm) top_X = gXm;
ret = TRUE;
redraw = TRUE;
}
if(redraw){
gchar msg[80];
//DBG("move: botX=%g, topX=%g", bot_X, top_X);
g_snprintf(msg, 79,
_("Selected region from %.1f to %.1f, %d datapoints"),
bot_X, top_X, (int)(((double)Npts)/gXm*(top_X-bot_X)+0.5));
set_status_text(StatusText, msg, window);
Gconfigure(window);
gdk_window_invalidate_rect(graphArea->window, NULL, TRUE);
}
return ret;
}
void gen_tres_texture(Window *window){
window->image->stat.histogram.bot_tres = bot_X;
window->image->stat.histogram.top_tres = top_X;
treshold_image(window, bot_X / gXm, top_X / gXm);
}
gboolean isYlog(Window *window){
gboolean log_scale = FALSE;
if(window->context->graphMode == GR_GRAPH){
if(window->context->graphYaxis & Y_LOGGRAPH)
log_scale = TRUE;
} else if(window->context->graphYaxis & Y_LOGHIST)
log_scale = TRUE;
return log_scale;
}
gboolean fit_gaussian(Window *window){
//FNAME();
Point *pt, *pt0;
double C, A, s, x0;
double xx, yy;
#ifndef GSL_FOUND
GSLERR;
return FALSE;
#endif
if(!window->parent || !window->parent->texture){
g_err(_("No parent window or image"));
return FALSE;
}
double imW = (double)window->parent->texture->w;
double imH = (double)window->parent->texture->h;
gchar msg[160];
GtkWidget *graphArea = window->drawingArea;
if(!Pdata){ // no points? (O_o, but such a thing be?)
g_err(_("No data points"));
return FALSE;
}
int n = (int)(((double)Npts)/gXm*(top_X-bot_X) + 0.5);
if(n < 10 || n > (int)(sqrt(imW * imW + imH * imH))){ // few or many points
g_err(_("Bad amount of data points"));
return FALSE;
}
int n0 = (int)(((double)Npts)/gXm*bot_X); // the number of a first point
if(n0 < 0 || (n0 + n) > Npts){ // do not get there?
g_err(_("Invalid number of first point"));
return FALSE;
}
clear_selection();
selection.n = n;
selection.data = malloc(n * sizeof(Point));
if(!selection.data){
g_err(_("Can't allocate memory"));
return FALSE;
}
pt = selection.data;
pt0 = &Pdata->data[n0];
DBG("pt0: (%g, %g)", pt0->x, pt0->y);
// Copy the desired sample into a separate array
for(; n--; pt++, pt0++){
pt->x = pt0->x;
pt->y = pt0->y;
}
gauss_fit(&selection, &C, &A, &s, &x0);
gaussian_v(C, A, s, x0, &selection);
get_image_crds(x0, &xx, &yy);
//DBG("move: botX=%g, topX=%g", bot_X, top_X);
g_snprintf(msg, 159,
_("Fit gaussian, x0=%.2f I(%.2f, %.2f), s=%.2f, A=%.1f, C=%.3f"),
x0, xx, yy, s, A, C);
set_status_text(StatusText, msg, window);
Gconfigure(window);
gdk_window_invalidate_rect(graphArea->window, NULL, TRUE);
return TRUE;
}

1281
src/ui.h Normal file

File diff suppressed because it is too large Load Diff