diff options
| -rw-r--r-- | CMakeLists.txt | 6 | ||||
| -rw-r--r-- | imagealgos.cpp | 17 | ||||
| -rw-r--r-- | imagealgos.h | 2 | ||||
| -rw-r--r-- | macro.h | 2 | ||||
| -rw-r--r-- | main.cpp | 471 | ||||
| -rw-r--r-- | printerclient.cpp | 3 | ||||
| -rw-r--r-- | printerclient.h | 7 | ||||
| -rw-r--r-- | profile.cpp | 12 | ||||
| -rw-r--r-- | src/calibration.cpp | 28 | ||||
| -rw-r--r-- | src/calibration.h | 2 | ||||
| -rw-r--r-- | src/camera/ov9281.cpp | 385 | ||||
| -rw-r--r-- | src/camera/ov9281.h | 68 | ||||
| -rw-r--r-- | src/image.cpp | 110 | ||||
| -rw-r--r-- | src/image.h | 21 | ||||
| -rw-r--r-- | src/pixels.cpp | 7 | ||||
| -rw-r--r-- | src/pixels.h | 3 | ||||
| -rw-r--r-- | src/typedefs.h | 18 |
17 files changed, 837 insertions, 325 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 43cd7c7..9c1b165 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,9 @@ set(CMAKE_SYSROOT ${TARGET_SYSROOT}) set(CMAKE_LIBRARY_PATH ${CMAKE_SYSROOT}/usr/lib/aarch64-linux-gnu) # arch: -# set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-linux-gnu/pkgconfig) +set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-linux-gnu/pkgconfig) # gentoo: -set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-rpi5-linux-gnu/pkgconfig) +# set(ENV{PKG_CONFIG_PATH} $PKG_CONFIG_PATH:/usr/lib/aarch64-rpi5-linux-gnu/pkgconfig) set(ENV{PKG_CONFIG_LIBDIR} /usr/lib/pkgconfig:/usr/share/pkgconfig/:${TARGET_SYSROOT}/usr/lib/aarch64-linux-gnu/pkgconfig:${TARGET_SYSROOT}/usr/lib/pkgconfig) set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) @@ -98,6 +98,8 @@ qt_add_executable(apporpheus src/dumps.h src/dumps.cpp src/calibration.h src/calibration.cpp profile.h profile.cpp + src/camera/ov9281.h src/camera/ov9281.cpp + src/image.h src/image.cpp ) target_link_libraries(app${PROJECT_NAME} PRIVATE diff --git a/imagealgos.cpp b/imagealgos.cpp index 98d5fb0..6bd4d1e 100644 --- a/imagealgos.cpp +++ b/imagealgos.cpp @@ -94,23 +94,6 @@ void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest) stop_timer(unpack_16bit); } -void rotate(Image &image) -{ - start_timer(rotate); - - using namespace std; - - for (size_t i = 0; i < img_height; ++i) - { - for (size_t j = 0; j < img_width; ++j) - { - image.rotated_cw[j][i] = image.data[img_height - i][j]; - } - } - - stop_timer(rotate); -} - template<class T, size_t N> constexpr size_t mysize(T (&)[N]) { return N; } diff --git a/imagealgos.h b/imagealgos.h index 60844b8..4094f87 100644 --- a/imagealgos.h +++ b/imagealgos.h @@ -3,6 +3,7 @@ #include <QLineF> #include <QList> +#include "image.h" #include "pixels.h" size_t pgm_save(Image *img, FILE *outfile, bool really_save = false); @@ -10,7 +11,6 @@ size_t pgm_save(Image *img, FILE *outfile, bool really_save = false); void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest); void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest); -void rotate(Image & image); Pixels process_columns(Image & image); QList<QLineF> pixelsToLines(const Pixels& rawProfile); @@ -15,3 +15,5 @@ // << std::chrono::duration_cast<std::chrono::microseconds>( \ // end_ ## name - begin_ ## name) \ // << std::endl; + +#define INIT_FIELD(name) m_##name(name) @@ -9,6 +9,7 @@ #include "LibCamera.h" #include "calibration.h" +#include "camera/ov9281.h" #include "dumps.h" #include "fuck_intel.h" #include "genetic_algos.h" @@ -59,10 +60,10 @@ QMutex calibrationPixelsMutex; using namespace std::chrono_literals; -static std::shared_ptr<libcamera::Camera> camera; -std::unique_ptr<libcamera::CameraConfiguration> config; -static std::map<int, std::pair<void*, unsigned int>> mappedBuffers_; -std::vector<std::unique_ptr<libcamera::Request>> requests; +// static std::shared_ptr<libcamera::Camera> camera; +// std::unique_ptr<libcamera::CameraConfiguration> config; +// static std::map<int, std::pair<void*, unsigned int>> mappedBuffers_; +// std::vector<std::unique_ptr<libcamera::Request>> requests; libcamera::ControlList lastControls; namespace @@ -71,12 +72,12 @@ CalibrationTablePtr calibrationTableZ; CalibrationTablePtr calibrationTableX; } // namespace -static bool applyConfig( - const std::unique_ptr<libcamera::CameraConfiguration>& config -); -static void onRequestCompleted(libcamera::Request* completed_request); -static void printControls(); -static QList<Pixels> filter(const QList<Pixels>& rawProfiles); +// static bool applyConfig( +// const std::unique_ptr<libcamera::CameraConfiguration>& config +// ); +// static void onRequestCompleted(libcamera::Request* completed_request); +// static void printControls(); +// static QList<Pixels> filter(const QList<Pixels>& rawProfiles); auto printPixels = [](const auto& pixels) { for (size_t i = (img_width - 10) / 2; @@ -88,6 +89,21 @@ auto printPixels = [](const auto& pixels) { std::cout << std::endl; }; +void onNewPixels(std::shared_ptr<Pixels> pixels) +{ + if (!*pixels) + { + qDebug() << "got empty pixels"; + } + + ::pixels = *pixels; + + if (!::pixels) + { + qDebug() << "empty pixels after copy"; + } +}; + bool initLaser(); int main(int argc, char* argv[]) @@ -98,7 +114,8 @@ int main(int argc, char* argv[]) qDebug() << "size of raw profile" << sizeof(Pixels); if (true) { - if (false) { + if (true) + { // z // if (!openCalibrationTable( // "/home/user/dumps/binz.calibration_table", @@ -166,14 +183,15 @@ int main(int argc, char* argv[]) std::cout << std::endl; } - exit(EXIT_SUCCESS); + // exit(EXIT_SUCCESS); interpolate(::calibrationTableX); // calibrationTableToImage(::calibrationTableX) // .save("/home/user/dumps/imageX_interpolated.png"); } - if (true) { + if (false) + { auto rawProfiles = openDump("/home/user/dumps/binz"); // auto rawProfiles = openDump("/home/user/dumps/z"); qDebug() << "raw z-profiles count is" << rawProfiles.size(); @@ -211,7 +229,8 @@ int main(int argc, char* argv[]) qDebug() << "--------------------------------------------------------"; - if (true) { + if (false) + { auto rawProfiles = openDump("/home/user/dumps/binx"); qDebug() << "raw x-profiles count is" << rawProfiles.size(); // qDebug() << "height" << calibrationColumnHeight; @@ -269,27 +288,40 @@ int main(int argc, char* argv[]) std::make_unique<libcamera::CameraManager>(); cm->start(); - const auto cameras = cm->cameras(); - - // openDump(); + // const auto cameras = cm->cameras(); + const auto cameras = OV9281::search(cm); if (cameras.empty()) { - std::cout << "No cameras were identified on the system." << std::endl; + std::cerr << "No cameras were identified on the system." << std::endl; cm->stop(); return EXIT_FAILURE; } + auto camera = cameras.at(0); + + camera->printControls(); + + camera->newPixels.connect(&onNewPixels); + + if (!camera->startStream()) + { + cm->stop(); + + return EXIT_FAILURE; + } + + /* std::string cameraId = cameras[0]->id(); std::cout << "using " << cameraId << std::endl; - /* - * Note that `camera` may not compare equal to `cameras[0]`. - * In fact, it might simply be a `nullptr`, as the particular - * device might have disappeared (and reappeared) in the meantime. - */ + // + // Note that `camera` may not compare equal to `cameras[0]`. + // In fact, it might simply be a `nullptr`, as the particular + // device might have disappeared (and reappeared) in the meantime. + // // std::shared_ptr<Camera> camera = cm->get(cameraId); camera = cm->get(cameraId); @@ -304,8 +336,7 @@ int main(int argc, char* argv[]) // FIXME: nullptr // std::unique_ptr<CameraConfiguration> config = // camera->generateConfiguration( { StreamRole::Viewfinder } ); - /*std::unique_ptr<CameraConfiguration> */ config = - camera->generateConfiguration({libcamera::StreamRole::Raw}); + config = camera->generateConfiguration({libcamera::StreamRole::Raw}); if (config->empty()) { @@ -481,8 +512,7 @@ int main(int argc, char* argv[]) { camera->queueRequest(request.get()); } - - printControls(); +*/ // std::this_thread::sleep_for(2s); // TODO: move to thread @@ -490,11 +520,13 @@ int main(int argc, char* argv[]) QHttpServer qHttpServer; qHttpServer.route("/v1/sensor/image", [&]() { + std::cout << "http: image" << std::endl; std::lock_guard<std::mutex> lg(pgm_image_mtx); // qDebug() << "image"; return QByteArray((const char*)pgm_image, pgm_image_size); }); qHttpServer.route("/v1/sensor/image2", [&]() { + std::cout << "http: image2" << std::endl; std::lock_guard<std::mutex> lg(pgm_image_mtx); // qDebug() << "image"; return QByteArray((const char*)pgm_image, pgm_image_size); @@ -504,6 +536,7 @@ int main(int argc, char* argv[]) // return "123"; // }); qHttpServer.route("/v1/pixels", [&]() { + std::cout << "http: pixels" << std::endl; std::lock_guard<std::mutex> lg(pgm_image_mtx); QJsonArray pixels; @@ -540,6 +573,7 @@ int main(int argc, char* argv[]) }); qHttpServer.route("/v1/profile", [&]() { + std::cout << "http: profile" << std::endl; std::lock_guard<std::mutex> lg(pgm_image_mtx); const Profile profile(::pixels, @@ -554,7 +588,9 @@ int main(int argc, char* argv[]) qHttpServer .route("/v1/commands/resetEncoder", [&](const QHttpServerRequest& request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) { + std::cout << "http: resetEncoder" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { return QHttpServerResponse::StatusCode::NotFound; } @@ -565,91 +601,92 @@ int main(int argc, char* argv[]) return QHttpServerResponse::StatusCode::Ok; }); - qHttpServer.route( - "/v1/commands/startCalibration", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } + qHttpServer + .route("/v1/commands/startCalibration", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: startCalibration" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - qDebug() << "start calibration"; + qDebug() << "start calibration"; - // TODO: use flags - scanningModeFlags = ScanningModeFlags::Calibration; - calibrationTimer.start(); + // TODO: use flags + scanningModeFlags = ScanningModeFlags::Calibration; + calibrationTimer.start(); - return QHttpServerResponse::StatusCode::Ok; - } - ); + return QHttpServerResponse::StatusCode::Ok; + }); - qHttpServer.route( - "/v1/commands/gCode", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } + qHttpServer + .route("/v1/commands/gCode", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: gCode" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } - const auto command = request.body(); + const auto command = request.body(); - qDebug() << "send gCode:" << command; + qDebug() << "send gCode:" << command; - // printerClient.sendCommand(command); + // printerClient.sendCommand(command); - return QHttpServerResponse::StatusCode::Ok; - } - ); + return QHttpServerResponse::StatusCode::Ok; + }); - qHttpServer.route( - "/v1/commands/startCalibration", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } + // qHttpServer + // .route("/v1/commands/startCalibration", + // [&](const QHttpServerRequest& request) -> QHttpServerResponse { + // std::cout << "http: startCalibration" << std::endl; + // if (request.method() != QHttpServerRequest::Method::Post) + // { + // return QHttpServerResponse::StatusCode::NotFound; + // } - const auto command = request.body(); + // const auto command = request.body(); - qDebug() << "send gCode:" << command; + // qDebug() << "send gCode:" << command; - // printerClient.sendCommand(command); + // // printerClient.sendCommand(command); - return QHttpServerResponse::StatusCode::Ok; - } - ); + // return QHttpServerResponse::StatusCode::Ok; + // }); qHttpServer.route( "/v1/sensor/params", [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: params" << std::endl; switch (request.method()) { case QHttpServerRequest::Method::Get: { std::lock_guard<std::mutex> lg(pgm_image_mtx); QJsonObject json; - const libcamera::ControlIdMap& ctrlIdMap = - camera->controls().idmap(); + // const libcamera::ControlIdMap& ctrlIdMap = + // camera->controls().idmap(); - qDebug() << "readParams:" << lastControls.size(); - qDebug() << request.method(); + // qDebug() << "readParams:" << lastControls.size(); + // qDebug() << request.method(); - for (const auto& [id, value] : lastControls) - { - const libcamera::ControlId* controlId = ctrlIdMap.at(id); - auto name = QString::fromStdString(controlId->name()); - const auto valueStr = - QString::fromStdString(value.toString()); - qDebug() - << "\t param:" << controlId->id() << name << valueStr; + // for (const auto& [id, value] : lastControls) + // { + // const libcamera::ControlId* controlId = ctrlIdMap.at(id); + // auto name = QString::fromStdString(controlId->name()); + // const auto valueStr = + // QString::fromStdString(value.toString()); + // qDebug() + // << "\t param:" << controlId->id() << name << valueStr; - name[0] = name[0].toLower(); - json[name] = valueStr; - } + // name[0] = name[0].toLower(); + // json[name] = valueStr; + // } - json[laserLevelKey] = requested_params.laserLevel; + // json[laserLevelKey] = requested_params.laserLevel; - qDebug() << "response body:" << json; + // qDebug() << "response body:" << json; // QHttpServerResponse return QHttpServerResponse(QJsonDocument(json).toJson()); @@ -715,8 +752,7 @@ int main(int argc, char* argv[]) ); } } - } - ); + }); qDebug() << "listen: " << qHttpServer.listen(QHostAddress::Any, 8081); @@ -745,97 +781,24 @@ int main(int argc, char* argv[]) future.cancel(); future.waitForFinished(); - for (auto& [fd, mem] : mappedBuffers_) - { - munmap(mem.first, mem.second); - } + // for (auto& [fd, mem] : mappedBuffers_) + // { + // munmap(mem.first, mem.second); + // } // FIXME: crash somewhere here. proper libcamera finishing needed - requests.clear(); - mappedBuffers_.clear(); + // requests.clear(); + // mappedBuffers_.clear(); - camera->stop(); - config.reset(); - allocator->free(stream); - allocator.reset(); - camera->release(); - camera.reset(); + // camera->stop(); + // config.reset(); + // allocator->free(stream); + // allocator.reset(); + // camera->release(); + // camera.reset(); cm->stop(); return result; - - // time_t start_time = time(0); - // int frame_count = 0; - - // LibCamera cam; - // uint32_t width = 1280; - // uint32_t height = 800; - // uint32_t stride; - // char key; - - // ret = cam.initCamera(); - - // if (ret != EXIT_SUCCESS) - // { - // std::cerr << "cannot open camera" << std::endl; - - // return EXIT_FAILURE; - // } - - // cam.configureStill(width, height, formats::R8, 1, 0); - // // ControlList controls_; - // int64_t frame_time = 1000000 / 10; - // // Set frame rate - // // controls_.set( controls::FrameDurationLimits, libcamera::Span<const - // int64_t, 2>( - // // { frame_time, frame_time } )); - // // Adjust the brightness of the output images, in the range -1.0 to 1.0 - // // controls_.set(controls::Brightness, 0.5); - // // Adjust the contrast of the output image, where 1.0 = normal contrast - // // controls_.set(controls::Contrast, 1.5); - // // Set the exposure time - // // controls_.set(controls::ExposureTime, 20000); - // // cam.set(controls_); - - // std::cout << std::flush; - - // // NOTE: already checked - // if (ret == EXIT_SUCCESS) { - // bool flag; - // LibcameraOutData frameData; - // cam.startCamera(); - // cam.VideoStream(&width, &height, &stride); - - // while (true) { - // flag = cam.readFrame(&frameData); - // if (!flag) - // continue; - - // // key = waitKey(1); - // // if (key == 'q') { - // // break; - // // } else if (key == 'f') { - // // ControlList controls; - // // controls.set(controls::AfMode, controls::AfModeAuto); - // // controls.set(controls::AfTrigger, 0); - // // cam.set(controls); - // // } - - // frame_count++; - // if ((time(0) - start_time) >= 1){ - // printf("fps: %d\n", frame_count); - // frame_count = 0; - // start_time = time(0); - // } - // cam.returnFrameBuffer(frameData); - // } - - // cam.stopCamera(); - // } - - // cam.closeCamera(); - - // return EXIT_SUCCESS; } /* @@ -843,6 +806,7 @@ int main(int argc, char* argv[]) * important not to block the thread for a long time, as this blocks internal * processing of the camera pipelines, and can affect realtime performance. */ +/* void onRequestCompleted(libcamera::Request* completed_request) { using namespace libcamera; @@ -879,13 +843,6 @@ void onRequestCompleted(libcamera::Request* completed_request) const FrameMetadata& metadata = buffer->metadata(); - // if (verbose) - // { - // std::cout << " seq: " << std::setw(6) << std::setfill('0') - // << metadata.sequence - // << " bytesused: "; - // } - for (size_t i = 0; i < buffer->planes().size(); ++i) { const FrameBuffer::Plane& plane = buffer->planes()[i]; @@ -913,7 +870,7 @@ void onRequestCompleted(libcamera::Request* completed_request) // unpack_16bit((uint8_t*)img.data, img, (uint16_t*)&unpacked); // img.data = unpacked; // img.dataSize = img.width * img.height * sizeof(uint16_t); - rotate(img); + img.rotate(); Pixels pixels = process_columns(img); ::pixels = pixels; @@ -1040,120 +997,72 @@ void onRequestCompleted(libcamera::Request* completed_request) // qDebug() << "-------------------------------------------"; } +*/ -static bool applyConfig( - const std::unique_ptr<libcamera::CameraConfiguration>& config -) -{ - using namespace libcamera; - - auto status = config->validate(); - - // WARNING: unsafe - libcamera::StreamConfiguration& streamConfig = config->at(0); - - switch (status) - { - case CameraConfiguration::Status::Valid: - std::cout << "config is valid" << std::endl; - break; - case CameraConfiguration::Status::Adjusted: - std::cout << "\tpixelFormat: " << streamConfig.pixelFormat.toString() - << std::endl; - std::cout << "\tbufferCount: " << streamConfig.bufferCount << std::endl; - std::cout << "\torientation: " << config->orientation << std::endl; - break; - case CameraConfiguration::Status::Invalid: - std::cout << "config is invalid, quit." << std::endl; - - return false; - } - - return true; -} - -static void printControls() -{ - using namespace libcamera; - const libcamera::ControlInfoMap& control_map = camera->controls(); +// static bool applyConfig( +// const std::unique_ptr<libcamera::CameraConfiguration>& config +// ) +// { +// using namespace libcamera; - // for (const auto & [id, info]: control_map) - for (const std::pair<const ControlId*, ControlInfo>& pair : control_map) - { - const ControlId* const& id = pair.first; - const ControlInfo& info = pair.second; +// auto status = config->validate(); - std::cout << "\tc " << id->name() << " (" << id->id() - << "): " << info.toString() - << (info.def().isNone() - ? "" - : " (dflt:" + info.def().toString() + ")"); +// // WARNING: unsafe +// libcamera::StreamConfiguration& streamConfig = config->at(0); - if (!info.values().size()) - { - std::cout << std::endl; - continue; - } +// switch (status) +// { +// case CameraConfiguration::Status::Valid: +// std::cout << "config is valid" << std::endl; +// break; +// case CameraConfiguration::Status::Adjusted: +// std::cout << "\tpixelFormat: " << streamConfig.pixelFormat.toString() +// << std::endl; +// std::cout << "\tbufferCount: " << streamConfig.bufferCount << std::endl; +// std::cout << "\torientation: " << config->orientation << std::endl; +// break; +// case CameraConfiguration::Status::Invalid: +// std::cout << "config is invalid, quit." << std::endl; - std::cout << " - ["; +// return false; +// } - for (const auto& v : info.values()) - { - std::cout << " " << v.toString(); - } +// return true; +// } - std::cout << " ]\n"; - } -} - -static QList<Pixels> filter(const QList<Pixels>& rawProfiles) -{ - QList<Pixels> result; +// static void printControls() +// { +// using namespace libcamera; +// const libcamera::ControlInfoMap& control_map = camera->controls(); - QList<Pixels>::const_iterator it = rawProfiles.constBegin(); +// // for (const auto & [id, info]: control_map) +// for (const std::pair<const ControlId*, ControlInfo>& pair : control_map) +// { +// const ControlId* const& id = pair.first; +// const ControlInfo& info = pair.second; - // for (size_t i = 0; i < 10; ++i) - // { - // std::cout << "pos - " << rawProfiles.at(i).counters.encoderPosition - // << std::endl; - // } +// std::cout << "\tc " << id->name() << " (" << id->id() +// << "): " << info.toString() +// << (info.def().isNone() +// ? "" +// : " (dflt:" + info.def().toString() + ")"); - // ++it; - while (it != rawProfiles.constEnd()) - { - Pixels sum = *it; - // std::cout << "current pos is " << sum.counters.encoderPosition - // << std::endl; +// if (!info.values().size()) +// { +// std::cout << std::endl; +// continue; +// } - // printPixels(sum.pixels); +// std::cout << " - ["; - size_t count{1}; - ++it; +// for (const auto& v : info.values()) +// { +// std::cout << " " << v.toString(); +// } - // std::cout << "here\n"; - while (it != rawProfiles.constEnd() && - it->counters.encoderPosition == sum.counters.encoderPosition) - { - // std::cout << "here2\n"; - // std::cout << "\tadd to pos " << it->counters.encoderPosition - // << std::endl; - sum += *it; - // std::cout << "\tadded" << std::endl; - ++count; - ++it; - } - - // std::cout << "here3\n"; - sum /= float(count); - // printPixels(sum.pixels); - - result << sum; - - // return result; - } - - return result; -} +// std::cout << " ]\n"; +// } +// } bool initLaser() { diff --git a/printerclient.cpp b/printerclient.cpp index ac269b3..4fed38f 100644 --- a/printerclient.cpp +++ b/printerclient.cpp @@ -19,8 +19,7 @@ QString getFirstTtyUSB() } PrinterClient::PrinterClient(QObject* parent) - : QObject{parent} - // , m_serialPort{new QSerialPort{"/dev/ttyUSB0", this}} + : QObject{parent} // , m_serialPort{new QSerialPort{"/dev/ttyUSB0", this}} , m_serialPort{new QSerialPort{getFirstTtyUSB(), this}} { if (!m_serialPort->setBaudRate(QSerialPort::Baud115200)) { diff --git a/printerclient.h b/printerclient.h index 08bf0c2..d1266dd 100644 --- a/printerclient.h +++ b/printerclient.h @@ -6,8 +6,7 @@ class QSerialPort; -class PrinterClient - : public QObject +class PrinterClient : public QObject { // Q_OBJECT @@ -16,8 +15,8 @@ public: // ~PrinterClient() override = default; // ~PrinterClient -// signals: -// void newData(const QString output); + // signals: + // void newData(const QString output); public: void sendCommand(const QString command); diff --git a/profile.cpp b/profile.cpp index 31d88cd..0c4967c 100644 --- a/profile.cpp +++ b/profile.cpp @@ -11,6 +11,14 @@ Profile::Profile( const CalibrationTablePtr calibrationTableX) : m_counters(pixels.counters) { + if (!calibrationTableZ || !calibrationTableX) + { + std::cerr << __func__ << ": got invalid calibration tables" + << std::endl; + + return; + } + static bool done{false}; if (!done) { @@ -71,8 +79,8 @@ Profile::Profile( // TODO: use only NaN (or zero?) everywhere // NOTE: QJsonValue converts NaN to zero if (qFuzzyIsNull(z) || std::isnan(z)) { - qDebug() << "got nan z for discrete" << pixelDiscrete << leftMmZ - << rightMmZ; + // qDebug() << "got nan z for discrete" << pixelDiscrete << leftMmZ + // << rightMmZ; m_pointsMm.at(i) = {std::nan(""), std::nan("")}; continue; } diff --git a/src/calibration.cpp b/src/calibration.cpp index 73bd786..ff37e73 100644 --- a/src/calibration.cpp +++ b/src/calibration.cpp @@ -191,6 +191,34 @@ QImage calibrationTableToImage(const CalibrationTablePtr& calibrationTable) return result; } +QList<Pixels> filter(const QList<Pixels>& rawProfiles) +{ + QList<Pixels> result; + + QList<Pixels>::const_iterator it = rawProfiles.constBegin(); + + while (it != rawProfiles.constEnd()) + { + Pixels sum = *it; + + size_t count{1}; + ++it; + + while (it != rawProfiles.constEnd() && + it->counters.encoderPosition == sum.counters.encoderPosition) + { + sum += *it; + ++count; + ++it; + } + + sum /= float(count); + result << sum; + } + + return result; +} + CalibrationTablePtr calibrateZ(const QList<Pixels>& rawProfiles, const uint32_t& stepsPerMm) { diff --git a/src/calibration.h b/src/calibration.h index ddb66a6..c2e5d73 100644 --- a/src/calibration.h +++ b/src/calibration.h @@ -21,6 +21,8 @@ bool openCalibrationTable(const QString &filename, CalibrationTablePtr &table); void dumpCalibrationPixels(std::vector<Pixels> &&calibrationPixels); bool dump(const CalibrationTablePtr &table, const QString &filename); +QList<Pixels> filter(const QList<Pixels> &rawProfiles); + CalibrationTablePtr calibrateX(const QList<Pixels> &rawProfiles); CalibrationTablePtr calibrateZ(const QList<Pixels> &rawProfiles, const uint32_t &stepsPerMm); diff --git a/src/camera/ov9281.cpp b/src/camera/ov9281.cpp new file mode 100644 index 0000000..bdb9f89 --- /dev/null +++ b/src/camera/ov9281.cpp @@ -0,0 +1,385 @@ +#include "ov9281.h" + +#include <iostream> +#include <thread> + +#include <string.h> +#include <sys/mman.h> + +#include <libcamera/camera.h> +#include <libcamera/camera_manager.h> +#include <libcamera/control_ids.h> +#include <libcamera/framebuffer_allocator.h> +#include <libcamera/request.h> + +#include "image.h" +#include "macro.h" +#include "pixels.h" +#include "rotaryencoder.h" +#include "typedefs.h" + +OV9281::OV9281(const std::shared_ptr<libcamera::Camera> &camera) + : INIT_FIELD(camera) +{ + std::cout << __func__ << ":\tid: " << m_camera->id(); +} + +OV9281::~OV9281() +{ + for (auto &[fd, mem] : m_mappedBuffers) + { + munmap(mem.first, mem.second); + } + + m_camera->release(); +} + +bool OV9281::init() +{ + if (m_camera->acquire() != EXIT_SUCCESS) + { + std::cerr << __func__ << ": " << m_camera->id() + << ": cannot acquire camera." << std::endl; + + return false; + } + + m_config = m_camera->generateConfiguration({libcamera::StreamRole::Raw}); + + if (m_config->empty()) + { + std::cerr << __func__ << ": " << m_camera->id() + << ": cannot generate configuration" << std::endl; + + return false; + } + + m_config->orientation = libcamera::Orientation::Rotate90; + + if (m_config->empty()) + { + std::cerr << __func__ << ": " << m_camera->id() << ": config is empty" + << std::endl; + + return false; + } + + libcamera::StreamConfiguration &streamConfig = m_config->at(0); + + streamConfig.pixelFormat = OV9281::pixelFormat; + streamConfig.bufferCount = OV9281::bufferCount; + + if (!validateConfig()) + { + std::cerr << __func__ << ": " << m_camera->id() + << ": cannot apply default config" << std::endl; + + return false; + } + + return true; +} + +bool OV9281::validateConfig() +{ + using namespace libcamera; + + auto status = m_config->validate(); + + // WARNING: unsafe + + switch (status) + { + case CameraConfiguration::Status::Valid: { + std::cout << __func__ << ": " << "config is valid" << std::endl; + break; + } + case CameraConfiguration::Status::Adjusted: { + if (m_config->empty()) + { + std::cerr << __func__ << ": " << "config is adjusted, but empty" + << std::endl; + return false; + } + + libcamera::StreamConfiguration &streamConfig = m_config->at(0); + std::cout << __func__ + << ":\tpixelFormat: " << streamConfig.pixelFormat.toString() + << std::endl; + std::cout << __func__ << ":\tbufferCount: " << streamConfig.bufferCount + << std::endl; + std::cout << __func__ << ":\torientation: " << m_config->orientation + << std::endl; + break; + } + case CameraConfiguration::Status::Invalid: { + std::cerr << __func__ << ":\tconfig is invalid" << std::endl; + + return false; + } + } + + return true; +} + +bool OV9281::applyConfig() +{ + // FIXME: may crassh even on success (e.g. by setting pixelFormat to "8") + if (m_camera->configure(m_config.get()) != EXIT_SUCCESS) + { + std::cerr << __func__ << ":\tcannot apply config" << std::endl; + + return false; + } + + return true; +} + +void OV9281::onRequestCompleted(libcamera::Request *completed_request) +{ + using namespace libcamera; + + if (completed_request->status() == Request::RequestCancelled) + { + std::cerr << __func__ << ":\trequest canceled" << std::endl; + + return; + } + + const auto &buffers = completed_request->buffers(); + + for (auto [stream, buffer] : buffers) + { + const auto &streamConfig = stream->configuration(); + const auto &imageSize = streamConfig.size; + const auto &pixelFormat = streamConfig.pixelFormat; + const auto &stride = streamConfig.stride; + + const FrameMetadata &metadata = buffer->metadata(); + + for (size_t i = 0; i < buffer->planes().size(); ++i) + { + const FrameBuffer::Plane &plane = buffer->planes()[i]; + const FrameMetadata::Plane &metaplane = buffer->metadata() + .planes()[i]; + + size_t size = std::min(metaplane.bytesused, plane.length); + void *data = m_mappedBuffers[plane.fd.get()].first; + + auto img = std::make_shared<Image>(); + + img->width = imageSize.width; + img->height = imageSize.height; + + memcpy(img->data, data, size); + img->dataSize = size; + img->stride = stride; + img->pixelFormat = pixelFormat; + img->counters.measurementCounter = metadata.sequence; + img->counters.timestampUs = metadata.timestamp / 1000; + img->counters.encoderPosition = RotaryEncoder::instance()->position(); + + img->rotate(); + + auto pixels = img->pixels(); +#ifdef emit +#undef emit + newPixels.emit(pixels); +#define emit +#endif + } + } + + const libcamera::ControlList &metadata = completed_request->metadata(); + const ControlInfoMap &control_map = m_camera->controls(); + // const ControlIdMap & ctrlIdMap = control_map.idmap(); + + auto frameDurationCtrl = control_map.find(&controls::FrameDurationLimits); + double fps = frameDurationCtrl == control_map.end() ? + std::numeric_limits<double>::quiet_NaN() : + (1e6 / frameDurationCtrl->second.min().get<int64_t>()); + + auto exp = metadata.get(controls::ExposureTime); + auto ag = metadata.get(controls::AnalogueGain); + auto ae = metadata.get(controls::AeEnable); + // auto br= metadata.get(controls::Brightness); + static auto lastControls = completed_request->controls(); + + completed_request->reuse(Request::ReuseBuffers); + completed_request->controls().set(libcamera::controls::AeEnable, false); + completed_request->controls() + .set(libcamera::controls::draft ::NoiseReductionMode, + libcamera::controls::draft ::NoiseReductionModeEnum :: + NoiseReductionModeHighQuality); + + completed_request->controls().set(libcamera::controls::ExposureTime, + m_exposureTime); + + m_camera->queueRequest(completed_request); +} + +std::vector<std::shared_ptr<OV9281>> OV9281::search( + std::unique_ptr<libcamera::CameraManager> &manager) +{ + std::vector<std::shared_ptr<OV9281>> result; + + for (const auto &camera : manager->cameras()) + { + auto ov9281 = std::shared_ptr<OV9281>(new OV9281(camera)); + + if (!ov9281->init()) + { + continue; + } + + result.push_back(ov9281); + } + + return result; +} + +bool OV9281::startStream() +{ + if (m_config->empty()) + { + std::cerr << __func__ << ":\tconfig is empty" << std::endl; + return false; + } + + auto &streamConfig = m_config->at(0); + m_allocator = std::make_unique<libcamera::FrameBufferAllocator>(m_camera); + auto stream = streamConfig.stream(); + auto ret = m_allocator->allocate(stream); + + // TODO: check if zero + if (ret < 0) + { + std::cerr << __func__ << ":\tcan't allocate buffers: " << strerror(ret) + << std::endl; + return false; + } + + auto allocatedCount = ret; + std::cout << __func__ << ":\tallocated " << allocatedCount + << " buffers for stream" << std::endl; + + const auto &buffers = m_allocator->buffers(stream); + + for (const auto &buffer : buffers) + { + auto request = m_camera->createRequest(); + + if (!request) + { + std::cerr << __func__ << ":\tcan't create request" << std::endl; + + return false; + } + + // TODO: try multiple buffers per request and compare performance + ret = request->addBuffer(stream, buffer.get()); + + if (ret < 0) + { + std::cerr << __func__ + << ":\tcan't set buffer for request: " << strerror(ret) + << std::endl; + + return false; + } + + for (const auto &plane : buffer->planes()) + { + void *memory = mmap(NULL, + plane.length, + PROT_READ, + MAP_SHARED, + plane.fd.get(), + 0); + m_mappedBuffers[plane.fd.get()] = std::make_pair(memory, + plane.length); + } + + std::int64_t lowerUS = 1 * 1000 * 1000 / desiredFPS; + std::int64_t higherUS = lowerUS; + std::int64_t value_pair[2] = {higherUS / 2, higherUS}; + request->controls().set(libcamera::controls::AnalogueGain, 1.0); + request->controls().set(libcamera::controls::ExposureTime, 100); + request->controls().set(libcamera::controls::FrameDurationLimits, + libcamera::Span<const std::int64_t, 2>( + value_pair)); + + m_requests.push_back(std::move(request)); + } + + m_camera->requestCompleted.connect(this, &OV9281::onRequestCompleted); + + // FIXME: memleak + std::unique_ptr<libcamera::ControlList> camcontrols{ + new libcamera::ControlList()}; + + { + using namespace std::chrono_literals; + std::this_thread::sleep_for(500ms); + } + + ret = m_camera->start(camcontrols.get()); + + if (ret) + { + std::cerr << __func__ << ":\tfailed to start camera: " << strerror(ret) + << std::endl; + + return false; + } + + for (auto &request : m_requests) + { + ret = m_camera->queueRequest(request.get()); + + if (ret) + { + std::cerr << __func__ + << ":\tfailed to queue request: " << strerror(ret) + << std::endl; + + return false; + } + } + + return true; +} + +void OV9281::printControls() +{ + using namespace libcamera; + const libcamera::ControlInfoMap &control_map = m_camera->controls(); + + // for (const auto & [id, info]: control_map) + for (const std::pair<const ControlId *, ControlInfo> &pair : control_map) + { + const ControlId *const &id = pair.first; + const ControlInfo &info = pair.second; + + std::cout << "\tc " << id->name() << " (" << id->id() + << "): " << info.toString() + << (info.def().isNone() ? + "" : + " (dflt:" + info.def().toString() + ")"); + + if (!info.values().size()) + { + std::cout << std::endl; + continue; + } + + std::cout << " - ["; + + for (const auto &v : info.values()) + { + std::cout << " " << v.toString(); + } + + std::cout << " ]\n"; + } +} diff --git a/src/camera/ov9281.h b/src/camera/ov9281.h new file mode 100644 index 0000000..f70db2f --- /dev/null +++ b/src/camera/ov9281.h @@ -0,0 +1,68 @@ +#pragma once + +#include <map> +#include <memory> +#include <vector> + +#include <libcamera/base/signal.h> +#include <libcamera/formats.h> + +namespace libcamera { +class Camera; +class CameraConfiguration; +class CameraManager; +class FrameBufferAllocator; +class Request; +} // namespace libcamera + +class Image; +class Pixels; + +class OV9281 +{ +public: + ~OV9281(); + +public: + static std::vector<std::shared_ptr<OV9281>> search( + std::unique_ptr<libcamera::CameraManager> &manager); + + // public functions +public: + bool startStream(); + void printControls(); + + // signals +public: + // TODO: image->pixels in separate thread + // TODO: respect sender/receiver threads + libcamera::Signal<std::shared_ptr<Pixels>> newPixels; + +private: + explicit OV9281(const std::shared_ptr<libcamera::Camera> &camera); + + // private functions +private: + bool init(); + bool validateConfig(); + bool applyConfig(); + + void onRequestCompleted(libcamera::Request *completed_request); + + // constants +private: + static inline constexpr auto pixelFormat{libcamera::formats::R16}; + static inline constexpr unsigned int bufferCount{2}; + static inline constexpr size_t desiredFPS{144}; + + // member variables +private: + std::shared_ptr<libcamera::Camera> m_camera{nullptr}; + std::unique_ptr<libcamera::CameraConfiguration> m_config{nullptr}; + std::map<int, std::pair<void *, unsigned int>> m_mappedBuffers; + std::vector<std::unique_ptr<libcamera::Request>> m_requests; + std::unique_ptr<libcamera::FrameBufferAllocator> m_allocator{nullptr}; + + // TODO: set exposureTime from outside + int32_t m_exposureTime{1000}; +}; diff --git a/src/image.cpp b/src/image.cpp new file mode 100644 index 0000000..a9280a4 --- /dev/null +++ b/src/image.cpp @@ -0,0 +1,110 @@ +#include "image.h" + +#include "macro.h" +#include "pixels.h" + +float process_column(const uint16_t (&column)[]) +{ + start_timer(process_column); + + float result = std::numeric_limits<float>::quiet_NaN(); + + constexpr uint32_t signalThreshold = 900; // = SKO * sqrt(patternSize) + static constexpr uint32_t patternOffset = patternSize - + ((patternSize % 2 == 1) ? 1 : 0); + const uint32_t correlationSize = img_height - patternSize + + ((patternSize % 2 == 1) ? 1 : 0); + uint32_t correlation[img_height]; + uint32_t integralSum[img_height]; + uint32_t maxSum = signalThreshold * 50; + uint32_t x1 = 0; + int32_t y1 = 0; + int32_t y2 = 0; + + memset(correlation, 0, img_height * sizeof(correlation[0])); + integralSum[0] = 0; + + for (uint32_t i = 1; i < img_height; ++i) + { + // if (column[i] < 100) + // { + // column[i] = 0; + // } + + integralSum[i] = column[i] / 256 + integralSum[i - 1]; + } + + for (uint32_t i = 0; i < correlationSize; ++i) + correlation[i + patternSize / 2] = column[i + patternSize / 2] / 256 * + (integralSum[i + patternOffset] - + integralSum[i]); + + for (uint32_t i = 3; i < img_height - 2; ++i) + { + const auto sum = correlation[i - 1] + correlation[i] + + correlation[i + 1]; + + if (sum > maxSum) + { + const int32_t rioux0 = int32_t(correlation[i - 2 - 1] + + correlation[i - 1 - 1]) - + int32_t(correlation[i + 1 - 1] + + correlation[i + 2 - 1]); + + if (rioux0 < 0) + { + const int32_t rioux1 = int32_t(correlation[i - 2] + + correlation[i - 1]) - + int32_t(correlation[i + 1] + + correlation[i + 2]); + + if (rioux1 >= 0) + { + x1 = i - 1; + y1 = rioux0; + y2 = rioux1; + maxSum = sum; + } + } + } + } + + result = (y2 != y1) ? (float(x1) - (float(y1) / (y2 - y1))) + : std::numeric_limits<float>::quiet_NaN(); + + return result; +} + +void Image::rotate() +{ + start_timer(rotate); + + using namespace std; + + for (size_t i = 0; i < img_height; ++i) + { + for (size_t j = 0; j < img_width; ++j) + { + rotated_cw[j][i] = data[img_height - i][j]; + } + } + + stop_timer(rotate); +} + +std::shared_ptr<Pixels> Image::pixels() const +{ + auto result = std::make_shared<Pixels>(); + result->counters = counters; + + start_timer(process_columns); + + for (size_t i = 0; i < width; i++) + { + result->pixels[i] = process_column(rotated_cw[i]); + } + + stop_timer(process_columns); + + return result; +} diff --git a/src/image.h b/src/image.h new file mode 100644 index 0000000..2fff020 --- /dev/null +++ b/src/image.h @@ -0,0 +1,21 @@ +#pragma once + +#include "constants.h" +#include "typedefs.h" + +class Pixels; + +struct Image +{ + int width{0}; + int height{0}; + uint16_t data[img_height][img_width] = {{0}}; + uint16_t rotated_cw[img_width][img_height] = {{0}}; + size_t dataSize{0}; + unsigned int stride{0}; + libcamera::PixelFormat pixelFormat{0}; + Counters counters{}; + + void rotate(); + std::shared_ptr<Pixels> pixels() const; +}; diff --git a/src/pixels.cpp b/src/pixels.cpp index b314e6a..5aac4a6 100644 --- a/src/pixels.cpp +++ b/src/pixels.cpp @@ -93,3 +93,10 @@ bool Pixels::save(const QString& filename) return true; } + +Pixels::operator bool() const +{ + return std::find_if(pixels.cbegin(), pixels.cend(), [](const auto& p) { + return !qFuzzyIsNull(p) && !std::isnan(p); + }) != pixels.cend(); +} diff --git a/src/pixels.h b/src/pixels.h index 6095220..1d5effb 100644 --- a/src/pixels.h +++ b/src/pixels.h @@ -6,6 +6,7 @@ #include <QString> +#include "constants.h" #include "typedefs.h" struct Pixels @@ -29,4 +30,6 @@ struct Pixels * \return - true on success, false otherwise */ [[nodiscard]] bool save(const QString& filename); + + operator bool() const; }; diff --git a/src/typedefs.h b/src/typedefs.h index 2af2921..7ed8411 100644 --- a/src/typedefs.h +++ b/src/typedefs.h @@ -2,8 +2,6 @@ #include <libcamera/pixel_format.h> -#include "constants.h" - struct Counters { uint32_t timestampUs{0}; @@ -11,22 +9,10 @@ struct Counters int32_t encoderPosition{0}; }; -struct Image -{ - int width; - int height; - uint16_t data[img_height][img_width]; - uint16_t rotated_cw[img_width][img_height]; - size_t dataSize; - unsigned int stride; - libcamera::PixelFormat pixelFormat; - Counters counters{}; -}; - struct requested_params_t { - int32_t exposureTime = {1000}; - int32_t laserLevel = {3000}; + int32_t exposureTime{1000}; + int32_t laserLevel{3000}; uint32_t stepsPerMm{200}; }; |
