Compare commits
48 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
9e0d7a34cd | 12 months ago |
![]() |
773734e6be | 12 months ago |
![]() |
45ef16522d | 1 year ago |
![]() |
3775fcfb1a | 1 year ago |
![]() |
f4beff5fc9 | 1 year ago |
![]() |
476cd5f28a | 1 year ago |
![]() |
aec946622c | 1 year ago |
![]() |
53978fe62c | 1 year ago |
![]() |
609997f878 | 1 year ago |
![]() |
d8a45f7e36 | 1 year ago |
![]() |
25e9e79159 | 1 year ago |
![]() |
d7a438b14c | 1 year ago |
![]() |
1a6f619db6 | 1 year ago |
![]() |
c069025ee6 | 1 year ago |
![]() |
5b9dbbb286 | 1 year ago |
![]() |
9f177afc05 | 1 year ago |
![]() |
10e22bdfaf | 1 year ago |
![]() |
b12e6ef392 | 1 year ago |
![]() |
cea91493db | 1 year ago |
![]() |
fea2b31c15 | 1 year ago |
![]() |
6b1fd2f0e3 | 1 year ago |
![]() |
0baa99402d | 1 year ago |
![]() |
cefc4dc4c5 | 1 year ago |
![]() |
d5703a23fd | 1 year ago |
![]() |
b1852b8259 | 1 year ago |
![]() |
fdfc884cf3 | 1 year ago |
![]() |
58422814e6 | 1 year ago |
![]() |
6abd14cfa7 | 1 year ago |
![]() |
391d304fef | 1 year ago |
![]() |
f889f0dfc2 | 1 year ago |
![]() |
b2f0db9b90 | 1 year ago |
![]() |
6641250cc2 | 1 year ago |
![]() |
116efb461a | 1 year ago |
![]() |
ffeb1beb1d | 1 year ago |
![]() |
fa8a5423ef | 1 year ago |
![]() |
550db09287 | 1 year ago |
![]() |
72a7dd0e1c | 1 year ago |
![]() |
c28a56a3e1 | 1 year ago |
![]() |
cf3f6b8658 | 1 year ago |
![]() |
91fae55dd3 | 1 year ago |
![]() |
1cb3fdb21f | 1 year ago |
![]() |
f42ef02b39 | 1 year ago |
![]() |
1fd4478521 | 1 year ago |
![]() |
307d0be80c | 1 year ago |
![]() |
a792c7e06d | 1 year ago |
![]() |
72e3c72374 | 1 year ago |
![]() |
6ce7283502 | 1 year ago |
![]() |
02679b6a9c | 1 year ago |
@ -0,0 +1,7 @@ |
||||
Copyright © 2021 Alister Sanders (alister@sugol.org) |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -1,200 +0,0 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<requires lib="gtk+" version="3.24"/> |
||||
<object class="GtkApplicationWindow" id="mainWindow"> |
||||
<property name="name">mainWindow</property> |
||||
<property name="title" translatable="yes">Books</property> |
||||
<property name="default-width">1200</property> |
||||
<property name="default-height">800</property> |
||||
<child> |
||||
<object class="GtkPaned" id="content"> |
||||
<property name="shrink-start-child">0</property> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="width-request">300</property> |
||||
<property name="margin-start">8</property> |
||||
<property name="margin-end">8</property> |
||||
<property name="margin-top">16</property> |
||||
<property name="margin-bottom">16</property> |
||||
<property name="orientation">vertical</property> |
||||
<property name="spacing">8</property> |
||||
<child> |
||||
<object class="GtkScrolledWindow"> |
||||
<property name="vexpand">1</property> |
||||
<property name="propagate-natural-width">1</property> |
||||
<child> |
||||
<object class="GtkViewport"> |
||||
<child> |
||||
<object class="GtkTreeView" id="filesTreeView"> |
||||
<property name="activate-on-single-click">1</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="can-focus">0</property> |
||||
<property name="orientation">vertical</property> |
||||
<property name="spacing">8</property> |
||||
<child> |
||||
<object class="GtkGrid"> |
||||
<property name="can-focus">0</property> |
||||
<property name="row-spacing">6</property> |
||||
<property name="column-spacing">6</property> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<property name="label" translatable="yes">Image directory</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">0</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<property name="label" translatable="yes">Export directory</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">1</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnExportDirectory"> |
||||
<property name="label" translatable="yes">(None)</property> |
||||
<property name="receives-default">1</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">1</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnImageDirectory"> |
||||
<property name="label" translatable="yes">(None)</property> |
||||
<property name="receives-default">1</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">0</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Margins</property> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">2</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkScale" id="scaleMargins"> |
||||
<property name="hexpand">1</property> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">-50</property> |
||||
<property name="upper">200</property> |
||||
<property name="value">0</property> |
||||
</object> |
||||
</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">2</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkExpander"> |
||||
<property name="label">Advanced</property> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="margin-start">8</property> |
||||
<property name="margin-end">8</property> |
||||
<property name="margin-top">16</property> |
||||
<property name="margin-bottom">16</property> |
||||
<property name="orientation">vertical</property> |
||||
<property name="spacing">8</property> |
||||
<child> |
||||
<object class="GtkCheckButton" id="chkShowFeatures"> |
||||
<property name="label">Display features</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkCheckButton" id="chkShowFitness"> |
||||
<property name="label">Display fitness scores</property> |
||||
<property name="sensitive">0</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="visible">0</property> |
||||
<property name="orientation">horizontal</property> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Show filter step</property> |
||||
<property name="hexpand">1</property> |
||||
<property name="halign">start</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkSpinButton" id="spinBtnLayer"> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">-1</property> |
||||
<property name="upper">10</property> |
||||
<property name="step-increment">1</property> |
||||
</object> |
||||
</property> |
||||
<property name="value">-1</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnExport"> |
||||
<property name="label" translatable="yes">Export</property> |
||||
<property name="receives-default">1</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkScrolledWindow"> |
||||
<child> |
||||
<object class="GtkViewport"> |
||||
<property name="can-focus">0</property> |
||||
<child> |
||||
<object class="GtkDrawingArea" id="preview"> |
||||
<property name="can-focus">0</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</interface> |
@ -1,19 +0,0 @@ |
||||
#include <gtkmm.h> |
||||
#include <mainwindow.hpp> |
||||
#include <ui-resources.h> |
||||
|
||||
static void on_startup(Glib::RefPtr<Gtk::Application> app) { |
||||
ui::MainWindow* window = ui::MainWindow::create(); |
||||
app->add_window(*window); |
||||
window->show(); |
||||
} |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
auto resource_bundle = Glib::wrap(ui_res_get_resource()); |
||||
resource_bundle->register_global(); |
||||
|
||||
auto app = Gtk::Application::create("org.sugol.books"); |
||||
app->signal_startup().connect([&]() { on_startup(app); }); |
||||
|
||||
return app->run(argc, argv); |
||||
} |
@ -1,102 +0,0 @@ |
||||
#include <vector> |
||||
#include <algorithm> |
||||
|
||||
#include <opencv2/objdetect.hpp> |
||||
#include <util/box.hpp> |
||||
#include <crop.hpp> |
||||
|
||||
Cropper::Cropper() {} |
||||
|
||||
cv::Rect Cropper::auto_crop(CVImage& image, int margin, bool remove_whiteboard, int kernel_size) { |
||||
float image_area = image.total(); |
||||
cv::Mat edges = m_filterchain.apply_filters(image); |
||||
|
||||
std::vector<std::vector<cv::Point>> contours; |
||||
cv::findContours(edges, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); |
||||
|
||||
std::vector<cv::Rect> bounding_boxes; |
||||
for (auto c : contours) { |
||||
cv::Rect box = cv::boundingRect(c); |
||||
bounding_boxes.push_back(box); |
||||
|
||||
// if (box.area() / image_area >= MIN_RELATIVE_BOX_SIZE) {
|
||||
// bounding_boxes.push_back(box);
|
||||
// }
|
||||
} |
||||
|
||||
if (bounding_boxes.size() == 0) { |
||||
return cv::Rect(0, 0, 0, 0); |
||||
} |
||||
|
||||
|
||||
cv::groupRectangles(bounding_boxes, 0, 10); |
||||
|
||||
util::Point image_center = {(unsigned int)image.size().width / 2, (unsigned int)image.size().height / 2}; |
||||
util::Box midbox = bounding_boxes[0]; |
||||
|
||||
// Find the box that is closest to the image center
|
||||
for (util::Box box : bounding_boxes) { |
||||
if (image_center.distance_to(box.midpoint()) < image_center.distance_to(midbox.midpoint())) { |
||||
midbox = box; |
||||
} |
||||
} |
||||
|
||||
return midbox; |
||||
|
||||
// if (remove_whiteboard) {
|
||||
// if (bounding_boxes.size() == 1) {
|
||||
// return cv::Rect(0, 0, 0, 0);
|
||||
// }
|
||||
|
||||
// // Sort bounding boxes by x-position
|
||||
// std::sort(bounding_boxes.begin(), bounding_boxes.end(), [](cv::Rect r1, cv::Rect r2) {
|
||||
// return r1.x < r2.x;
|
||||
// });
|
||||
|
||||
// // Remove the first bounding box (the whiteboard)
|
||||
// bounding_boxes.erase(bounding_boxes.begin());
|
||||
// }
|
||||
|
||||
// for (auto b : bounding_boxes) {
|
||||
// cv::Point pt1(b.x, b.y);
|
||||
// cv::Point pt2(b.x + b.width, b.y + b.height);
|
||||
// cv::rectangle(image, pt1, pt2, cv::Scalar(0, 255, 0), 2);
|
||||
// }
|
||||
|
||||
// Get the enclosing box
|
||||
// cv::Rect enclosing = enclose_bounding_boxes(bounding_boxes);
|
||||
|
||||
// // Pad out the enclosing box with a margin
|
||||
// enclosing.x -= margin; enclosing.x = std::max(enclosing.x, 0);
|
||||
// enclosing.y -= margin; enclosing.y = std::max(enclosing.y, 0);
|
||||
// enclosing.width += margin * 2;
|
||||
// enclosing.width = std::min(enclosing.width, image.size().width - enclosing.x);
|
||||
// enclosing.height += margin * 2;
|
||||
// enclosing.height = std::min(enclosing.height, image.size().height - enclosing.y);
|
||||
//
|
||||
} |
||||
|
||||
filter::FilterChain& Cropper::get_filterchain() { |
||||
return m_filterchain; |
||||
} |
||||
|
||||
cv::Rect Cropper::enclose_bounding_boxes(std::vector<util::Box> boxes) { |
||||
std::vector<int> x_vals; |
||||
std::vector<int> y_vals; |
||||
|
||||
for (auto& box : boxes) { |
||||
x_vals.push_back(box.top_left().x); |
||||
x_vals.push_back(box.bottom_right().x); |
||||
y_vals.push_back(box.top_left().y); |
||||
y_vals.push_back(box.bottom_right().y); |
||||
} |
||||
|
||||
// TODO: Sort the two lists beforehand so we don't have to use
|
||||
// min/max_element
|
||||
int x1 = *std::min_element(x_vals.begin(), x_vals.end()); |
||||
int y1 = *std::min_element(y_vals.begin(), y_vals.end()); |
||||
int x2 = *std::max_element(x_vals.begin(), x_vals.end()); |
||||
int y2 = *std::max_element(y_vals.begin(), y_vals.end()); |
||||
|
||||
return cv::Rect(x1, y1, x2 - x1, y2 - y1); |
||||
} |
@ -1,24 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#define MIN_RELATIVE_BOX_SIZE 0.03 |
||||
|
||||
#include <util/box.hpp> |
||||
#include <filter/filterchain.hpp> |
||||
|
||||
class Cropper { |
||||
public: |
||||
Cropper(); |
||||
|
||||
cv::Rect auto_crop(cv::Mat& image, |
||||
int margin = 32, |
||||
bool remove_whiteboard = true, |
||||
int kernel_size = 11); |
||||
|
||||
filter::FilterChain& get_filterchain(); |
||||
|
||||
private: |
||||
cv::Rect enclose_bounding_boxes(std::vector<util::Box> boxes); |
||||
|
||||
private: |
||||
filter::FilterChain m_filterchain; |
||||
}; |
After Width: | Height: | Size: 390 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 2.3 KiB |
@ -0,0 +1,318 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<interface> |
||||
<requires lib="gtk+" version="4.0"/> |
||||
<object class="GtkApplicationWindow" id="mainWindow"> |
||||
<property name="name">mainWindow</property> |
||||
<property name="title" translatable="yes">Books</property> |
||||
<property name="default-width">1200</property> |
||||
<property name="default-height">800</property> |
||||
<child type="titlebar"> |
||||
<object class="GtkHeaderBar"> |
||||
<child type="end"> |
||||
<object class="GtkMenuButton"> |
||||
<property name="icon-name">open-menu-symbolic</property> |
||||
<property name="popover"> |
||||
<object class="GtkPopover"> |
||||
<property name="width-request">300</property> |
||||
<property name="halign">end</property> |
||||
<child> |
||||
<object class="GtkGrid"> |
||||
<property name="can-focus">0</property> |
||||
<property name="margin-top">8</property> |
||||
<property name="margin-start">8</property> |
||||
<property name="margin-bottom">8</property> |
||||
<property name="margin-end">8</property> |
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Margins</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">0</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkBox"> |
||||
<style> |
||||
<class name="linked" /> |
||||
</style> |
||||
<property name="halign">end</property> |
||||
<property name="orientation">horizontal</property> |
||||
<child> |
||||
<object class="GtkButton" id="btnMarginAdd"> |
||||
<property name="icon-name">list-add-symbolic</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnMarginSubtract"> |
||||
<property name="icon-name">list-remove-symbolic</property> |
||||
</object> |
||||
</child> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">0</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Rejection threshold</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">1</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkScale" id="scaleRejection"> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">0</property> |
||||
<property name="upper">1</property> |
||||
<property name="value">0.1</property> |
||||
</object> |
||||
</property> |
||||
<property name="digits">2</property> |
||||
<property name="draw-value">1</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">1</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Display features</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">2</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkSwitch" id="switchShowFeatures"> |
||||
<property name="halign">end</property> |
||||
<property name="hexpand">0</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">2</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Display fitness score</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">3</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkSwitch" id="switchShowFitness"> |
||||
<property name="halign">end</property> |
||||
<property name="hexpand">0</property> |
||||
<property name="sensitive">0</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">3</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Blur kernel size</property> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">4</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkSpinButton" id="spinBlurKernel"> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">1</property> |
||||
<property name="upper">49</property> |
||||
<property name="step-increment">2</property> |
||||
<property name="value">21</property> |
||||
</object> |
||||
</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">4</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Dilate kernel size</property> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">5</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkSpinButton" id="spinDilateKernel"> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">1</property> |
||||
<property name="upper">50</property> |
||||
<property name="step-increment">1</property> |
||||
<property name="value">16</property> |
||||
</object> |
||||
</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">5</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkLabel"> |
||||
<property name="label">Threshold</property> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">start</property> |
||||
<property name="hexpand">1</property> |
||||
<layout> |
||||
<property name="column">0</property> |
||||
<property name="row">6</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
|
||||
<child> |
||||
<object class="GtkSpinButton" id="spinThreshold"> |
||||
<property name="adjustment"> |
||||
<object class="GtkAdjustment"> |
||||
<property name="lower">0</property> |
||||
<property name="upper">255</property> |
||||
<property name="step-increment">1</property> |
||||
<property name="value">70</property> |
||||
</object> |
||||
</property> |
||||
<layout> |
||||
<property name="column">1</property> |
||||
<property name="row">6</property> |
||||
</layout> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnImport"> |
||||
<style> |
||||
<class name="flat" /> |
||||
</style> |
||||
<property name="icon-name">folder-new-symbolic</property> |
||||
<property name="tooltip-text">Add folder</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnExport"> |
||||
<style> |
||||
<class name="flat" /> |
||||
</style> |
||||
<property name="icon-name">document-save-symbolic</property> |
||||
<property name="tooltip-text">Export</property> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkButton" id="btnReload"> |
||||
<style> |
||||
<class name="flat" /> |
||||
</style> |
||||
<property name="icon-name">view-refresh-symbolic</property> |
||||
<property name="tooltip-text">Reload</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkPaned" id="content"> |
||||
<child> |
||||
<object class="GtkScrolledWindow"> |
||||
<property name="width-request">300</property> |
||||
<child> |
||||
<object class="GtkTreeView" id="filesTreeView"> |
||||
<property name="activate-on-single-click">1</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkBox"> |
||||
<property name="hexpand">1</property> |
||||
<property name="width-request">300</property> |
||||
<property name="height-request">200</property> |
||||
<child> |
||||
<object class="GtkOverlay"> |
||||
<property name="hexpand">1</property> |
||||
<child type="overlay"> |
||||
<object class="GtkButton" id="btnPrevImage"> |
||||
<property name="halign">start</property> |
||||
<property name="valign">center</property> |
||||
<property name="icon-name">go-previous-symbolic</property> |
||||
<style> |
||||
<class name="flat" /> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
<child type="overlay"> |
||||
<object class="GtkButton" id="btnNextImage"> |
||||
<property name="halign">end</property> |
||||
<property name="valign">center</property> |
||||
<property name="icon-name">go-next-symbolic</property> |
||||
<style> |
||||
<class name="flat" /> |
||||
</style> |
||||
</object> |
||||
</child> |
||||
<child> |
||||
<object class="GtkPicture" id="preview"> |
||||
<property name="can-focus">0</property> |
||||
<property name="halign">center</property> |
||||
<property name="hexpand">1</property> |
||||
<property name="vexpand">1</property> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</child> |
||||
</object> |
||||
</interface> |
After Width: | Height: | Size: 7.7 KiB |
@ -0,0 +1,24 @@ |
||||
1 VERSIONINFO |
||||
FILEVERSION 1,0,0,0 |
||||
PRODUCTVERSION 1,0,0,0 |
||||
BEGIN |
||||
BLOCK "StringFileInfo" |
||||
BEGIN |
||||
BLOCK "040904E4" |
||||
BEGIN |
||||
VALUE "CompanyName", "Sugol" |
||||
VALUE "FileDescription", "Books" |
||||
VALUE "FileVersion", "0.2.2" |
||||
VALUE "InternalName", "Books" |
||||
VALUE "LegalCopyright", "al (alister@sugol.org)" |
||||
VALUE "OriginalFilename", "books.exe" |
||||
VALUE "ProductName", "Books" |
||||
VALUE "ProductVersion", "0.2.2" |
||||
END |
||||
END |
||||
BLOCK "VarFileInfo" |
||||
BEGIN |
||||
VALUE "Translation", 0x409, 1252 |
||||
END |
||||
END |
||||
id ICON "books.ico" |
@ -0,0 +1,22 @@ |
||||
#!/bin/bash |
||||
|
||||
# Copyright (C) 2021 Alister Sanders |
||||
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
# of this software and associated documentation files (the "Software"), to deal |
||||
# in the Software without restriction, including without limitation the rights |
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
# copies of the Software, and to permit persons to whom the Software is |
||||
# furnished to do so, subject to the following conditions: |
||||
# The above copyright notice and this permission notice shall be included in |
||||
# all copies or substantial portions of the Software. |
||||
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
# THE SOFTWARE. |
||||
|
||||
convert ../icons/org.sugol.books-16.png ../icons/org.sugol.books-32.png ../icons/org.sugol.books-256.png books.ico |
@ -1,30 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <fitnessmetrics.hpp> |
||||
#include <imagedata.hpp> |
||||
#include <worker.hpp> |
||||
|
||||
namespace worker { |
||||
class FeatureDetector : public Worker<img::ImageData, img::ImageData> { |
||||
public: |
||||
const double CV_IMAGE_TARGET_DIMENSION = 400.0; |
||||
|
||||
FeatureDetector(std::shared_ptr<InputQueue> input_queue, |
||||
std::shared_ptr<OutputQueue> output_queue, |
||||
const std::vector<ft::FitnessMetric>& fitness_metrics, |
||||
const std::vector<float>& fitness_metric_weights); |
||||
|
||||
void run(IWorkerPool* wp) override; |
||||
|
||||
private: |
||||
cv::UMat apply_filters(const cv::UMat& mat); |
||||
void find_features(img::ImageData& image_data); |
||||
double resize_mat(const cv::UMat& src, cv::UMat& dest); |
||||
float calculate_fitness(const cv::Mat& mat, const util::Box& box); |
||||
img::ImageData::Feature& best_candidate_box(std::vector<img::ImageData::Feature>& features); |
||||
|
||||
private: |
||||
std::vector<ft::FitnessMetric> m_fitness_metrics; |
||||
std::vector<float> m_fitness_weights; |
||||
}; |
||||
} |
@ -1,63 +0,0 @@ |
||||
#include <featuredetectorpool.hpp> |
||||
|
||||
namespace worker { |
||||
FeatureDetectorPool::FeatureDetectorPool(size_t n_workers, |
||||
std::vector<ft::FitnessMetric> fitness_metrics, |
||||
std::vector<float> fitness_weights) { |
||||
m_active_workers = n_workers; |
||||
m_input_queue = std::make_shared<InputQueue>(); |
||||
m_output_queue = std::make_shared<OutputQueue>(); |
||||
|
||||
for (size_t i = 0; i < n_workers; i++) { |
||||
m_workers.push_back(std::make_unique<FeatureDetector>(m_input_queue, |
||||
m_output_queue, |
||||
fitness_metrics, |
||||
fitness_weights)); |
||||
} |
||||
} |
||||
|
||||
FeatureDetectorPool::~FeatureDetectorPool() { |
||||
join_all(); |
||||
} |
||||
|
||||
std::shared_ptr<FeatureDetectorPool::InputQueue> FeatureDetectorPool::input() { |
||||
return m_input_queue; |
||||
} |
||||
|
||||
std::shared_ptr<FeatureDetectorPool::OutputQueue> FeatureDetectorPool::output() { |
||||
return m_output_queue; |
||||
} |
||||
|
||||
void FeatureDetectorPool::set_input(std::shared_ptr<InputQueue> in) { |
||||
m_input_queue = in; |
||||
|
||||
for (auto& worker : m_workers) { |
||||
worker->set_input_queue(m_input_queue); |
||||
} |
||||
} |
||||
|
||||
void FeatureDetectorPool::set_output(std::shared_ptr<OutputQueue> out) { |
||||
m_output_queue = out; |
||||
} |
||||
|
||||
void FeatureDetectorPool::run_workers() { |
||||
for (auto& worker : m_workers) { |
||||
worker->run(this); |
||||
} |
||||
} |
||||
|
||||
void FeatureDetectorPool::join_all() { |
||||
for (auto& worker : m_workers) { |
||||
worker->join(); |
||||
} |
||||
} |
||||
|
||||
void FeatureDetectorPool::signal_done() { |
||||
std::lock_guard lock(m_mutex); |
||||
m_active_workers--; |
||||
|
||||
if (m_active_workers == 0) { |
||||
m_output_queue->finish(); |
||||
} |
||||
} |
||||
} |
@ -1,32 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <featuredetector.hpp> |
||||
#include <workerpool.hpp> |
||||
|
||||
namespace worker { |
||||
class FeatureDetectorPool : public WorkerPool<FeatureDetector> { |
||||
public: |
||||
FeatureDetectorPool() { } |
||||
FeatureDetectorPool(size_t n_workers, |
||||
std::vector<ft::FitnessMetric> fitness_metrics, |
||||
std::vector<float> fitness_weights); |
||||
|
||||
~FeatureDetectorPool(); |
||||
|
||||
std::shared_ptr<InputQueue> input() override; |
||||
std::shared_ptr<OutputQueue> output() override; |
||||
void set_input(std::shared_ptr<InputQueue> in) override; |
||||
void set_output(std::shared_ptr<OutputQueue> out) override; |
||||
|
||||
void run_workers() override; |
||||
void join_all() override; |
||||
void signal_done() override; |
||||
|
||||
private: |
||||
std::shared_ptr<InputQueue> m_input_queue; |
||||
std::shared_ptr<OutputQueue> m_output_queue; |
||||
mutable std::mutex m_mutex; |
||||
size_t m_active_workers; |
||||
std::vector<std::unique_ptr<FeatureDetector>> m_workers; |
||||
}; |
||||
} |
@ -1,13 +0,0 @@ |
||||
#include "opencv2/imgproc.hpp" |
||||
#include <filter/bgr2grey.hpp> |
||||
|
||||
namespace filter { |
||||
BGR2GreyFilter::~BGR2GreyFilter() {} |
||||
|
||||
cv::Mat BGR2GreyFilter::apply(const cv::Mat &img) { |
||||
cv::Mat img_grey; |
||||
cv::cvtColor(img, img_grey, cv::COLOR_BGR2GRAY); |
||||
|
||||
return img_grey; |
||||
} |
||||
} |
@ -1,11 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |
||||
class BGR2GreyFilter : public Filter { |
||||
public: |
||||
~BGR2GreyFilter(); |
||||
cv::Mat apply(cv::Mat const& img) override; |
||||
}; |
||||
} |
@ -1,25 +0,0 @@ |
||||
#include "opencv2/imgproc.hpp" |
||||
#include <filter/canny.hpp> |
||||
|
||||
namespace filter { |
||||
CannyFilter::~CannyFilter() { |
||||
} |
||||
|
||||
cv::Mat CannyFilter::apply(const cv::Mat& img) { |
||||
double low_thres, high_thres; |
||||
get_thresholds(img, low_thres, high_thres); |
||||
|
||||
cv::Mat canny; |
||||
cv::Canny(img, canny, low_thres, high_thres); |
||||
|
||||
return canny; |
||||
} |
||||
|
||||
void CannyFilter::get_thresholds(const cv::Mat& img, double& low, double& high) { |
||||
cv::Mat dummy; |
||||
|
||||
// Automagically determine threshold values for the canny filter. Do you know how this works? I sure don't.
|
||||
high = cv::threshold(img, dummy, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); |
||||
low = 0.5 * high; |
||||
} |
||||
} |
@ -1,13 +0,0 @@ |
||||
#pragma once |
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |
||||
class CannyFilter : public Filter { |
||||
public: |
||||
~CannyFilter(); |
||||
cv::Mat apply(cv::Mat const& img) override; |
||||
|
||||
private: |
||||
void get_thresholds(cv::Mat const& img, double& low, double& high); |
||||
}; |
||||
} |
@ -1,17 +0,0 @@ |
||||
#include <filter/dilate.hpp> |
||||
|
||||
namespace filter { |
||||
DilateFilter::DilateFilter(int kernel_size) |
||||
: m_kernelSize(kernel_size) { } |
||||
|
||||
DilateFilter::~DilateFilter() {} |
||||
|
||||
cv::Mat DilateFilter::apply(const cv::Mat &img) { |
||||
cv::Mat dilated; |
||||
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(m_kernelSize, m_kernelSize)); |
||||
|
||||
cv::dilate(img, dilated, kernel); |
||||
|
||||
return dilated; |
||||
} |
||||
} |
@ -1,15 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |
||||
class DilateFilter : public Filter { |
||||
public: |
||||
DilateFilter(int kernel_size = 8); |
||||
~DilateFilter(); |
||||
cv::Mat apply(cv::Mat const& img) override; |
||||
|
||||
private: |
||||
int m_kernelSize; |
||||
}; |
||||
} |
@ -1,11 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <opencv2/imgproc.hpp> |
||||
|
||||
namespace filter { |
||||
class Filter { |
||||
public: |
||||
virtual ~Filter() { }; |
||||
virtual cv::Mat apply(cv::Mat const& img) = 0; |
||||
}; |
||||
} |
@ -1,34 +0,0 @@ |
||||
#include "filter/canny.hpp" |
||||
#include "filter/medianblur.hpp" |
||||
#include <filter/filterchain.hpp> |
||||
|
||||
#include <filter/filters_all.hpp> |
||||
|
||||
namespace filter { |
||||
FilterChain::FilterChain() { |
||||
// Construct the default filter chain
|
||||
// TODO: Allow for custom filter chains
|
||||
|
||||
m_filters.emplace_back(new BGR2GreyFilter); |
||||
m_filters.emplace_back(new MedianBlurFilter(11)); |
||||
m_filters.emplace_back(new NormaliseFilter); |
||||
m_filters.emplace_back(new DilateFilter(4)); |
||||
m_filters.emplace_back(new ThresholdFilter); |
||||
m_filters.emplace_back(new CannyFilter); |
||||
} |
||||
|
||||
FilterChain::~FilterChain() { |
||||
for (auto filter : m_filters){ |
||||
delete filter; |
||||
} |
||||
} |
||||
|
||||
cv::Mat FilterChain::apply_filters(const cv::Mat& img) { |
||||
cv::Mat filter_result = img; |
||||
for (auto& filter : m_filters) { |
||||
filter_result = filter->apply(filter_result); |
||||
} |
||||
|
||||
return filter_result; |
||||
} |
||||
} |
@ -1,19 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <opencv2/imgproc.hpp> |
||||
#include <vector> |
||||
#include <map> |
||||
|
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |
||||
class FilterChain { |
||||
public: |
||||
FilterChain(); |
||||
~FilterChain(); |
||||
cv::Mat apply_filters(const cv::Mat& img); |
||||
|
||||
private: |
||||
std::vector<Filter*> m_filters; |
||||
}; |
||||
} |
@ -1,8 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <filter/bgr2grey.hpp> |
||||
#include <filter/medianblur.hpp> |
||||
#include <filter/normalise.hpp> |
||||
#include <filter/dilate.hpp> |
||||
#include <filter/threshold.hpp> |
||||
#include <filter/canny.hpp> |
@ -1,16 +0,0 @@ |
||||
#include "opencv2/imgproc.hpp" |
||||
#include <filter/medianblur.hpp> |
||||
|
||||
namespace filter { |
||||
MedianBlurFilter::MedianBlurFilter(int kernel_size) |
||||
: m_kernel_size(kernel_size) { } |
||||
|
||||
MedianBlurFilter::~MedianBlurFilter() {} |
||||
|
||||
cv::Mat MedianBlurFilter::apply(const cv::Mat& img) { |
||||
cv::Mat blurred; |
||||
cv::medianBlur(img, blurred, m_kernel_size); |
||||
|
||||
return blurred; |
||||
} |
||||
} |
@ -1,15 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |
||||
class MedianBlurFilter : public Filter { |
||||
public: |
||||
MedianBlurFilter(int kernel_size = 11); |
||||
~MedianBlurFilter(); |
||||
cv::Mat apply(cv::Mat const& img) override; |
||||
|
||||
private: |
||||
int m_kernel_size; |
||||
}; |
||||
} |
@ -1,13 +0,0 @@ |
||||
#include "opencv2/core.hpp" |
||||
#include "opencv2/core/base.hpp" |
||||
#include <filter/normalise.hpp> |
||||
|
||||
namespace filter { |
||||
NormaliseFilter::~NormaliseFilter() {} |
||||
|
||||
cv::Mat NormaliseFilter::apply(const cv::Mat& img) { |
||||
cv::Mat normalised; |
||||
cv::normalize(img, normalised, 255, 0, cv::NORM_MINMAX); |
||||
return normalised; |
||||
} |
||||
} |
@ -1,11 +0,0 @@ |
||||
#pragma once |
||||
|
||||
#include <filter/filter.hpp> |
||||
|
||||
namespace filter { |