Basic working GUI
This commit is contained in:
+15
-43
@@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(eibin VERSION 0.1 LANGUAGES CXX)
|
||||
project(kouryu VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
@@ -9,53 +9,27 @@ set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
|
||||
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
|
||||
find_package(OpenCV REQUIRED)
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
main.cpp
|
||||
mainwindow.cpp
|
||||
mainwindow.h
|
||||
mainwindow.ui
|
||||
serfile.cpp
|
||||
serfile.h
|
||||
mainwindow.cpp mainwindow.h mainwindow.ui
|
||||
serfile.cpp serfile.h
|
||||
laplacian.cpp laplacian.h
|
||||
)
|
||||
|
||||
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
||||
qt_add_executable(eibin
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define target properties for Android with Qt 6 as:
|
||||
# set_property(TARGET eibin APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
|
||||
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
|
||||
else()
|
||||
if(ANDROID)
|
||||
add_library(eibin SHARED
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
# Define properties for Android with Qt 5 after find_package() calls as:
|
||||
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
else()
|
||||
add_executable(eibin
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
qt_add_executable(kouryu
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(eibin PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OpenCV_LIBS})
|
||||
target_include_directories(eibin PRIVATE ${OpenCV_INCLUDE_DIRS})
|
||||
target_compile_options(eibin PRIVATE -mavx2)
|
||||
target_link_libraries(kouryu PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${OpenCV_LIBS})
|
||||
target_include_directories(kouryu PRIVATE ${OpenCV_INCLUDE_DIRS})
|
||||
target_compile_options(kouryu PRIVATE -mavx2)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
# explicit, fixed bundle identifier manually though.
|
||||
if(${QT_VERSION} VERSION_LESS 6.1.0)
|
||||
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.eibin)
|
||||
endif()
|
||||
set_target_properties(eibin PROPERTIES
|
||||
set_target_properties(kouryu PROPERTIES
|
||||
${BUNDLE_ID_OPTION}
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||
@@ -64,12 +38,10 @@ set_target_properties(eibin PROPERTIES
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
install(TARGETS eibin
|
||||
install(TARGETS kouryu
|
||||
BUNDLE DESTINATION .
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt_finalize_executable(eibin)
|
||||
endif()
|
||||
qt_finalize_executable(kouryu)
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
#include "laplacian.h"
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
double laplacian(const uint16_t *img, int32_t *out, uint32_t width, uint32_t height)
|
||||
{
|
||||
__m256 mean = _mm256_setzero_ps();
|
||||
__m256 M2 = _mm256_setzero_ps();
|
||||
uint32_t count = 0;
|
||||
for(uint32_t y = 1; y < height - 1; y++)
|
||||
{
|
||||
uint32_t row = (y - 1) * width;
|
||||
for(uint32_t x = 1; x < width - 8; x += 8)
|
||||
{
|
||||
__m128i p0 = _mm_loadu_si128(reinterpret_cast<__m128i const*>(img + row + x));
|
||||
__m128i p1 = _mm_loadu_si128(reinterpret_cast<__m128i const*>(img + (row + width) + x - 1));
|
||||
__m128i p2 = _mm_loadu_si128(reinterpret_cast<__m128i const*>(img + (row + width) + x));
|
||||
__m128i p3 = _mm_loadu_si128(reinterpret_cast<__m128i const*>(img + (row + width) + x + 1));
|
||||
__m128i p4 = _mm_loadu_si128(reinterpret_cast<__m128i const*>(img + (row + width * 2) + x));
|
||||
|
||||
__m256i sum = _mm256_setzero_si256();
|
||||
|
||||
__m256i a;
|
||||
a = _mm256_cvtepu16_epi32(p0);
|
||||
sum = _mm256_add_epi32(sum, _mm256_cvtepu16_epi32(p0));
|
||||
sum = _mm256_add_epi32(sum, _mm256_cvtepu16_epi32(p1));
|
||||
sum = _mm256_add_epi32(sum, _mm256_cvtepu16_epi32(p3));
|
||||
sum = _mm256_add_epi32(sum, _mm256_cvtepu16_epi32(p4));
|
||||
sum = _mm256_sub_epi32(sum, _mm256_sll_epi32(_mm256_cvtepu16_epi32(p2), _mm_set1_epi64x(2)));
|
||||
|
||||
if(out)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(out + row + x), sum);
|
||||
}
|
||||
|
||||
__m256 af = _mm256_cvtepi32_ps(sum);
|
||||
|
||||
count++;
|
||||
__m256 delta = _mm256_sub_ps(af, mean);
|
||||
mean = _mm256_add_ps(mean, _mm256_div_ps(delta, _mm256_set1_ps(static_cast<float>(count))));
|
||||
__m256 delta2 = _mm256_sub_ps(af, mean);
|
||||
M2 = _mm256_add_ps(M2, _mm256_mul_ps(delta, delta2));
|
||||
}
|
||||
}
|
||||
float mean_2[8];
|
||||
float M2_2[8];
|
||||
_mm256_storeu_ps(mean_2, mean);
|
||||
_mm256_storeu_ps(M2_2, M2);
|
||||
|
||||
auto welford_merge = [](uint32_t n, float &mean_1, float mean_2, float &M2_1, float M2_2)
|
||||
{
|
||||
uint32_t count = 2 * n;
|
||||
float delta = mean_2 - mean_1;
|
||||
float mean = mean_1 + delta * ((float)n / count);
|
||||
float M2 = M2_1 + M2_2 + delta * delta * n * n / count;
|
||||
mean_1 = mean;
|
||||
M2_1 = M2;
|
||||
};
|
||||
|
||||
/*for(int i = 0; i < 8; i++)
|
||||
qDebug() << M2_2[i] / count;*/
|
||||
|
||||
welford_merge(count, mean_2[0], mean_2[1], M2_2[0], M2_2[1]);
|
||||
welford_merge(count, mean_2[2], mean_2[3], M2_2[2], M2_2[3]);
|
||||
welford_merge(count, mean_2[4], mean_2[5], M2_2[4], M2_2[5]);
|
||||
welford_merge(count, mean_2[6], mean_2[7], M2_2[6], M2_2[7]);
|
||||
|
||||
welford_merge(count * 2, mean_2[0], mean_2[2], M2_2[0], M2_2[2]);
|
||||
welford_merge(count * 2, mean_2[4], mean_2[6], M2_2[4], M2_2[6]);
|
||||
|
||||
welford_merge(count * 4, mean_2[0], mean_2[4], M2_2[0], M2_2[4]);
|
||||
|
||||
return (double)M2_2[0] / (count * 8);
|
||||
}
|
||||
|
||||
bool reflow(const cv::Mat &ref, const cv::Mat &img, cv::Mat &warped)
|
||||
{
|
||||
try
|
||||
{
|
||||
cv::Mat flow(ref.size(), CV_32FC2);
|
||||
cv::calcOpticalFlowFarneback(ref, img, flow, 0.5, 3, 40, 3, 5, 1.2, 0);
|
||||
|
||||
std::vector<cv::Mat> flow_xy(2);
|
||||
cv::split(flow, flow_xy);
|
||||
cv::Mat flow_x = flow_xy[0];
|
||||
cv::Mat flow_y = flow_xy[1];
|
||||
|
||||
// --- Build map_x and map_y for remapping
|
||||
cv::Mat map_x(ref.size(), CV_32FC1);
|
||||
cv::Mat map_y(ref.size(), CV_32FC1);
|
||||
|
||||
for (int y = 0; y < ref.rows; y++) {
|
||||
for (int x = 0; x < ref.cols; x++) {
|
||||
map_x.at<float>(y, x) = x + flow_x.at<float>(y, x);
|
||||
map_y.at<float>(y, x) = y + flow_y.at<float>(y, x);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Warp img1 to align it with img2 using the optical flow
|
||||
cv::remap(img, warped, map_x, map_y, cv::INTER_LANCZOS4);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#ifndef LAPLACIAN_H
|
||||
#define LAPLACIAN_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <opencv2/opencv.hpp>
|
||||
|
||||
double laplacian(const uint16_t *img, int32_t *out, uint32_t width, uint32_t height);
|
||||
bool reflow(const cv::Mat &ref, const cv::Mat &img, cv::Mat &warped);
|
||||
|
||||
#endif // LAPLACIAN_H
|
||||
@@ -2,8 +2,12 @@
|
||||
|
||||
#include "serfile.h"
|
||||
#include <QApplication>
|
||||
#include <QElapsedTimer>
|
||||
#include <QThread>
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
#include <QQueue>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <iostream>
|
||||
#include <complex>
|
||||
#include <cmath>
|
||||
|
||||
@@ -209,202 +213,81 @@ void fft(std::vector<std::complex<float>> &x, bool inv = false)
|
||||
}
|
||||
}
|
||||
|
||||
double laplacian(const uint16_t *img, int32_t *out, uint32_t width, uint32_t height)
|
||||
|
||||
|
||||
//光流
|
||||
void opticalflow()
|
||||
{
|
||||
__m256 mean = _mm256_setzero_ps();
|
||||
__m256 M2 = _mm256_setzero_ps();
|
||||
uint32_t count = 0;
|
||||
for(uint32_t y = 1; y < height - 1; y++)
|
||||
cv::Mat frame1, prvs;
|
||||
SERFileReader ser;
|
||||
ser.open("/media/data/indi_2025-09-29/indi_record_2025-09-29@17-29-08.ser");
|
||||
frame1 = cv::Mat(ser.height(), ser.width(), CV_16U);
|
||||
ser.getFrame(0, (char*)frame1.data);
|
||||
|
||||
cv::Mat frame2(ser.height(), ser.width(), CV_16U);
|
||||
for(int i=0; i<ser.frameCount(); i++)
|
||||
{
|
||||
uint32_t row = (y - 1) * width;
|
||||
for(uint32_t x = 1; x < width - 17; x += 16)
|
||||
{
|
||||
__m256i p0 = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(img + row + x));
|
||||
__m256i p1 = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(img + (row + width) + x - 1));
|
||||
__m256i p2 = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(img + (row + width) + x));
|
||||
__m256i p3 = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(img + (row + width) + x + 1));
|
||||
__m256i p4 = _mm256_loadu_si256(reinterpret_cast<__m256i const*>(img + (row + width * 2) + x));
|
||||
ser.getFrame(i, (char*)frame2.data);
|
||||
if (frame2.empty())
|
||||
break;
|
||||
|
||||
__m256i sumA = _mm256_setzero_si256();
|
||||
__m256i sumB = _mm256_setzero_si256();
|
||||
cv::Mat flow(prvs.size(), CV_32FC2);
|
||||
cv::calcOpticalFlowFarneback(frame1, frame2, flow, 0.5, 3, 40, 3, 5, 1.2, 0);
|
||||
|
||||
__m256i a,b;
|
||||
a = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p0, 0));
|
||||
b = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p0, 1));
|
||||
sumA = _mm256_add_epi32(sumA, a);
|
||||
sumB = _mm256_add_epi32(sumB, b);
|
||||
|
||||
a = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p1, 0));
|
||||
b = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p1, 1));
|
||||
sumA = _mm256_add_epi32(sumA, a);
|
||||
sumB = _mm256_add_epi32(sumB, b);
|
||||
std::vector<cv::Mat> flow_xy(2);
|
||||
cv::split(flow, flow_xy);
|
||||
cv::Mat flow_x = flow_xy[0];
|
||||
cv::Mat flow_y = flow_xy[1];
|
||||
|
||||
a = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p2, 0));
|
||||
b = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p2, 1));
|
||||
a = _mm256_sll_epi32(a, _mm_set1_epi64x(2));
|
||||
b = _mm256_sll_epi32(b, _mm_set1_epi64x(2));
|
||||
sumA = _mm256_sub_epi32(sumA, a);
|
||||
sumB = _mm256_sub_epi32(sumB, b);
|
||||
// --- Build map_x and map_y for remapping
|
||||
cv::Mat map_x(frame1.size(), CV_32FC1);
|
||||
cv::Mat map_y(frame1.size(), CV_32FC1);
|
||||
|
||||
a = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p3, 0));
|
||||
b = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p3, 1));
|
||||
sumA = _mm256_add_epi32(sumA, a);
|
||||
sumB = _mm256_add_epi32(sumB, b);
|
||||
|
||||
a = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p4, 0));
|
||||
b = _mm256_cvtepu16_epi32(_mm256_extracti128_si256(p4, 1));
|
||||
sumA = _mm256_add_epi32(sumA, a);
|
||||
sumB = _mm256_add_epi32(sumB, b);
|
||||
|
||||
if(out)
|
||||
{
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(out + row + x), sumA);
|
||||
_mm256_storeu_si256(reinterpret_cast<__m256i*>(out + row + x + 8), sumB);
|
||||
for (int y = 0; y < frame1.rows; y++) {
|
||||
for (int x = 0; x < frame1.cols; x++) {
|
||||
map_x.at<float>(y, x) = x + flow_x.at<float>(y, x);
|
||||
map_y.at<float>(y, x) = y + flow_y.at<float>(y, x);
|
||||
}
|
||||
|
||||
__m256 af = _mm256_cvtepi32_ps(sumA);
|
||||
__m256 bf = _mm256_cvtepi32_ps(sumB);
|
||||
|
||||
count++;
|
||||
__m256 delta = _mm256_sub_ps(af, mean);
|
||||
mean = _mm256_add_ps(mean, _mm256_div_ps(delta, _mm256_set1_ps(static_cast<float>(count))));
|
||||
__m256 delta2 = _mm256_sub_ps(af, mean);
|
||||
M2 = _mm256_add_ps(M2, _mm256_mul_ps(delta, delta2));
|
||||
|
||||
count++;
|
||||
delta = _mm256_sub_ps(bf, mean);
|
||||
mean = _mm256_add_ps(mean, _mm256_div_ps(delta, _mm256_set1_ps(static_cast<float>(count))));
|
||||
delta2 = _mm256_sub_ps(bf, mean);
|
||||
M2 = _mm256_add_ps(M2, _mm256_mul_ps(delta, delta2));
|
||||
|
||||
//count += 1
|
||||
//delta = new_value - mean
|
||||
//mean += delta / count
|
||||
//delta2 = new_value - mean
|
||||
//M2 += delta * delta2
|
||||
}
|
||||
|
||||
// --- Warp img1 to align it with img2 using the optical flow
|
||||
cv::Mat warped;
|
||||
cv::remap(frame2, warped, map_x, map_y, cv::INTER_LANCZOS4);
|
||||
cv::imshow("orig", frame2);
|
||||
cv::imshow("warp", warped);
|
||||
/*int key = cv::waitKey(3);
|
||||
if (key == 'q' || key == 27)
|
||||
break;
|
||||
continue;*/
|
||||
|
||||
// visualization
|
||||
cv::Mat flow_parts[2];
|
||||
split(flow, flow_parts);
|
||||
cv::Mat magnitude, angle, magn_norm;
|
||||
cv::cartToPolar(flow_parts[0], flow_parts[1], magnitude, angle, true);
|
||||
cv::normalize(magnitude, magn_norm, 0.0f, 1.0f, cv::NORM_MINMAX);
|
||||
cv::imshow("mag", magn_norm);
|
||||
/*angle *= ((1.f / 360.f) * (180.f / 255.f));
|
||||
//build hsv image
|
||||
cv::Mat _hsv[3], hsv, hsv8, bgr;
|
||||
_hsv[0] = angle;
|
||||
_hsv[1] = cv::Mat::ones(angle.size(), CV_32F);
|
||||
_hsv[2] = magn_norm;
|
||||
merge(_hsv, 3, hsv);
|
||||
hsv.convertTo(hsv8, CV_8U, 255.0);
|
||||
cvtColor(hsv8, bgr, cv::COLOR_HSV2BGR);
|
||||
imshow("frame2", bgr);*/
|
||||
int keyboard = cv::waitKey(30);
|
||||
if (keyboard == 'q' || keyboard == 27)
|
||||
break;
|
||||
}
|
||||
float mean_2[8];
|
||||
float M2_2[8];
|
||||
_mm256_storeu_ps(mean_2, mean);
|
||||
_mm256_storeu_ps(M2_2, M2);
|
||||
|
||||
auto welford_merge = [](uint32_t n, float &mean_1, float mean_2, float &M2_1, float M2_2)
|
||||
{
|
||||
uint32_t count = 2 * n;
|
||||
float delta = mean_2 - mean_1;
|
||||
float mean = mean_1 + delta * ((float)n / count);
|
||||
float M2 = M2_1 + M2_2 + delta * delta * n * n / count;
|
||||
mean_1 = mean;
|
||||
M2_1 = M2;
|
||||
};
|
||||
|
||||
for(int i = 0; i < 8; i++)
|
||||
qDebug() << M2_2[i] / count;
|
||||
|
||||
welford_merge(count, mean_2[0], mean_2[1], M2_2[0], M2_2[1]);
|
||||
welford_merge(count, mean_2[2], mean_2[3], M2_2[2], M2_2[3]);
|
||||
welford_merge(count, mean_2[4], mean_2[5], M2_2[4], M2_2[5]);
|
||||
welford_merge(count, mean_2[6], mean_2[7], M2_2[6], M2_2[7]);
|
||||
|
||||
welford_merge(count * 2, mean_2[0], mean_2[2], M2_2[0], M2_2[2]);
|
||||
welford_merge(count * 2, mean_2[4], mean_2[6], M2_2[4], M2_2[6]);
|
||||
|
||||
welford_merge(count * 4, mean_2[0], mean_2[4], M2_2[0], M2_2[4]);
|
||||
|
||||
return (double)M2_2[0] / (count * 8);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SERFileReader ser;
|
||||
ser.open("/home/nou/.wine/drive_c/indi_2025-10-03/indi_record_2025-10-03@18-24-37.ser");
|
||||
|
||||
cv::Rect rect(1024, 1024, 128, 128);
|
||||
double maxQ = 0;
|
||||
cv::Mat best;
|
||||
cv::Mat lap;
|
||||
cv::Mat first(ser.height(), ser.width(), CV_16U);
|
||||
cv::Mat img(ser.height(), ser.width(), CV_16U);
|
||||
cv::Mat out(ser.height(), ser.width(), CV_32S);
|
||||
cv::Mat imgf32;
|
||||
ser.getFrame(0, (char*)first.data);
|
||||
first.convertTo(first, CV_32F);
|
||||
for(uint32_t i = 0; i < ser.frameCount(); i++)
|
||||
{
|
||||
ser.getFrame(i, (char*)img.data);
|
||||
double var = laplacian((uint16_t*)img.data, (int32_t*)out.data, img.cols, img.rows);
|
||||
double minval, maxval;
|
||||
cv::minMaxLoc(out, &minval, &maxval);
|
||||
out.convertTo(out, CV_32F, 1.0 / (maxval - minval), -minval / (maxval - minval));
|
||||
qDebug() << "minmax" << minval << maxval;
|
||||
cv::imshow("lap", out);
|
||||
|
||||
img.convertTo(imgf32, CV_32F);
|
||||
cv::Laplacian(imgf32, lap, CV_32F, 1);
|
||||
cv::minMaxLoc(lap, &minval, &maxval);
|
||||
qDebug() << "minmax" << minval << maxval;
|
||||
cv::Mat stddev;
|
||||
cv::Mat mean;
|
||||
cv::meanStdDev(lap, mean, stddev);
|
||||
lap -= minval;
|
||||
lap /= (maxval - minval);
|
||||
cv::imshow("lapcv", lap);
|
||||
cv::waitKey();
|
||||
qDebug() << var << std::sqrt(var) << stddev.at<double>(0);
|
||||
//continue;
|
||||
return 0;
|
||||
|
||||
img.convertTo(imgf32, CV_32F);
|
||||
cv::Laplacian(imgf32, lap, CV_32F, 1);
|
||||
cv::Point2d off = cv::phaseCorrelate(first(rect), imgf32(rect));
|
||||
if(maxQ < stddev.at<double>(0))
|
||||
{
|
||||
maxQ = stddev.at<double>(0);
|
||||
img.copyTo(best);
|
||||
//qDebug() << "new best" << i;
|
||||
}
|
||||
}
|
||||
cv::imshow("lap", best);
|
||||
cv::waitKeyEx();
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
cv::Mat img1= cv::imread("/home/nou/Obrázky/astro/moon_2025-10-03/R.tif", cv::IMREAD_GRAYSCALE);
|
||||
cv::Mat img2= cv::imread("/home/nou/Obrázky/astro/moon_2025-10-03/G.tif", cv::IMREAD_GRAYSCALE);
|
||||
|
||||
img1.convertTo(img1, CV_32F);
|
||||
img2.convertTo(img2, CV_32F);
|
||||
|
||||
cv::Point2d point = cv::phaseCorrelate(img1, img2);
|
||||
|
||||
|
||||
cv::Mat img2dst;
|
||||
cv::Mat t(2, 3, CV_32F);
|
||||
t.at<float>(0, 0) = 1.0;
|
||||
t.at<float>(1, 1) = 1.0;
|
||||
t.at<float>(0, 2) = -point.x;
|
||||
t.at<float>(1, 2) = -point.y;
|
||||
cv::warpAffine(img2, img2dst, t, img1.size());
|
||||
|
||||
//cv::imshow("img1", img1 / 64.0);
|
||||
auto diff = img1 - img2dst;
|
||||
double min, max;
|
||||
cv::minMaxLoc(diff, &min, &max);
|
||||
qDebug() << min << max;
|
||||
cv::imshow("img2", (diff - min) / (32));
|
||||
cv::imwrite("diff.png", (diff - min) / (max - min) * 255);
|
||||
cv::imwrite("avg.png", (img1 + img2dst) * 0.5);
|
||||
|
||||
cv::minMaxLoc(img1, &min, &max);
|
||||
qDebug() << min << max;
|
||||
cv::waitKey();
|
||||
|
||||
|
||||
return 0;
|
||||
/*QApplication a(argc, argv);
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
return a.exec();*/
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
+135
-3
@@ -1,15 +1,147 @@
|
||||
#include "mainwindow.h"
|
||||
#include "./ui_mainwindow.h"
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
#include <QPromise>
|
||||
#include "laplacian.h"
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
: QMainWindow(parent)
|
||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
, ui(new Ui::MainWindow)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->actionOpen, &QAction::triggered, this, &MainWindow::openFile);
|
||||
connect(ui->actionSave, &QAction::triggered, this, &MainWindow::saveImage);
|
||||
connect(ui->analyzeButton, &QPushButton::clicked, this, &MainWindow::analyze);
|
||||
connect(ui->stackButton, &QPushButton::clicked, this, &MainWindow::stack);
|
||||
connect(ui->cancelButton, &QPushButton::clicked, this, [this](){
|
||||
if(_stack.isRunning())_stack.cancel();
|
||||
if(_analyze.isRunning())_analyze.cancel();
|
||||
});
|
||||
|
||||
ui->progressBar->hide();
|
||||
|
||||
connect(&_watcherAnalyze, &QFutureWatcher<void>::progressValueChanged, [this](int val){ ui->progressBar->setValue(val); });
|
||||
connect(&_watcherAnalyze, &QFutureWatcher<void>::finished, [this](){ ui->progressBar->hide(); ui->stackButton->setEnabled(true); });
|
||||
connect(&_watcherAnalyze, &QFutureWatcher<void>::canceled, [this](){ ui->progressBar->hide(); });
|
||||
connect(&_watcherAnalyze, &QFutureWatcher<void>::started, [this](){ ui->progressBar->show(); });
|
||||
connect(&_watcherStack, &QFutureWatcher<void>::progressValueChanged, [this](int val){ ui->progressBar->setValue(val); });
|
||||
connect(&_watcherStack, &QFutureWatcher<void>::finished, [this](){ ui->progressBar->hide(); stackFinish(); });
|
||||
connect(&_watcherStack, &QFutureWatcher<void>::canceled, [this](){ ui->progressBar->hide(); });
|
||||
connect(&_watcherStack, &QFutureWatcher<void>::started, [this](){ ui->progressBar->show(); });
|
||||
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::openFile()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this, tr("Open file"), QString(), tr("SER video (*.ser)"));
|
||||
_variance.clear();
|
||||
ui->stackButton->setEnabled(false);
|
||||
ui->analyzeButton->setEnabled(false);
|
||||
if(!fileName.isEmpty())
|
||||
{
|
||||
if(!_ser.open(fileName))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Failed to open file"), tr("Failed to open %1").arg(fileName));
|
||||
_ser.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if(_ser.pixelDepth() < 9 || _ser.pixelDepth() > 16 || _ser.pixelFormat() != SERPixelFormat::MONO)
|
||||
{
|
||||
QMessageBox::critical(this, tr("Invalid format"), tr("Only 9-16 bit mono video file supported"));
|
||||
ui->stackButton->setEnabled(false);
|
||||
_ser.close();
|
||||
return;
|
||||
}
|
||||
|
||||
ui->analyzeButton->setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::saveImage()
|
||||
{
|
||||
if(_stack.isValid())
|
||||
{
|
||||
QString fileName = QFileDialog::getSaveFileName(this, tr("Save image"), QString(), tr("Image (*.png)"));
|
||||
if(!fileName.isEmpty())
|
||||
{
|
||||
cv::Mat stack = _stack.result();
|
||||
stack.convertTo(stack, CV_16U, 65355.0);
|
||||
QImage img(stack.data, stack.cols, stack.rows, stack.cols * 2, QImage::Format_Grayscale16);
|
||||
img.save(fileName, "PNG");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::analyze()
|
||||
{
|
||||
if(_ser.frameCount() && !_analyze.isRunning() && !_stack.isRunning())
|
||||
{
|
||||
_analyze = QtConcurrent::run([this](QPromise<int> &promise) {
|
||||
promise.start();
|
||||
cv::Mat img(_ser.height(), _ser.width(), CV_16U);
|
||||
_variance.clear();
|
||||
promise.setProgressRange(0, 100);
|
||||
for(uint32_t i = 0; i < _ser.frameCount() && !promise.isCanceled(); i++)
|
||||
{
|
||||
_ser.getFrame(i, (char*)img.data);
|
||||
double var = laplacian((uint16_t*)img.data, nullptr, img.cols, img.rows);
|
||||
_variance.push_back({i, var});
|
||||
promise.setProgressValue(i * 100.0 / _ser.frameCount());
|
||||
}
|
||||
std::sort(_variance.begin(), _variance.end(), [](const std::pair<int, double> &a, const std::pair<int, double> &b){
|
||||
return a.second > b.second;
|
||||
});
|
||||
|
||||
promise.finish();
|
||||
});
|
||||
_watcherAnalyze.setFuture(_analyze);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::stack()
|
||||
{
|
||||
if(!_variance.empty() && !_analyze.isRunning() && !_stack.isRunning())
|
||||
{
|
||||
_stack = QtConcurrent::run([this](QPromise<cv::Mat> &promise, uint32_t frames) {
|
||||
promise.start();
|
||||
cv::Mat img(_ser.height(), _ser.width(), CV_16U);
|
||||
cv::Mat ref(_ser.height(), _ser.width(), CV_16U);
|
||||
cv::Mat stack(_ser.height(), _ser.width(), CV_32F);
|
||||
_ser.getFrame(_variance[0].first, (char*)ref.data);
|
||||
stack += ref;
|
||||
cv::Mat warp;
|
||||
promise.setProgressRange(0, 100);
|
||||
for(uint32_t i = 1; i < frames && !promise.isCanceled(); i++)
|
||||
{
|
||||
_ser.getFrame(_variance[i].first, (char*)img.data);
|
||||
reflow(ref, img, warp);
|
||||
stack += warp;
|
||||
promise.setProgressValue(i * 100.0 / frames);
|
||||
}
|
||||
cv::Mat norm;
|
||||
cv::normalize(stack, norm, 0.0, 1.0, cv::NORM_MINMAX);
|
||||
promise.addResult(norm);
|
||||
promise.finish();
|
||||
}, ui->frameTop->value() / 100.0 * _ser.frameCount());
|
||||
_watcherStack.setFuture(_stack);
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::stackFinish()
|
||||
{
|
||||
cv::Mat stack = _stack.result();
|
||||
cv::Mat scaled;
|
||||
QSize s = ui->image->size();
|
||||
cv::resize(stack, scaled, cv::Size(s.width(), s.height()));
|
||||
scaled.convertTo(scaled, CV_8U, 255.0);
|
||||
QImage img(scaled.data, s.width(), s.height(), scaled.cols, QImage::Format_Grayscale8);
|
||||
ui->image->setPixmap(QPixmap::fromImage(img));
|
||||
}
|
||||
|
||||
+19
-2
@@ -2,12 +2,15 @@
|
||||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QFuture>
|
||||
#include <QFutureWatcher>
|
||||
#include <vector>
|
||||
#include <opencv2/opencv.hpp>
|
||||
#include "serfile.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
class MainWindow;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class MainWindow : public QMainWindow
|
||||
{
|
||||
@@ -17,7 +20,21 @@ public:
|
||||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
public slots:
|
||||
void openFile();
|
||||
void saveImage();
|
||||
void analyze();
|
||||
void stack();
|
||||
|
||||
void stackFinish();
|
||||
private:
|
||||
SERFileReader _ser;
|
||||
Ui::MainWindow *ui;
|
||||
QFuture<void> _analyze;
|
||||
QFuture<cv::Mat> _stack;
|
||||
QFutureWatcher<void> _watcherAnalyze;
|
||||
QFutureWatcher<cv::Mat> _watcherStack;
|
||||
std::vector<std::pair<int, double>> _variance;
|
||||
int cvType = 0;
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
|
||||
+101
-17
@@ -6,39 +6,123 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
<width>1213</width>
|
||||
<height>744</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
<string>Kouryu</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>90</x>
|
||||
<y>110</y>
|
||||
<width>411</width>
|
||||
<height>91</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QSpinBox" name="frameTop">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>5</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="analyzeButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Analyze</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="stackButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Stack</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="progressBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="cancelButton">
|
||||
<property name="text">
|
||||
<string>Cancel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="image">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<width>1213</width>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionSave"/>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionOpen">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave">
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
||||
+16
@@ -76,6 +76,12 @@ bool SERFileReader::open(const QString &path)
|
||||
return true;
|
||||
}
|
||||
|
||||
void SERFileReader::close()
|
||||
{
|
||||
_fr.close();
|
||||
std::memset(&_header, 0, sizeof(SERHeader));
|
||||
}
|
||||
|
||||
uint32_t SERFileReader::width() const
|
||||
{
|
||||
return _header.imagewidth;
|
||||
@@ -96,6 +102,16 @@ uint64_t SERFileReader::frameSize() const
|
||||
return static_cast<uint64_t>(_header.imagewidth) * _header.imageheight * _header.pixeldepth / 8;
|
||||
}
|
||||
|
||||
uint32_t SERFileReader::pixelDepth() const
|
||||
{
|
||||
return _header.pixeldepth;
|
||||
}
|
||||
|
||||
SERPixelFormat SERFileReader::pixelFormat() const
|
||||
{
|
||||
return _header.colorid;
|
||||
}
|
||||
|
||||
void SERFileReader::getFrame(int32_t index, std::vector<uint8_t> &data)
|
||||
{
|
||||
if(index >= static_cast<int32_t>(_header.framecount))return;
|
||||
|
||||
@@ -54,10 +54,13 @@ class SERFileReader
|
||||
{
|
||||
public:
|
||||
bool open(const QString &path);
|
||||
void close();
|
||||
uint32_t width() const;
|
||||
uint32_t height() const;
|
||||
uint32_t frameCount() const;
|
||||
uint64_t frameSize() const;
|
||||
uint32_t pixelDepth() const;
|
||||
SERPixelFormat pixelFormat() const;
|
||||
void getFrame(int32_t index, std::vector<uint8_t> &data);
|
||||
void getFrame(int32_t index, char *data);
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user