Автор Тема: Модифицирование Scan Tailor  (Прочитано 49942 раз)

SorokaSV

  • Пользователь
  • **
  • Сообщений: 56
    • Просмотр профиля
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #15 : 23 ПЭТРам 2013, 15:59:35 »
Раз уж Вы занялись ST, то можно пожелание?
Я не могу привыкнуть, что надо сначала включать режим зон заливки, а уж потом менять масштаб. Нельзя ли сделать так, чтобы масштаб не менялся при смене режима (вывод на зоны заливки)?

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #16 : 23 ПЭТРам 2013, 19:29:25 »
SorokaSV
Цитировать
Раз уж Вы занялись ST, то можно пожелание?
К сожалению, я пока не готов реализовывать чужие пожелания. Если только они не совпадут случайно с моими. :) Я намерен сейчас сконцентрироваться на тех местах в СТ, где пользователь теряет наибольшее количество времени - в процессе сканобработки сканов книги. По моему мнению, таких мест сейчас лишь 2:

1. Авто-выделение зон иллюстраций ("зазубрины" и "каверны" - долгое исправление вручную).
2. Dewarping - тут вообще швах. Даже ручной dewarping и то составляется долго, а автоматический - вообще полностью бесполезен.

Этих 2-х позиций мне хватит очень надолго. :)

А Ваше же пожелание явно не относится к категории тех мест, где пользователь теряет слишком много времени. Согласитесь, что разумнее мне взяться за проблемы, отнимающие наибольшее время, нежели чем за проблемы, отнимающие наименьшее время (с учётом ограниченности моих ресурсов времени и сил).

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #17 : 27 ПЭТРам 2013, 12:46:03 »
Я переделал механизм создания разделённых сканов - в соответствии с советами Tulon.

В первом варианте я сделал галку в главном меню "Split mixed" - и вывод делался с разделением по 2 папкам.

Оказалось, что этого недостаточно - потому что мне обязательно было нужно, чтобы вывод был в формате имён 0001.tif, 0002.tif, ...., 0010.tif, .... - этот формат я называю "сплошная нумерация" - он, кстати, и в FineReader применяется, и вообще он очень удобен.

А СТ выводит свои файлы в довольно причудливом формате имён - где указывается левая-правая страницы, и имя исходного скана - например: 0074_1L.tif, 0074_2R.tif.

Раньше я использовал утилиту ST Split - она разделяла вывод СТ на субсканы и заодно переименовывала его в сплошную нумерацию. Когда я встроил разделение сканов в СТ оказалось, что операция переименовывания в сплошную нумерацию повисла в воздухе - в СТ это сделать оказалось нереально (по словам Tulon), и Tulon предложил мне сделать разделение сканов в виде экспорта - причём экспорта в виде сплошной нумерации.

Именно этот вариант я и реализовал в своей новой сборке. Я сделал в главном меню новый пункт - Export..., по нажатию на который открывается окно, где можно указать папку вывода, разделять смешанные сканы на субсканы или нет, и ещё есть опция вывода в папку по умолчанию.

Папка по умолчанию - это автоматически создаваемая папка "export" внутри папки "out". Если пользователь указывает свою папку вывода - то всё равно в ней автоматически создается папка "export" - а уже в неё делается вывод. Это сделано для того, что если какая-то неопытная женщина выберет в качестве папки экспорта "Рабочий стол" - то он заполнился бы сотнями файлов, а так они по-любому окажутся локализованными в одной папке. У меня и в DjVu Small такой же принцип.

Так что в любом случае создаётся папка "export", куда делается вывод.

Если стоит галка "Split mixed output" - то в папке "export" автоматически создаются подпапки с именами "1" и "2" - соответственно для передних и задних субсканов. Если попадается не-"смешанный" скан, то он попадает в папку "1" - если чёрно-белый, или в папку "2" - если серый/цветной. Имена файлов, естественно, присваиваются в сплошной нумерации, и у каждой созданной пары субсканов - одинаковые имена (а папки разные - "1" и "2").

Если галка "Split mixed output" не стоит - то в папку "export" просто выводятся все сканы - но уже в сплошной нумерации.

Я хотел было сделать папки не "1" и "2" - а "text" и "pic" - но отверг этот вариант, потому что в папке "export" при упорядочивании по именам первой оказывается "pic", а "text" - только второй. Да и вообще - что такое "1" и "2" - это и ёжику ясно, а вот что такое "text" и "pic" - это ж надо будет ещё извилину напрячь некоторым юзерам...

При экспорте осуществляются все нужные проверки:

- Загружен ли проект
- Не находится ли в процессе пакетная обработка
- Нет ли знаков вопроса на какой-либо из миниатюр стадии вывода
- Нет ли отсутствующих файлов в папке out
- Если папка вывода - не "по умолчанию", выбрал ли юзер свою папку, есть ли она, не надо ли создать и т.п.

Процесс вывода отображается постранично прогресс-баром. Ещё хотел текстом показывать номер текущей страницы - но пока не получилось, потом буду делать.

Вот коды правок:

Я создал же новое окно - ExportDialog. Вот его файл ExportDialog.ui:
C:\build\scantailor-0.9.11.1\ui\ExportDialog.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>ExportDialog</class>
 <widget class="QDialog" name="ExportDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>486</width>
    <height>202</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Export</string>
  </property>
  <widget class="QCheckBox" name="SplitMixed">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>10</y>
     <width>381</width>
     <height>18</height>
    </rect>
   </property>
   <property name="text">
    <string>Split mixed output</string>
   </property>
  </widget>
  <widget class="QPushButton" name="ExportButton">
   <property name="geometry">
    <rect>
     <x>297</x>
     <y>170</y>
     <width>101</width>
     <height>25</height>
    </rect>
   </property>
   <property name="text">
    <string>Export</string>
   </property>
  </widget>
  <widget class="QPushButton" name="OkButton">
   <property name="geometry">
    <rect>
     <x>400</x>
     <y>170</y>
     <width>77</width>
     <height>25</height>
    </rect>
   </property>
   <property name="text">
    <string>OK</string>
   </property>
  </widget>
  <widget class="QCheckBox" name="DefaultOutputFolder">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>40</y>
     <width>381</width>
     <height>18</height>
    </rect>
   </property>
   <property name="text">
    <string>Default output folder</string>
   </property>
  </widget>
  <widget class="QGroupBox" name="groupBoxExport">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>70</y>
     <width>464</width>
     <height>60</height>
    </rect>
   </property>
   <property name="title">
    <string>Output Directory</string>
   </property>
   <layout class="QHBoxLayout" name="ExportLayout">
    <item>
     <widget class="QLineEdit" name="outExportDirLine"/>
    </item>
    <item>
     <widget class="QPushButton" name="outExportDirBrowseBtn">
      <property name="text">
       <string>Browse</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QProgressBar" name="progressBar">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>140</y>
     <width>464</width>
     <height>21</height>
    </rect>
   </property>
   <property name="value">
    <number>0</number>
   </property>
   <property name="textVisible">
    <bool>false</bool>
   </property>
  </widget>
  <widget class="QLabel" name="labelFilesProcessed">
   <property name="geometry">
    <rect>
     <x>10</x>
     <y>173</y>
     <width>281</width>
     <height>20</height>
    </rect>
   </property>
   <property name="text">
    <string>TextLabel</string>
   </property>
  </widget>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>OkButton</sender>
   <signal>clicked()</signal>
   <receiver>ExportDialog</receiver>
   <slot>accept()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>438</x>
     <y>162</y>
    </hint>
    <hint type="destinationlabel">
     <x>242</x>
     <y>89</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>


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

/*
    Scan Tailor - Interactive post-processing tool for scanned pages.
    Copyright (C) 2007-2009  Joseph Artsimovich <joseph_a@mail.ru>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
// This file was added by monday2000

#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();

signals:
    //void ExportOutputSignal(QString export_dir_path, bool default_out_dir, bool split_subscans, ExportDialog* p_ed);
void ExportOutputSignal(QString export_dir_path, bool default_out_dir, bool split_subscans);

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

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

/*
    Scan Tailor - Interactive post-processing tool for scanned pages.
    Copyright (C)  Joseph Artsimovich <joseph.artsimovich@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
// This file was added by monday2000

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

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

QSettings settings;

// begin of added
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->setText("");
// end of added
}

ExportDialog::~ExportDialog()
{
}

// begin of added
void
ExportDialog::OnCheckSplitMixed(bool state)
{
QSettings settings;

//QMessageBox::information(0, "Information", "OnCheckSplitMixed");

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

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

//QMessageBox::information(0, "Information", "OnCheckSplitMixed");

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

void
ExportDialog::OnClickExport()
{
//QMessageBox::information(0, "Information", "ExportDialog::OnClickGenerateSubscans()");

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();
m_count = 0;

ui.progressBar->setValue(0);
ui.labelFilesProcessed->setText("");

emit ExportOutputSignal(export_dir_path, default_out_dir, split_subscans);
}

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.progressBar->setValue(ui.progressBar->value() + 1);
ui.labelFilesProcessed->setText("Processed file " +
QString::number(ui.progressBar->value()) + " of " + QString::number(m_count));
}

// end of added

Для того, чтобы добавить в проект новые файлы с новым классом, по совету Tulon пришлось добавить упоминание о новом классе - такое:

C:\build\scantailor-0.9.11.1\CMakeLists.txt

SET(
gui_only_sources
.....
SettingsDialog.cpp SettingsDialog.h
ExportDialog.cpp ExportDialog.h
........


После этого понадобилось 2-3 перекомпиляции - пока сгенерировались автоматически нужные файлы привязки.
« Последнее редактирование: 27 ПЭТРам 2013, 13:05:29 от monday2000 »

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #18 : 27 ПЭТРам 2013, 12:55:10 »
А вот основной код исправления:
C:\build\scantailor-0.9.11.1\ThumbnailSequence.h

#include <QMessageBox> //added

class ThumbnailSequence : public QObject
{
.......
public:
........
bool AllThumbnailsComplete();//added
.......

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

class ThumbnailSequence::Impl
{
public:
.......
bool AllThumbnailsComplete();//added
........


// begin of added by monday2000
bool
ThumbnailSequence::Impl::AllThumbnailsComplete()
{
ItemsInOrder::iterator ord_it(m_itemsInOrder.begin());
ItemsInOrder::iterator const ord_end(m_itemsInOrder.end());
for (; ord_it != ord_end; ++ord_it)
{
if (ord_it->composite->incompleteThumbnail())
{
//QMessageBox::information(0, "Information", "not ready");

PageInfo page = ord_it->pageInfo;

QString name(QFileInfo(page.imageId().filePath()).completeBaseName());

QString sub_page = page.id().subPageAsString();

QString show_name = name + ".tif - " + sub_page;

QMessageBox::warning(0,"Warning", "The file \"" + show_name + "\" is not ready for output.");

return false;
}
}

return true;
}

bool
ThumbnailSequence::AllThumbnailsComplete()
{
return m_ptrImpl->AllThumbnailsComplete();
}
// end of added by monday2000

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:
......
void ExportOutput(QString export_dir_path, bool default_out_dir, bool split_subscans);// added
......
private slots:
.......
void openExportDialog();//added
.......
private:
......
ExportDialog* m_p_export_dialog;//added
.......

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

MainWindow::MainWindow()
.......
// begin of added by monday2000
connect(
actionExport, SIGNAL(triggered(bool)),
this, SLOT(openExportDialog())
);
// end of 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)));
}


template<typename MixedPixel>
bool GenerateSubscans(QImage& source_img, QImage& subscan1, QImage& subscan2)
{
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>(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];
}
}
source_line += source_stride;
subscan1_line += subscan1_stride;
subscan2_line += subscan2_stride;
}

return mixed_detected;
}

void

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

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

QString export_dir;

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

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

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

QImage out_img;
QImage subscan1;
QImage subscan2;

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

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

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

PageId page_id;
QString out_file_path;

// counting the total number of export pages

int number_pages = 0;

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)
number_pages++; // counting the total number of export pages
}

m_p_export_dialog->setCount(number_pages);

// exporting pages

int page_num = 1;

for (unsigned i = 0; i < count; ++i)
{
//pages.pageAt(i).imageId().filePath();
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)
{
out_file_path = m_outFileNameGen.filePathFor(PageId(page_id.imageId(), subpage));

QString st_num = QString::number(page_num);

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 = export_dir + "\\" + name + ".tif";

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

return;
}

out_img = ImageLoader::load(out_file_path);

if (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);
}
page_num++;

m_p_export_dialog->StepProgress();
}
}

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

Вот сборка: http://rghost.ru/43343073   (4,5 МБ)

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #19 : 27 ПЭТРам 2013, 20:53:11 »
Я немного перекроил индикацию процесса экспорта. Теперь номер текущего экспортируемого файла выводится в реальном времени. Ещё хочу при запуске экспорта сначала выводить надпись типа "начинаю экспорт" - а то сейчас если нажать кнопку 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

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #20 : 30 ПЭТРам 2013, 21:15:30 »
Очередное исправление экспорта разделённых сканов. Внесены некоторые улучшения:

- При нажатии на кнопку "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 МБ)
« Последнее редактирование: 30 ПЭТРам 2013, 21:34:55 от monday2000 »

NBell

  • Постоялец
  • ***
  • Сообщений: 173
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #21 : 31 ПЭТРам 2013, 15:28:39 »
Tulon наверное имеет суперустойчивую ОС, как и Вы. Автосохранения проекта как не было так и нет.
Как Вы думаете - оно Вам нужно? Вдруг сбой и результаты правки зон и прочего книжки страниц на 500 придется повторить?

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #22 : 31 ПЭТРам 2013, 22:16:58 »
NBell
Всё, что пока могу предложить - вырабатывайте у себя привычку почаще нажимать Ctrl+S.

NBell

  • Постоялец
  • ***
  • Сообщений: 173
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #23 : 01 ДХТаРЫм 2013, 14:17:37 »
Ясно. так, глядишь, и Си выучу.

N.M.E.

  • Пользователь
  • **
  • Сообщений: 87
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #24 : 01 ДХТаРЫм 2013, 22:02:50 »
почаще нажимать Ctrl+S.
теоретически, на автоматическое нажатие Ctrl+S можно в программе какой-нибудь экшен по таймеру заделать.. хотя, абсолютной уверенности нет, ибо ни в с++, ни в Scan Tailor'e практически ничего не понимаю..

N.M.E.

  • Пользователь
  • **
  • Сообщений: 87
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #25 : 01 ДХТаРЫм 2013, 22:15:39 »
DikBSD вроде делал автосохранение, но, судя по описанию, как-то сложно там получается..

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #26 : 01 ДХТаРЫм 2013, 23:50:06 »
Исправил баг с Руборда:
Цитировать
Зато появился новый (?) баг на версии 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
« Последнее редактирование: 01 ДХТаРЫм 2013, 23:56:11 от monday2000 »

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #27 : 03 ДХТаРЫм 2013, 00:10:08 »
Я скачал из 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

NBell

  • Постоялец
  • ***
  • Сообщений: 173
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #28 : 03 ДХТаРЫм 2013, 09:43:54 »
Вам удобно включать Dewarp через окно? В повороте страниц неплохо добавлены 2 кнопки "авто" и "Вручную" - вот это классно!
И это!
С SHIFT ваш мод просто солнце любит!

P.S. Обычно ортогональность включается с SHIFT (MS Office, Ps, м.б. другое ПО), а CTRL отвечает за включение копирования. Это ИМХО. Такие программы легко освоить - все уже знакомо.
« Последнее редактирование: 03 ДХТаРЫм 2013, 09:47:14 от NBell »

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #29 : 03 ДХТаРЫм 2013, 10:44:55 »
NBell
Цитировать
P.S. Обычно ортогональность включается с SHIFT (MS Office, Ps, м.б. другое ПО), а CTRL отвечает за включение копирования.
Мне больше нравится с Ctrl, потому что его проще нажимать, нежели чем Shift. Ctrl можно нажимать вообще не глядя - это крайняя левая кнопка клавиатуры. А Shift надо нажимать прицельно - это менее удобно.