Просмотр сообщений

В этом разделе можно просмотреть все сообщения, сделанные этим пользователем.


Сообщения - monday2000

Страницы: 1 2 3 [4] 5 6 ... 72
46
NBell
Цитировать
а сохранение проекта логично предложить сразу после его создания - пока пользователь не забыл.
Это уже слишком.
Цитировать
как бы это пожелание донести до тулона?
Ничего он делать не будет, можете и не пытаться. Он уже и так сделал слишком много для всех нас.
Цитировать
или в вашем клоне будет вызов процедуры сохранения проекта сразу после его создания?
Нет, не будет. Ответственность за первоначальное сохранение проекта возлагается на самого пользователя. Автосохранение - это не более чем защита от случайного падения программы, и всё. Я не знаю ни единой программы, которая бы предлагала сама ни с того ни с сего сохранить (открытый файл, или проект, или т.п.)
Цитировать
и лишним не будет что то вроде readme с кратеньким описанием фич.
Уже есть - на https://sourceforge.net/projects/scantailor/files/scantailor-devel/featured/ (на английском языке).

47
Официальный выпуск моего клона Scan Tailor:
 
Scan Tailor Featured
 
https://sourceforge.net/projects/scantailor/files/scantailor-devel/featured/

48
Новая сборка.

Цитировать
при нажатом shift ваш мод рисует лучи солнца. 0.9.11.1 - нет. так что надо еще какое то место найти, где надо что то поменять.
Исправлено.

Добавлено:

- Автосохранение существующего проекта.

Условное наименование:

Auto_Save_Project

1. Включается в меню Настройки - в виде новой отдельной галки. Значение сохраняется между сеансами работы с программой.

2. Действует только для существующего проекта, если проект не сохранён изначально пользователем, то автосохранение не работает.

3. Автосохранение происходит при переключении со скана на скан - как в ScanKromsator.

4. При пакетной обработке (кажется) тоже работает.

Короче, надо ещё эту фичу тестировать - правильно ли она работает, хорошо ли получилась. Лично мне она совсем без интереса - у меня СТ никогда не падает.

Коды исправления:
C:\build\scantailor\SettingsDialog.h

class SettingsDialog : public QDialog
{
........
public:
.......
//begin of modified by monday2000
//Auto_Save_Project
signals:
void AutoSaveProjectStateSignal(bool auto_save);
//end of modified by monday2000
.......
private slots:
.......
//Auto_Save_Project
void OnCheckAutoSaveProject(bool);
//end of modified by monday2000
.......

C:\build\scantailor\SettingsDialog.cpp

SettingsDialog::SettingsDialog(QWidget* parent)
.......
//begin of modified by monday2000
//Auto_Save_Project
ui.AutoSaveProject->setChecked(settings.value("settings/auto_save_project").toBool());
connect(ui.AutoSaveProject, SIGNAL(toggled(bool)), this, SLOT(OnCheckAutoSaveProject(bool)));
//end of modified by monday2000
......

//begin of modified by monday2000
//Auto_Save_Project
void
SettingsDialog::OnCheckAutoSaveProject(bool state)
{
QSettings settings;

settings.setValue("settings/auto_save_project", state);

emit AutoSaveProjectStateSignal(state);
}
//end of modified by monday2000

C:\build\scantailor\MainWindow.h

class MainWindow :
....
public:
......
//begin of modified by monday2000
........
//Auto_Save_Project
void AutoSaveProjectState(bool auto_save);
//end of modified by monday2000
.........
private:
.......
//begin of modified by monday2000
.......
//Auto_Save_Project
void autoSaveProject();
bool m_auto_save_project;
//end of modified by monday2000
........

C:\build\scantailor\MainWindow.cpp

MainWindow::MainWindow()
......
#if !defined(ENABLE_OPENGL)
// Right now the only setting is 3D acceleration, so get rid of
// the whole Settings dialog, if it's inaccessible.
//begin of modified by monday2000
//Auto_Save_Project
//actionSettings->setVisible(false); // commented by monday2000
//end of modified by monday2000
#endif
.......
//begin of modified by monday2000
//Auto_Save_Project
m_auto_save_project = settings.value("settings/auto_save_project").toBool();
//end of modified by monday2000
}
........

void
MainWindow::goToPage(PageId const& page_id)
{
focusButton->setChecked(true);

m_ptrThumbSequence->setSelection(page_id);

// If the page was already selected, it will be reloaded.
// That's by design.
updateMainArea();

//begin of modified by monday2000
//Auto_Save_Project
autoSaveProject();
//end of modified by monday2000
}

void
MainWindow::currentPageChanged(
PageInfo const& page_info, QRectF const& thumb_rect,
ThumbnailSequence::SelectionFlags const flags)
{
m_selectedPage.set(page_info.id(), getCurrentView());

if ((flags & ThumbnailSequence::SELECTED_BY_USER) || focusButton->isChecked()) {
if (!(flags & ThumbnailSequence::AVOID_SCROLLING_TO)) {
thumbView->ensureVisible(thumb_rect, 0, 0);
}
}

if (flags & ThumbnailSequence::SELECTED_BY_USER) {
if (isBatchProcessingInProgress()) {
stopBatchProcessing();
} else if (!(flags & ThumbnailSequence::REDUNDANT_SELECTION)) {
// Start loading / processing the newly selected page.
updateMainArea();
}
}

//begin of modified by monday2000
//Auto_Save_Project
if (flags & ThumbnailSequence::SELECTED_BY_USER)
autoSaveProject();
//end of modified by monday2000
}

//begin of modified by monday2000
//Auto_Save_Project
void
MainWindow::autoSaveProject()
{
if (m_projectFile.isEmpty())
return;

if (!m_auto_save_project)
return;

saveProjectWithFeedback(m_projectFile);
}

void
MainWindow::AutoSaveProjectState(bool auto_save)
{
m_auto_save_project = auto_save;
}
//end of modified by monday2000
Как видно, исправление совсем маленькое и очень простое.

Сборка:  http://rghost.ru/43524866

49
NBell
Цитировать
линуксу линуксово.
Но программа-то - кроссплатформенная, и это не пустой звук - есть реальные люди, кто ею под Linux пользуется.
Цитировать
чем это чудо можно скомпилировать? (может дойдут руки до си)
Это очень просто. Знаний программирования не требуется. Инструкция по сборке:
http://scantailor.git.sourceforge.net/git/gitweb.cgi?p=scantailor/scantailor;a=blob_plain;f=packaging/windows/readme.ru.txt

50
NBell
Цитировать
тогда необходим визуальный индикатор режима - продублируйте кнопками в левой панели режим выделения.
А зачем? Разве Вы не видите в любой момент времени, удерживает Ваш палец Ctrl нажатым, или нет?
Цитировать
а если надо 3 зоны - держать CTRL? неудобно.
Ну нажимайте трижды Ctrl. И потом - ставьте прямоугольные зоны приблизительно, а потом при помощи прямоугольного сдвигания углов корректируйте их точно по месту. У меня был же изначально вариант с Caps Lock - при установленном Caps Lock был включен прямоугольный режим - но я от него отказался как от недостаточно удобного.
Цитировать
единообразность интерфейса творений МС убила всех конкурентов. держать в голове набор клавиш для каждой проги - утомительно и появляется желание послать ее при наличии аналога со стандартным интерфейсом.
Да, но не забывайте - Scan Tailor - он и под Linux работает, и даже под Mac. :) Там, наверное, свои традиции. :)
Хотите через Shift, а не через Ctrl - нет проблем, я дам Вам свои исходники, там в одном-единственном месте измените название клавиши, и соберите себе свой личный билд.

51
NBell
Цитировать
P.S. Обычно ортогональность включается с SHIFT (MS Office, Ps, м.б. другое ПО), а CTRL отвечает за включение копирования.
Мне больше нравится с Ctrl, потому что его проще нажимать, нежели чем Shift. Ctrl можно нажимать вообще не глядя - это крайняя левая кнопка клавиатуры. А Shift надо нажимать прицельно - это менее удобно.

52
Я скачал из Git самые свежие исходники оригинального Scan Tailor, собрал их и накатил туда все свои правки. Попутно я навёл порядок в своих правках - все аккуратно пометил, и даже подписал каждую правку, к какому именно исправлению она относится. Я составил для этого список условных обозначений своих правок:

1. Delete_3_Red_Points - удаление 3-х красных точек на самой верхней (нижней) горизонтальной синей линии сетки dewarping - при её создании.

2. Manual_Dewarp_Auto_Switch - автоматическое переключение на ручной режим dewarping, как только пользователь стронет с места синюю сетку dewarping.

3. Blue_Dewarp_Line_Vert_Drag - вертикальное перетаскивание самой верхней (нижней) горизонтальной синей линии сетки dewarping за её самую левую (правую) красную точку - с зажатым Ctrl.

4. Square_Picture_Zones - создание прямоугольных зон иллюстраций - с зажатым Ctrl.

5. Ortho_Corner_Move_Square_Picture_Zones - прямоугольное сдвигание углов (прямоугольных) зон иллюстраций - с зажатым Ctrl.

6. Export_Subscans - экспорт (суб)сканов.

Также мне удалось временно решить проблему перевода программы - я же нашёл глюк в официальной последней версии СТ - там слетел частично перевод. Написал Tulon о причинах, надеюсь, он подправит. Подробности, думаю, ожидаются. Так что теперь у меня сборка полностью переведена на русский - в части моих добавлений.

Вот сборка: http://rghost.ru/43502918

53
Исправил баг с Руборда:
Цитировать
Зато появился новый (?) баг на версии 2013.01.30 с прямоугольным выделением. Зажимаем Ctrl, ставим мышку в правый верхний или левый нижний угол, выделяем. Потом пытаемся изменить размер прямоугольной зоны выделения (зажимаем Ctrl и тянем за уголок) - зона выделения искажается и теряет прямоугольную форму.

Оказалось, что надо было при создании прямоугольных зон выделять 2 случая: зоны, где вершины идут в порядке по часовой стрелке, и зоны, где против часовой.

Коды исправления:
C:\build\scantailor-0.9.11.1\zones\ZoneCreationInteraction.cpp

void
ZoneCreationInteraction::onMouseMoveEvent(QMouseEvent* event, InteractionState& interaction)
{
QPointF const screen_mouse_pos(event->pos() + QPointF(0.5, 0.5));
QTransform const to_screen(m_rContext.imageView().imageToWidget());
QTransform const from_screen(m_rContext.imageView().widgetToImage());

m_nextVertexImagePos = from_screen.map(screen_mouse_pos);

QPointF const last(to_screen.map(m_ptrSpline->lastVertex()->point()));

// begin of added by monday2000

Qt::KeyboardModifiers mask = event->modifiers();

if (mask == Qt::ControlModifier)
{
m_ctrl = true;

QPointF screen_mouse_pos_mid1;
screen_mouse_pos_mid1.setX(last.x());
screen_mouse_pos_mid1.setY(screen_mouse_pos.y());

QPointF screen_mouse_pos_mid2;
screen_mouse_pos_mid2.setX(screen_mouse_pos.x());
screen_mouse_pos_mid2.setY(last.y());

int dx = screen_mouse_pos.x() - last.x();
int dy = screen_mouse_pos.y() - last.y();

if ((dx > 0 && dy > 0) || (dx < 0 && dy < 0))
{
m_nextVertexImagePos_mid1 = from_screen.map(screen_mouse_pos_mid1);
m_nextVertexImagePos_mid2 = from_screen.map(screen_mouse_pos_mid2);
}
else
{
m_nextVertexImagePos_mid2 = from_screen.map(screen_mouse_pos_mid1);
m_nextVertexImagePos_mid1 = from_screen.map(screen_mouse_pos_mid2);
}
}
//end of added by monday2000

if (Proximity(last, screen_mouse_pos) <= interaction.proximityThreshold()) {
m_nextVertexImagePos = m_ptrSpline->lastVertex()->point();
} else if (m_ptrSpline->hasAtLeastSegments(2)) {
QPointF const first(to_screen.map(m_ptrSpline->firstVertex()->point()));
if (Proximity(first, screen_mouse_pos) <= interaction.proximityThreshold()) {
m_nextVertexImagePos = m_ptrSpline->firstVertex()->point();
updateStatusTip();
}
}

m_rContext.imageView().update();
}

Сборка: http://rghost.ru/43477179

54
NBell
Всё, что пока могу предложить - вырабатывайте у себя привычку почаще нажимать Ctrl+S.

55
Очередное исправление экспорта разделённых сканов. Внесены некоторые улучшения:

- При нажатии на кнопку "Export" в окне появляется надпись "Starting the export...", а уже после неё отображается в реальном времени постраничная индикация экспорта. Я никак не мог ранее этого добиться - но всё-таки смог с помощью Tulon.

- Проверка открыт ли проект и нет ли пакетной обработки перенесена на начало обработчика нажатия кнопки "Export" - с окончания. Это сделано для того, чтобы не вылезала надпись  "Starting the export..." если, допустим, проект не открыт.

- Добавил возможность прервать экспорт в процессе его совершения. После старта экспорта кнопка "Export" меняет своё название на "Stop" - и её можно нажать, чтобы остановить процесс. Правда, кнопка получилась слегка "жестковата" - т.е. не сразу реагирует на нажатие.

- Добавил дату сборки в качестве "версии" программы.

- Попытался русифицировать свои добавления, но пока не слишком успешно. Удалось русифицировать пока лишь визуальные элементы.

- Убрал баг: ранее, если экспортировался чёрно-белый скан, установленный в режиме "Mixed", то для него создавался сплошной белый задний субскан. Теперь не создаётся.

- Кстати, галки "Split mixed" и "Default output folder" авто-запоминаются между сеансами запуска программы. По-видимому, в реестре Windows - больше негде. Точно не знаю, потому что это абстрагируется классом QSettings.

Коды исправления:
C:\build\scantailor-0.9.11.1\ExportDialog.h

#ifndef EXPORT_DIALOG_H_
#define EXPORT_DIALOG_H_

#include "ui_ExportDialog.h"
#include <QDialog>

class ExportDialog : public QDialog
{
Q_OBJECT
public:
ExportDialog(QWidget* parent = 0);

virtual ~ExportDialog();

void setCount(int count);
void StepProgress();
void reset(void);
void setExportLabel(void);
void setStartExport(void);

signals:
void ExportOutputSignal(QString export_dir_path, bool default_out_dir, bool split_subscans);
void ExportStopSignal();
void SetStartExportSignal();

public slots:
void startExport(void);

private slots:
void OnCheckSplitMixed(bool);
void OnCheckDefaultOutputFolder(bool);
void OnClickExport();
void outExportDirBrowse();
void outExportDirEdited(QString const&);
private:
Ui::ExportDialog ui;

bool m_autoOutDir;
void setExportOutputDir(QString const& dir);
int m_count;
};

#endif

#include "ExportDialog.h"
#include "ExportDialog.h.moc"
#include "OpenGLSupport.h"
#include "config.h"
#include <QSettings>
#include <QVariant>
#include <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QTimer>

ExportDialog::ExportDialog(QWidget* parent)
: QDialog(parent)
{
ui.setupUi(this);

QSettings settings;

connect(ui.SplitMixed, SIGNAL(toggled(bool)), this, SLOT(OnCheckSplitMixed(bool)));
connect(ui.DefaultOutputFolder, SIGNAL(toggled(bool)), this, SLOT(OnCheckDefaultOutputFolder(bool)));
connect(ui.ExportButton, SIGNAL(clicked()), this, SLOT(OnClickExport()));

connect(ui.outExportDirBrowseBtn, SIGNAL(clicked()), this, SLOT(outExportDirBrowse()));

connect(ui.outExportDirLine, SIGNAL(textEdited(QString const&)),
this, SLOT(outExportDirEdited(QString const&))
);

ui.SplitMixed->setChecked(settings.value("settings/split_mixed").toBool());
ui.DefaultOutputFolder->setChecked(settings.value("settings/default_output_folder").toBool());
ui.labelFilesProcessed->clear();
ui.ExportButton->setText(tr("Export"));
}

ExportDialog::~ExportDialog()
{
}


void
ExportDialog::OnCheckSplitMixed(bool state)
{
QSettings settings;

settings.setValue("settings/split_mixed", state);
}

void
ExportDialog::OnCheckDefaultOutputFolder(bool state)
{
QSettings settings;

settings.setValue("settings/default_output_folder", state);
}

void
ExportDialog::OnClickExport()
{
if (ui.outExportDirLine->text().isEmpty() &&
!ui.DefaultOutputFolder->isChecked())
{
QMessageBox::warning(
this, tr("Error"),
tr("The export output directory is empty.")
);
return;
}

QDir const out_dir(ui.outExportDirLine->text());

if (out_dir.isAbsolute() && !out_dir.exists()) {
// Maybe create it.
bool create = m_autoOutDir;
if (!m_autoOutDir) {
create = QMessageBox::question(
this, tr("Create Directory?"),
tr("The export output directory doesn't exist.  Create it?"),
QMessageBox::Yes|QMessageBox::No
) == QMessageBox::Yes;
if (!create) {
return;
}
}
if (create) {
if (!out_dir.mkpath(out_dir.path())) {
QMessageBox::warning(
this, tr("Error"),
tr("Unable to create the export output directory.")
);
return;
}
}
}
if ((!out_dir.isAbsolute() || !out_dir.exists())&& !ui.DefaultOutputFolder->isChecked()) {

QMessageBox::warning(
this, tr("Error"),
tr("The export output directory is not set or doesn't exist.")
);
return;
}

QString export_dir_path = ui.outExportDirLine->text();
bool split_subscans = ui.SplitMixed->isChecked();
bool default_out_dir = ui.DefaultOutputFolder->isChecked();

if (ui.ExportButton->text() != tr("Stop"))
emit SetStartExportSignal();

else
emit ExportStopSignal();
}

void
ExportDialog::outExportDirBrowse()
{
QString initial_dir(ui.outExportDirLine->text());
if (initial_dir.isEmpty() || !QDir(initial_dir).exists()) {
initial_dir = QDir::home().absolutePath();
}

QString const dir(
QFileDialog::getExistingDirectory(
this, tr("Export output directory"), initial_dir
)
);

if (!dir.isEmpty()) {
setExportOutputDir(dir);
}
}

void
ExportDialog::setExportOutputDir(QString const& dir)
{
ui.outExportDirLine->setText(QDir::toNativeSeparators(dir));
}

void
ExportDialog::outExportDirEdited(QString const& text)
{
m_autoOutDir = false;
}

void
ExportDialog::setCount(int count)
{
m_count = count;
ui.progressBar->setMaximum(m_count);
}

void
ExportDialog::StepProgress()
{
ui.labelFilesProcessed->setText(tr("Processed file") + " " + QString::number(ui.progressBar->value()+1) + " " + tr("of") + " " + QString::number(m_count));
ui.progressBar->setValue(ui.progressBar->value() + 1);
}

void
ExportDialog::startExport(void)
{
QString export_dir_path = ui.outExportDirLine->text();
bool split_subscans = ui.SplitMixed->isChecked();
bool default_out_dir = ui.DefaultOutputFolder->isChecked();

emit ExportOutputSignal(export_dir_path, default_out_dir, split_subscans);
}

void
ExportDialog::reset(void)
{
ui.labelFilesProcessed->clear();
ui.progressBar->setValue(0);
ui.ExportButton->setText(tr("Export"));
}

void
ExportDialog::setExportLabel(void)
{
ui.ExportButton->setText(tr("Export"));
}

void
ExportDialog::setStartExport(void)
{
m_count = 0;

ui.progressBar->setValue(0);
ui.labelFilesProcessed->setText(tr("Starting the export..."));
ui.ExportButton->setText(tr("Stop"));

QTimer::singleShot(1, this, SLOT(startExport()));
}

C:\build\scantailor-0.9.11.1\MainWindow.h

// begin of added by monday2000
#include <QMessageBox>
#include "stdint.h"
#include "TiffWriter.h"
#include "ImageLoader.h"
#include "ExportDialog.h"
// end of added by monday2000

class MainWindow :
......
public slots:
.......
//begin of added by monday2000
void ExportOutput(QString export_dir_path, bool default_out_dir, bool split_subscans);
void ExportStop();
void SetStartExport();
//end of added by monday2000
........
private slots:
..........
//begin of added by monday2000
void openExportDialog();
//end of added by monday2000

private:
//begin of added by monday2000
ExportDialog* m_p_export_dialog;
QVector<QString> m_outpaths_vector;
int ExportNextFile();
int m_exportTimerId;
QString m_export_dir;
bool m_split_subscans;
int m_pos_export;
//end of added by monday2000
}

C:\build\scantailor-0.9.11.1\MainWindow.cpp

MainWindow::MainWindow()
.......
//begin of added by monday2000
m_outpaths_vector(0),
//end of added by monday2000
......
connect(
actionSettings, SIGNAL(triggered(bool)),
this, SLOT(openSettingsDialog())
);
// begin of added by monday2000
connect(
actionExport, SIGNAL(triggered(bool)),
this, SLOT(openExportDialog())
);
// end of added by monday2000
..............

void
MainWindow::timerEvent(QTimerEvent* const event)
{
// begin of added by monday2000
if (event->timerId() == m_exportTimerId)
{
int res = ExportNextFile();

if (res)
{
killTimer(m_exportTimerId);

m_exportTimerId = 0;

if (res == 1)
{
m_p_export_dialog->setExportLabel();

QMessageBox::information(0, "Information", tr("The files export is finished."));
}
}
}
else
{
// end of added by monday2000

// We only use the timer event for delayed closing of the window.
killTimer(event->timerId());

if (closeProjectInteractive()) {
m_closing = true;
QSettings settings;
settings.setValue("mainWindow/maximized", isMaximized());
if (!isMaximized()) {
settings.setValue(
"mainWindow/nonMaximizedGeometry", saveGeometry()
);
}
close();
}
}//added by monday2000
}
.........

// begin of added by monday2000
void
MainWindow::openExportDialog()
{
m_p_export_dialog = new ExportDialog(this);

m_p_export_dialog->setAttribute(Qt::WA_DeleteOnClose);
m_p_export_dialog->setWindowModality(Qt::WindowModal);
m_p_export_dialog->show();

connect(m_p_export_dialog, SIGNAL(ExportOutputSignal(QString, bool, bool)), this, SLOT(ExportOutput(QString, bool, bool)));
connect(m_p_export_dialog, SIGNAL(ExportStopSignal()), this, SLOT(ExportStop()));
connect(m_p_export_dialog, SIGNAL(SetStartExportSignal()), this, SLOT(SetStartExport()));
}


template<typename MixedPixel>
bool GenerateSubscans(QImage& source_img, QImage& subscan1, QImage& subscan2, bool& non_bw_detected)
{
int const width = source_img.width();
int const height = source_img.height();

MixedPixel* source_line = reinterpret_cast<MixedPixel*>(source_img.bits());
int const source_stride = source_img.bytesPerLine() / sizeof(MixedPixel);

subscan1 = QImage(source_img.width(),source_img.height(),QImage::Format_Mono);
subscan2 = QImage(source_img.width(),source_img.height(),source_img.format());

subscan1.setDotsPerMeterX(source_img.dotsPerMeterX());
subscan1.setDotsPerMeterY(source_img.dotsPerMeterY());
subscan2.setDotsPerMeterX(source_img.dotsPerMeterX());
subscan2.setDotsPerMeterY(source_img.dotsPerMeterY());

QVector<QRgb> bw_palette(2);
bw_palette[0] = qRgb(0, 0, 0);
bw_palette[1] = qRgb(255, 255, 255);
subscan1.setColorTable(bw_palette);

if (subscan2.format() == QImage::Format_Indexed8)
{ //createGrayscalePalette() from C:\build\scantailor-0.9.11.1\imageproc\Grayscale.cpp
QVector<QRgb> palette(256);
for (int i = 0; i < 256; ++i) palette[i] = qRgb(i, i, i);
subscan2.setColorTable(palette);
}

uchar* subscan1_line = subscan1.bits();
int const subscan1_stride = subscan1.bytesPerLine();

MixedPixel* subscan2_line = reinterpret_cast<MixedPixel*>(subscan2.bits());
int const subscan2_stride = subscan2.bytesPerLine() / sizeof(MixedPixel);

uint32_t tmp_white_pixel = 0xffffffff;
uint32_t mask_pixel = 0x00ffffff;

MixedPixel mask = static_cast<MixedPixel>(mask_pixel);

bool mixed_detected = false;

for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{ //this line of code was suggested by Tulon:
if ((source_line[x] & mask) == 0 || (source_line[x] & mask) == mask) // BW
{
uint8_t value1 = static_cast<uint8_t>(source_line[x]);
//BW SetPixel from http://djvu-soft.narod.ru/bookscanlib/023.htm
value1 ? subscan1_line[x >> 3] |= (0x80 >> (x & 0x7)) : subscan1_line[x >> 3] &= (0xFF7F >> (x & 0x7));
subscan2_line[x] = static_cast<MixedPixel>(tmp_white_pixel);

mixed_detected = true;
}
else // non-BW
{
uint8_t value = static_cast<uint8_t>(tmp_white_pixel);
//BW SetPixel from http://djvu-soft.narod.ru/bookscanlib/023.htm
value ? subscan1_line[x >> 3] |= (0x80 >> (x & 0x7)) : subscan1_line[x >> 3] &= (0xFF7F >> (x & 0x7));
subscan2_line[x] = source_line[x];

non_bw_detected = true;
}
}
source_line += source_stride;
subscan1_line += subscan1_stride;
subscan2_line += subscan2_stride;
}

return mixed_detected;
}

int
MainWindow::ExportNextFile()
{
if (m_pos_export == m_outpaths_vector.size())
return 1; //all the files are processed

QImage subscan1;
QImage subscan2;

QString text_dir = m_export_dir + "\\1"; //folder for foreground subscans
QString pic_dir = m_export_dir + "\\2"; //folder for background subscans

QString out_file_path = m_outpaths_vector[m_pos_export];

QString st_num = QString::number(m_pos_export+1);

QString name;

for(int j=0;j<4-st_num.length();j++) name += "0";

name += st_num;

QString out_file_path1 = text_dir + "\\" + name + ".tif";
QString out_file_path2 = pic_dir + "\\" + name + ".tif";

QString out_file_path_no_split = m_export_dir + "\\" + name + ".tif";

if (!QFile().exists(out_file_path))
{
QMessageBox::critical(0, "Error", tr("The file") + " \"" + out_file_path + "\" " + tr("is not found") + ".");

return -1;
}

QImage out_img = ImageLoader::load(out_file_path);

if (m_split_subscans)
{
bool mixed_detected;
bool non_bw_detected = false;
bool non_bw;

if (out_img.format() == QImage::Format_Indexed8)
{
mixed_detected = GenerateSubscans<uint8_t>(out_img, subscan1, subscan2, non_bw_detected);
non_bw = true;
}
else if(out_img.format() == QImage::Format_RGB32
|| out_img.format() == QImage::Format_ARGB32)
{
mixed_detected = GenerateSubscans<uint32_t>(out_img, subscan1, subscan2, non_bw_detected);
non_bw = true;
}
else if(out_img.format() == QImage::Format_Mono)
{
TiffWriter::writeImage(out_file_path1, out_img);
non_bw = false;
}

if (non_bw)
{
if (mixed_detected)
{
TiffWriter::writeImage(out_file_path1, subscan1);

if (non_bw_detected) // preventing the generation of blank backgrounds
// in case of pure black-and-white set as "mixed"
TiffWriter::writeImage(out_file_path2, subscan2);
}
else
{
if(out_img.format() == QImage::Format_Mono)
TiffWriter::writeImage(out_file_path1, out_img);
else
TiffWriter::writeImage(out_file_path2, out_img);
}
}
}
else
{
TiffWriter::writeImage(out_file_path_no_split, out_img);
}

m_p_export_dialog->StepProgress();

m_pos_export++;

return 0;
}

void
MainWindow::ExportOutput(QString export_dir_path, bool default_out_dir, bool split_subscans)
{
if (isBatchProcessingInProgress())
{
QMessageBox::critical(0, "Error", tr("Batch processing is in the progress."));

return;
}

if (!isProjectLoaded())
{
QMessageBox::critical(0, "Error", tr("No project is loaded."));

return;
}

m_ptrInteractiveQueue->cancelAndClear();
if (m_ptrBatchQueue.get()) // Should not happen, but just in case.
m_ptrBatchQueue->cancelAndClear();

// Checking whether all the output thumbnails don't have a question mark on them

std::auto_ptr<ThumbnailSequence> ptrThumbSequence;

ptrThumbSequence.reset(new ThumbnailSequence(m_maxLogicalThumbSize));

if (m_ptrThumbnailCache.get()) {
IntrusivePtr<CompositeCacheDrivenTask> const task(
createCompositeCacheDrivenTask(5)
);

ptrThumbSequence->setThumbnailFactory(
IntrusivePtr<ThumbnailFactory>(
new ThumbnailFactory(
m_ptrThumbnailCache,
m_maxLogicalThumbSize, task
)
)
);
}

ptrThumbSequence->reset(
m_ptrPages->toPageSequence(m_ptrStages->filterAt(5)->getView()),
ThumbnailSequence::RESET_SELECTION, currentPageOrderProvider()
);

if (!ptrThumbSequence->AllThumbnailsComplete()) return;

// Getting the output filenames

if (default_out_dir)
{
m_export_dir = m_outFileNameGen.outDir() + "\\export";
}
else
{
m_export_dir = export_dir_path + "\\export";
}
QDir().mkdir(m_export_dir);

QString text_dir = m_export_dir + "\\1"; //folder for foreground subscans
QString pic_dir = m_export_dir + "\\2"; //folder for background subscans

m_split_subscans = split_subscans;

if (split_subscans)
{
QDir().mkdir(text_dir);
QDir().mkdir(pic_dir);
}

std::vector<PageId::SubPage> erase_variations;
erase_variations.reserve(3);

PageSequence const& pages = allPages(); // get all the pages (input pages)

unsigned const count = pages.numPages(); // input pages number

PageId page_id;

m_outpaths_vector.clear();

for (unsigned i = 0; i < count; ++i)
{
page_id = pages.pageAt(i).id();

erase_variations.clear();

switch (page_id.subPage())
{
case PageId::SINGLE_PAGE:
erase_variations.push_back(PageId::LEFT_PAGE);
erase_variations.push_back(PageId::RIGHT_PAGE);
break;
case PageId::LEFT_PAGE:
erase_variations.push_back(PageId::LEFT_PAGE);
break;
case PageId::RIGHT_PAGE:
erase_variations.push_back(PageId::RIGHT_PAGE);
break;
}

BOOST_FOREACH(PageId::SubPage subpage, erase_variations)
{
QString out_file_path = m_outFileNameGen.filePathFor(PageId(page_id.imageId(), subpage));
m_outpaths_vector.append(out_file_path);
}
}

// exporting pages

m_pos_export = 0;

m_p_export_dialog->setCount(m_outpaths_vector.size());

m_exportTimerId = startTimer(0);
}

void
MainWindow::ExportStop()
{
killTimer(m_exportTimerId);

m_exportTimerId = 0;

m_p_export_dialog->reset();

QMessageBox::information(0, "Information", tr("The files export is stopped by the user."));
}

void
MainWindow::SetStartExport()
{
if (isBatchProcessingInProgress())
{
QMessageBox::critical(0, "Error", tr("Batch processing is in the progress."));

return;
}

if (!isProjectLoaded())
{
QMessageBox::critical(0, "Error", tr("No project is loaded."));

return;
}

m_p_export_dialog->setStartExport();
}
// end of added by monday2000
........
void
MainWindow::updateWindowTitle()
{
QString project_name;
if (m_projectFile.isEmpty()) {
project_name = tr("Unnamed");
} else {
project_name = QFileInfo(m_projectFile).baseName();
}
QString const version(QString::fromUtf8(VERSION));
//begin of added by monday2000
setWindowTitle(tr("%2 - Scan Tailor (clone by monday2000) %3 [%1bit]").arg(sizeof(void*)*8).arg(project_name, version));
//end of added by monday2000
}
..........

Сборка:  http://rghost.ru/43427492  (4,5 МБ)

56
DjVu / Re: Программа DjVu Small Mod
« : 29 ПЭТРам 2013, 20:10:30 »
textsharik
Я думаю, что более перспективно не возиться с профилями DjVu - а заняться математическими алгоритмами авто-сегментации - наподобие авто-выделения зон иллюстраций в Скан Тейлоре. Можно разработать ещё аналогичных алгоритмов.

План такой: используя все эти алгоритмы, делать предварительную авто-сегментацию. Затем доводить её вручную до ума в интерактивном режиме (в некоей гипотетической программе, напоминающей Скан Тейлор - также кроссплатформенной и свободно-бесплатной). А потом просто собирать полученные слои в единый DjVu - маску - при помощи documenttodjvu, всё остальное - при помощи DjVuLibre.

Пока это не произошло, в настоящее время лучше, чем просто кодировать сканы цветных журналов в задний фон DjVu ИМХО ничего не придумать. Хотя мне это самому не нравится, я видел "Технику-Молодёжи" в таком варианте - читать такое не хочется, качество всё же низкое. :(

Вот что меня ещё интересует - насколько трудно заставить работать documenttodjvu под Linux? Можно ли будет как-то передавать ей в Linux параметры командной строки?

57
DjVu / Re: Программа DjVu Small Mod
« : 27 ПЭТРам 2013, 21:18:56 »
textsharik
Скачал последнюю версию. Довольно интересно (на вид; пробовать времени нет). Только, на мой взгляд, сложность использования программы значительно повысилась - по сравнению с оригиналом. И словарь я бы оставил на значении 10 - а не так, чтобы один словарь на всю книгу (этот вопрос давно коллективно обсуждался, сам Леон Боту рекомендует не более 10-20).

ИМХО пусть исследования профилей DjVu - бесперспективен. Т.е. попытки заставить автоматическую сегментацию работать "как положено" мне представляются нереальными - для массового использования. Тем более, что исходники documenttodjvu - закрыты.

Лучше развивать направление DjVu Imager, а DjVu Small понизить исключительно до роли инструмента кодирования передних субсканов - и всё.

Опять-таки - принципиальный порок всех коммерческих DjVu-кодировщиков - это закрытость их исходных кодов (и их несвободность). Обойтись без них - пока невозможно, а развивать - по-моему, бессмысленно. Правильнее наборот - свести их использование к минимуму.

58
textsharik
Спасибо, я посмотрю.

59
Я немного перекроил индикацию процесса экспорта. Теперь номер текущего экспортируемого файла выводится в реальном времени. Ещё хочу при запуске экспорта сначала выводить надпись типа "начинаю экспорт" - а то сейчас если нажать кнопку Export - то пока первая страница не обработается - программа ничего не покажет, никакой активности. Добиться такой, казалось бы простой вещи, мне пока не удалось - несмотря на изрядные усилия.

Вот коды исправления:
C:\build\scantailor-0.9.11.1\MainWindow.h
class MainWindow :
............
private:
...........
//begin of added by monday2000
ExportDialog* m_p_export_dialog;
QVector<QString> m_outpaths_vector;
int ExportNextFile();
int m_exportTimerId;
QString m_export_dir;
bool m_split_subscans;
int m_pos_export;
//end of added by monday2000
};

C:\build\scantailor-0.9.11.1\MainWindow.cpp

int
MainWindow::ExportNextFile()
{
if (m_pos_export == m_outpaths_vector.size())
return 1; //all the files are processed

QImage subscan1;
QImage subscan2;

QString text_dir = m_export_dir + "\\1"; //folder for foreground subscans
QString pic_dir = m_export_dir + "\\2"; //folder for background subscans

QString out_file_path = m_outpaths_vector[m_pos_export];

QString st_num = QString::number(m_pos_export+1);

QString name;

for(int j=0;j<4-st_num.length();j++) name += "0";

name += st_num;

QString out_file_path1 = text_dir + "\\" + name + ".tif";
QString out_file_path2 = pic_dir + "\\" + name + ".tif";

QString out_file_path_no_split = m_export_dir + "\\" + name + ".tif";

if (!QFile().exists(out_file_path))
{
QMessageBox::critical(0, "Error", "The file \"" + out_file_path + "\" is not found.");

return -1;
}

QImage out_img = ImageLoader::load(out_file_path);

if (m_split_subscans)
{
bool mixed_detected;
bool non_bw;

if (out_img.format() == QImage::Format_Indexed8)
{
mixed_detected = GenerateSubscans<uint8_t>(out_img, subscan1, subscan2);
non_bw = true;
}
else if(out_img.format() == QImage::Format_RGB32
|| out_img.format() == QImage::Format_ARGB32)
{
mixed_detected = GenerateSubscans<uint32_t>(out_img, subscan1, subscan2);
non_bw = true;
}
else if(out_img.format() == QImage::Format_Mono)
{
TiffWriter::writeImage(out_file_path1, out_img);
non_bw = false;
}

if (non_bw)
{
if (mixed_detected)
{
TiffWriter::writeImage(out_file_path1, subscan1);
TiffWriter::writeImage(out_file_path2, subscan2);
}
else
{
if(out_img.format() == QImage::Format_Mono)
TiffWriter::writeImage(out_file_path1, out_img);
else
TiffWriter::writeImage(out_file_path2, out_img);
}
}
}
else
{
TiffWriter::writeImage(out_file_path_no_split, out_img);
}

m_p_export_dialog->StepProgress();

m_pos_export++;

return 0;
}

void
//MainWindow::ExportOutput(QString export_dir_path, bool default_out_dir, bool split_subscans, ExportDialog* p_export_dialog)
MainWindow::ExportOutput(QString export_dir_path, bool default_out_dir, bool split_subscans)
{
//QMessageBox::information(0, "Information", "Here will be the subscans generation.");

//m_p_export_dialog->clearLabel();
//m_p_export_dialog->setLabelStart();

if (isBatchProcessingInProgress())
{
QMessageBox::critical(0, "Error", "Batch processing is in the progress.");

return;
}

if (!isProjectLoaded())
{
QMessageBox::critical(0, "Error", "No project is loaded.");

return;
}

m_ptrInteractiveQueue->cancelAndClear();
if (m_ptrBatchQueue.get()) // Should not happen, but just in case.
m_ptrBatchQueue->cancelAndClear();

// Checking whether all the output thumbnails don't have a question mark on them

//from void MainWindow::resetThumbSequence(IntrusivePtr<PageOrderProvider const> const& page_order_provider)

std::auto_ptr<ThumbnailSequence> ptrThumbSequence;

ptrThumbSequence.reset(new ThumbnailSequence(m_maxLogicalThumbSize));

if (m_ptrThumbnailCache.get()) {
IntrusivePtr<CompositeCacheDrivenTask> const task(
createCompositeCacheDrivenTask(5)
);

ptrThumbSequence->setThumbnailFactory(
IntrusivePtr<ThumbnailFactory>(
new ThumbnailFactory(
m_ptrThumbnailCache,
m_maxLogicalThumbSize, task
)
)
);
}

ptrThumbSequence->reset(
m_ptrPages->toPageSequence(m_ptrStages->filterAt(5)->getView()),
ThumbnailSequence::RESET_SELECTION, currentPageOrderProvider()
);

if (!ptrThumbSequence->AllThumbnailsComplete()) return;

// Getting the output filenames

if (default_out_dir)
{
m_export_dir = m_outFileNameGen.outDir() + "\\export";
}
else
{
m_export_dir = export_dir_path + "\\export";
}
QDir().mkdir(m_export_dir);

QString text_dir = m_export_dir + "\\1"; //folder for foreground subscans
QString pic_dir = m_export_dir + "\\2"; //folder for background subscans

m_split_subscans = split_subscans;

if (split_subscans)
{
QDir().mkdir(text_dir);
QDir().mkdir(pic_dir);
}

// from void MainWindow::eraseOutputFiles(std::set<PageId> const& pages)
std::vector<PageId::SubPage> erase_variations;
erase_variations.reserve(3);

PageSequence const& pages = allPages(); // get all the pages (input pages?)

unsigned const count = pages.numPages(); // (input pages number?)

PageId page_id;

m_outpaths_vector.clear();

for (unsigned i = 0; i < count; ++i)
{
page_id = pages.pageAt(i).id();

erase_variations.clear();

switch (page_id.subPage())
{
case PageId::SINGLE_PAGE:
//erase_variations.push_back(PageId::SINGLE_PAGE);
erase_variations.push_back(PageId::LEFT_PAGE);
erase_variations.push_back(PageId::RIGHT_PAGE);
break;
case PageId::LEFT_PAGE:
//erase_variations.push_back(PageId::SINGLE_PAGE);
erase_variations.push_back(PageId::LEFT_PAGE);
break;
case PageId::RIGHT_PAGE:
//erase_variations.push_back(PageId::SINGLE_PAGE);
erase_variations.push_back(PageId::RIGHT_PAGE);
break;
}

BOOST_FOREACH(PageId::SubPage subpage, erase_variations)
{
QString out_file_path = m_outFileNameGen.filePathFor(PageId(page_id.imageId(), subpage));
m_outpaths_vector.append(out_file_path);
}
}

// exporting pages

m_pos_export = 0;

m_p_export_dialog->setCount(m_outpaths_vector.size());

m_exportTimerId = startTimer(0);

//QMessageBox::information(0, "Information", "The files export is finished.");
}
// end of added by monday2000

.............

void
MainWindow::timerEvent(QTimerEvent* const event)
{
// begin of added by monday2000
if (event->timerId() == m_exportTimerId)
{
int res = ExportNextFile();

if (res)
{
killTimer(m_exportTimerId);

m_exportTimerId = 0;

if (res == 1)

QMessageBox::information(0, "Information", "The files export is finished.");
}
}
else
{
// end of added by monday2000

// We only use the timer event for delayed closing of the window.
killTimer(event->timerId());

if (closeProjectInteractive()) {
m_closing = true;
QSettings settings;
settings.setValue("mainWindow/maximized", isMaximized());
if (!isMaximized()) {
settings.setValue(
"mainWindow/nonMaximizedGeometry", saveGeometry()
);
}
close();
}
}//added by monday2000
}

Я сделал индикацию прогресс-бара и постраничную при помощи таймерного события - по подсказке Tulon.

Вот сборка:
http://rghost.ru/43355161

60
Одношаговый кодировщик - задумка неплохая, но технически неосуществимая. По крайней мере, пока.

Всё дело в том, что передние субсканы нечем кодировать - кроме как коммерческими DjVu-кодировщиками. Сюда относятся DjVu Small, DjVu Express Professional, Document Express Enterprise with DjVu и т.п.

Всё это хозяйство работает только под Windows. А что делать на Linux? Не использовать же, в самом деле, minidjvu - это всё же слабая программа, и качество её - под большим вопросом. Остаётся 2 варианта (под Linux) - или виртуальная машина для коммерческого DjVu-кодировщика, или легально-бесплатный онлайн-кодировщик Any2DjVu http://any2djvu.djvuzone.org/ .

Any2DjVu, думаю, не стоит воспринимать всерьёз.

Так что, теоретически можно было бы сделать одношаговый кодировщик - но только под Windows. Пока не появится аналог minidjvu, сравнимый реально с коммерческими образцами - мечтать не о чем.

Страницы: 1 2 3 [4] 5 6 ... 72