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

monday2000

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

Собрался СТ легко, в точном соответствии с инструкцией по сборке http://scantailor.git.sourceforge.net/git/gitweb.cgi?p=scantailor/scantailor;a=blob_plain;f=packaging/windows/readme.ru.txt

С помощью автора программы Tulon'а я сделал пока что такие исправления:

Оба исправления касаются ручного dewarping.
 
1. Когда в окне dewarping создаётся синяя сетка, то на её самой верхней и самой нижней синих горизонтальных линиях рисуется по 5 красных точек. Это неудобно - мне нужны лишь 2 - самая левая и самая правая, остальные 3 я всегда вручную убираю - прежде чем ставить свои красные точки. Вот код исправления:
 
C:\build\scantailor-0.9.11.1\filters\output\DewarpingView.cpp
 
void
DewarpingView::initNewSpline(XSpline& spline, QPointF const& p1, QPointF const& p2)
{
    QLineF const line(p1, p2);
    spline.appendControlPoint(line.p1(), 0);
    //spline.appendControlPoint(line.pointAt(1.0/4.0), 1);
    //spline.appendControlPoint(line.pointAt(2.0/4.0), 1);
    //spline.appendControlPoint(line.pointAt(3.0/4.0), 1);
    spline.appendControlPoint(line.p2(), 0);
}

закомментировано создание 3 ненужных мне красных точек
 
2. Когда в режиме распрямления строк "Отключено" начинаешь вручную менять положение синих линий / красных точек - то режим распрямления строк не переключается при этом сам с "Отключено" на "Вручную". Приходится потом лезть в диалог и переключать самому - а это лишние телодвижения. Повторенные на десятках страниц, они начинают раздражать. В общем, вот код исправления:
 
C:\build\scantailor-0.9.11.1\filters\output\OptionsWidget.cpp
 
void
OptionsWidget::distortionModelChanged(dewarping::DistortionModel const& model)
{
    m_ptrSettings->setDistortionModel(m_pageId, model);
   
    // Note that OFF remains OFF while AUTO becomes MANUAL.
    /*if (m_dewarpingMode == DewarpingMode::AUTO)*/ {
   
        m_ptrSettings->setDewarpingMode(m_pageId, DewarpingMode::MANUAL);
        m_dewarpingMode = DewarpingMode::MANUAL;
        updateDewarpingDisplay();
    }
}

Закомментировано мною - комментариями вида /* .... */

3. Перемещение всей самой верхней (нижней) синей горизонтальной линии за мышью за её крайнюю (левую или правую) красную точку. Работает это так: В окне dewarping, там, где синяя сетка, можно, нажав и удерживая Ctrl, двигать за крайнюю красную точку всю её горизонтальную синюю линию. Это даёт небольшое удобство при ручном dewarping.

Вот код исправления:

C:\build\scantailor-0.9.11.1\interaction\DraggableObject.h
 
class DraggableObject
{
public:
.........
 
virtual void dragContinuation(QPointF const& mouse_pos) {   
        m_dragContinuationCallback(mouse_pos);
    }
// добавляем перегруженную функцию - с маской нажатого Ctrl
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask) {}
.........
}
 
C:\build\scantailor-0.9.11.1\interaction\DraggablePoint.h
 
class DraggablePoint : public DraggableObject
{
public:
........
typedef boost::function<
    //void (QPointF const&)
    void (QPointF const&, Qt::KeyboardModifiers mask)
> MoveRequestCallback;
 
............
//virtual void dragContinuation(QPointF const& mouse_pos);
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask);
...........
protected:
............
//virtual void pointMoveRequest(QPointF const& widget_pos) {
virtual void pointMoveRequest(QPointF const& widget_pos, Qt::KeyboardModifiers mask) {
    //m_moveRequestCallback(widget_pos);
    m_moveRequestCallback(widget_pos, mask);
}
..........
}
 
C:\build\scantailor-0.9.11.1\interaction\DraggablePoint.cpp
 
void
//DraggablePoint::dragContinuation(QPointF const& mouse_pos)
DraggablePoint::dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask)
{
    //pointMoveRequest(mouse_pos + m_pointRelativeToMouse);
    pointMoveRequest(mouse_pos + m_pointRelativeToMouse, mask);
}
 
C:\build\scantailor-0.9.11.1\interaction\ObjectDragHandler.cpp
 
void
ObjectDragHandler::onMouseMoveEvent(
    QMouseEvent* event, InteractionState& interaction)
{
    if (interaction.capturedBy(m_interaction)) {
        //m_pObj->dragContinuation(QPointF(0.5, 0.5) + event->pos());
        m_pObj->dragContinuation(QPointF(0.5, 0.5) + event->pos(), event->modifiers());
    }
}
 
C:\build\scantailor-0.9.11.1\interaction\InteractiveXSpline.h
 
class InteractiveXSpline : public InteractionHandler
{
...........
private:
...........
//void controlPointMoveRequest(int idx, QPointF const& pos);
void controlPointMoveRequest(int idx, QPointF const& pos, Qt::KeyboardModifiers mask);
...........
}
 
C:\build\scantailor-0.9.11.1\interaction\InteractiveXSpline.cpp
 
void
InteractiveXSpline::setSpline(XSpline const& spline)
{
..........
 
new_control_points[i].point.setMoveRequestCallback(
    //boost::bind(&InteractiveXSpline::controlPointMoveRequest, this, i, _1)
    boost::bind(&InteractiveXSpline::controlPointMoveRequest, this, i, _1, _2) // это самое сложное место - но мне его Tulon подсказал
..........
}
 
void
//InteractiveXSpline::controlPointMoveRequest(int idx, QPointF const& pos)
InteractiveXSpline::controlPointMoveRequest(int idx, QPointF const& pos, Qt::KeyboardModifiers mask)
{
........
 
(mc(mat, 2, 2)*mc(pt, 2, 1)).write(pt);   
//m_spline.moveControlPoint(i, pt + origin); // original line - now commented
//начало добавления
if (mask != Qt::ControlModifier) // default behavior
    m_spline.moveControlPoint(i, pt + origin);
else // Control key is currently pressed
{               
    Vec2d shift_y = storage_pt - old_pos;               
    QPointF new_position = m_spline.controlPointPosition(i) + shift_y;               
    new_position.setX(m_spline.controlPointPosition(i).x());               
    m_spline.moveControlPoint(i, new_position);
}
//конец добавления
}
} else {
.........
}

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #1 : 14 ПЭТРам 2013, 20:39:22 »
Я добавил в (свою копию) Scan Tailor генерацию разделённых субсканов - для "смешанных" (Mixed) сканов. Т.е. это то, для чего я сделал в своё время программу ST Split - которая теперь становится (наконец-таки) ненужной. Вот код исправления:
C:\build\scantailor-0.9.11.1\ui\MainWindow.ui

<widget class="QMenu" name="menuDebug">
    <property name="title">
     <string>Tools</string>
    </property>
    <addaction name="actionFixDpi"/>
    <addaction name="actionRelinking"/>
    <addaction name="separator"/>
    <addaction name="actionDebug"/>
    <addaction name="separator"/>
    <addaction name="actionSettings"/>
    <addaction name="actionSplitMixed"/> // added
   </widget>
..........

<action name="actionDebug">
   <property name="checkable">
    <bool>true</bool>
   </property>
   <property name="checked">
    <bool>false</bool>
   </property>
   <property name="text">
    <string>Debug Mode</string>
   </property>
  </action>
  <action name="actionSplitMixed"> // the whole node "actionSplitMixed" is added
    <property name="checkable">
      <bool>true</bool>
    </property>
    <property name="checked">
      <bool>false</bool>
    </property>
    <property name="text">
      <string>Split mixed</string>
    </property>
  </action>

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

Now compile the whole solution to make sure that in
C:\build\scantailor-build\ui_MainWindow.h
is automatically generated this:

class Ui_MainWindow
{
public:
    QAction *actionDebug;
    QAction *actionSplitMixed; // auto-generated from C:\build\scantailor-0.9.11.1\ui\MainWindow.ui
...............................

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

BackgroundTaskPtr
MainWindow::createCompositeTask(
PageInfo const& page, int const last_filter_idx, bool const batch, bool debug)
{
.................
if (last_filter_idx >= m_ptrStages->outputFilterIdx()) {
output_task = m_ptrStages->outputFilter()->createTask(
//page.id(), m_ptrThumbnailCache, m_outFileNameGen, batch, debug
page.id(), m_ptrThumbnailCache, m_outFileNameGen, batch, debug, actionSplitMixed->isChecked()
);
debug = false;
.................

C:\build\scantailor-0.9.11.1\filters\output\Filter.h

class Filter : public AbstractFilter
{
DECLARE_NON_COPYABLE(Filter)
public:
...........
IntrusivePtr<Task> createTask(
PageId const& page_id,
IntrusivePtr<ThumbnailPixmapCache> const& thumbnail_cache,
OutputFileNameGenerator const& out_file_name_gen,
//bool batch, bool debug);
bool batch, bool debug, bool split_mixed);
........................

C:\build\scantailor-0.9.11.1\filters\output\Filter.cpp

IntrusivePtr<Task>
Filter::createTask(
PageId const& page_id,
IntrusivePtr<ThumbnailPixmapCache> const& thumbnail_cache,
OutputFileNameGenerator const& out_file_name_gen,
//bool const batch, bool const debug)
bool const batch, bool const debug, bool split_mixed)
{
ImageViewTab lastTab(TAB_OUTPUT);
if (m_ptrOptionsWidget.get() != 0)
lastTab = m_ptrOptionsWidget->lastTab();
return IntrusivePtr<Task>(
new Task(
IntrusivePtr<Filter>(this), m_ptrSettings,
thumbnail_cache, page_id, out_file_name_gen,
//lastTab, batch, debug
lastTab, batch, debug, split_mixed
)
);
}
........................

C:\build\scantailor-0.9.11.1\filters\output\Task.h

class Task : public RefCountable
{
DECLARE_NON_COPYABLE(Task)
public:
Task(IntrusivePtr<Filter> const& filter,
IntrusivePtr<Settings> const& settings,
IntrusivePtr<ThumbnailPixmapCache> const& thumbnail_cache,
PageId const& page_id, OutputFileNameGenerator const& out_file_name_gen,
//ImageViewTab last_tab, bool batch, bool debug);
ImageViewTab last_tab, bool batch, bool debug, bool split_mixed);

virtual ~Task();

FilterResultPtr process(
TaskStatus const& status, FilterData const& data,
QPolygonF const& content_rect_phys);
bool m_split_mixed;//added
private:
........................

C:\build\scantailor-0.9.11.1\filters\output\Task.cpp

Task::Task(IntrusivePtr<Filter> const& filter,
IntrusivePtr<Settings> const& settings,
IntrusivePtr<ThumbnailPixmapCache> const& thumbnail_cache,
PageId const& page_id, OutputFileNameGenerator const& out_file_name_gen,
//ImageViewTab const last_tab, bool const batch, bool const debug)
ImageViewTab const last_tab, bool const batch, bool const debug, bool split_mixed)
: m_ptrFilter(filter),
m_ptrSettings(settings),
m_ptrThumbnailCache(thumbnail_cache),
m_pageId(page_id),
m_outFileNameGen(out_file_name_gen),
m_lastTab(last_tab),
m_batchProcessing(batch),
//m_debug(debug)
m_debug(debug),
m_split_mixed(split_mixed)//added
{
if (debug) {
m_ptrDbg.reset(new DebugImages);
}
}

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

// added by monday2000
template<typename MixedPixel>
void 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;
MixedPixel mask = static_cast<MixedPixel>(0x00ffffff);

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);
}
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;
}
}
// end of added by monday2000

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

FilterResultPtr
Task::process(
TaskStatus const& status, FilterData const& data,
QPolygonF const& content_rect_phys)
{
.....................

if (!need_reprocess) {
..................

if (write_speckles_file && speckles_img.isNull()) {
// Even if despeckling didn't actually take place, we still need
// to write an empty speckles file.  Making it a special case
// is simply not worth it.
BinaryImage(out_img.size(), WHITE).swap(speckles_img);
}

bool invalidate_params = false;
// start of modification by monday2000
//if (!TiffWriter::writeImage(out_file_path, out_img)) {
// invalidate_params = true;  
//} else {
// deleteMutuallyExclusiveOutputFiles();
//}
// if Mixed Output and Split Mixed then
// if an output file has picture zones split it into 2 subscans.
QString const file_name(m_outFileNameGen.fileNameFor(m_pageId));
QString pic_dir = m_outFileNameGen.outDir() + "\\pic"; //folder for background subscans
QString subscan2_path = QDir(pic_dir).absoluteFilePath(file_name); //file of the background subscan

QImage subscan1;
QImage subscan2;

if (render_params.mixedOutput() && m_split_mixed)
{
if (out_img.format() == QImage::Format_Indexed8)
GenerateSubscans<uint8_t>(out_img, subscan1, subscan2);

else {
assert(out_img.format() == QImage::Format_RGB32
|| out_img.format() == QImage::Format_ARGB32);

GenerateSubscans<uint32_t>(out_img, subscan1, subscan2);
}

//if (!TiffWriter::writeImage(out_file_path, out_img)) {
if (!TiffWriter::writeImage(out_file_path, subscan1)) {
invalidate_params = true;
} else {
QDir().mkdir(pic_dir);
TiffWriter::writeImage(subscan2_path, subscan2);

deleteMutuallyExclusiveOutputFiles();
}
}
else
{
if (!TiffWriter::writeImage(out_file_path, out_img)) {
invalidate_params = true;  
} else {
deleteMutuallyExclusiveOutputFiles();
if (QFile().exists(subscan2_path))
QFile().remove(subscan2_path);
}
}
// end of added

if (write_automask) {
.....................

Работает это примерно так:

В (моей копии) Scan Tailor появился новый пункт в меню: Инструменты - Split mixed. Это - помечаемый пункт меню, т.е. на нём можно выставить галку (обозначающую как бы "включено").

Установленная галка означает, что режим вывода разделённых сканов включён, снятая - обычное поведение программы.

Разделение сканов осуществляется функцией GenerateSubscans, вызываемой (в случае, если стоит галка Split mixed) непосредственно перед записью готового обработанного скана в выходной TIF-файл. Т.е. другими словами, разделение субсканов происходит самой последней операцией - прямо перед записью в выходной файл. Передний субскан записывается вместо обычного скана - под тем же именем, только (естественно) в чёрно-белом режиме, в папке out создаётся папка "pic", и туда записываются соответствующие задние субсканы - одноимённые передним.

Всё это почти не тестировалось - так что глюки и падения не исключаются.

Вот собранный с этой фичей Scan Tailor:

http://rghost.ru/43030118 (4,51 МБ)

yuree

  • Постоялец
  • ***
  • Сообщений: 172
    • Просмотр профиля
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #2 : 14 ПЭТРам 2013, 23:07:17 »
Па-си-ба! Буду завтра тестить на работе, там у меня хрюша)

ПыСы. Потестил сейчас дома, на семёрке 32-й, идёт без лагов и вылетов.
« Последнее редактирование: 15 ПЭТРам 2013, 00:01:42 от yuree »

$Shorox

  • Новичок
  • *
  • Сообщений: 1
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #3 : 15 ПЭТРам 2013, 00:51:30 »
monday2000,
В вашей версии у меня частично не работает регулировка "Вручную" в "Полезная область".  Работает она только если цеплять мышью за уголок полезной области (т.е. если цепляешь мышью за углы). Если цеплять за стороны, то они не регулируются.
OS Windows 7x64
« Последнее редактирование: 15 ПЭТРам 2013, 00:53:09 от $Shorox »

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #4 : 15 ПЭТРам 2013, 21:12:01 »
$Shorox
Спасибо, у меня такая же точно проблема. Посмотрел по исходникам и нашёл решение довольно быстро. Этот глюк возник из-за фичи "перемещение линии с Ctrl" - я добавил параметр маски в класс DraggableObject и в дочерний ему класс DraggablePoint (точнее, в виртуальную функцию класса DraggableObject), но я не учёл, что у класса DraggableObject есть ещё один дочерний ему класс - DraggableLineSegment. И его надо было тоже изменить аналогично изменениям в DraggablePoint. Вот эти изменения:
C:\build\scantailor-0.9.11.1\interaction\DraggableLineSegment.h

class DraggableLineSegment : public DraggableObject
{
public:
..........
typedef boost::function<
//void (QLineF const& line)
void (QLineF const& line, Qt::KeyboardModifiers mask)
> MoveRequestCallback;
..........

//virtual void dragContinuation(QPointF const& mouse_pos);
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask);
.............
protected:
..............
//virtual void lineSegmentMoveRequest(QLineF const& line) {
virtual void lineSegmentMoveRequest(QLineF const& line, Qt::KeyboardModifiers mask) {
//m_moveRequestCallback(line);
m_moveRequestCallback(line, mask);
}

C:\build\scantailor-0.9.11.1\interaction\DraggableLineSegment.cpp

void
//DraggableLineSegment::dragContinuation(QPointF const& mouse_pos)
DraggableLineSegment::dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask)
{
//lineSegmentMoveRequest(m_initialLinePos.translated(mouse_pos - m_initialMousePos));
lineSegmentMoveRequest(m_initialLinePos.translated(mouse_pos - m_initialMousePos), mask);
}
Да, ну и в DraggableObject я убрал добавленную мною перегруженную виртуальную функцию - и просто добавил в имеющуюся реквизит маски:
C:\build\scantailor-0.9.11.1\interaction\DraggableObject.h

class DraggableObject
{
public:
.........

//virtual void dragContinuation(QPointF const& mouse_pos) {
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask) {
m_dragContinuationCallback(mouse_pos);
}
.........
}

В общем, инструкция по фиче  "перемещение линии с Ctrl" обновлена, и  полностью она теперь выглядит так:
C:\build\scantailor-0.9.11.1\interaction\DraggableObject.h

class DraggableObject
{
public:
.........

//virtual void dragContinuation(QPointF const& mouse_pos) {
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask) {
m_dragContinuationCallback(mouse_pos);
}
.........
}

C:\build\scantailor-0.9.11.1\interaction\DraggablePoint.h

class DraggablePoint : public DraggableObject
{
public:
........
typedef boost::function<
//void (QPointF const&)
void (QPointF const&, Qt::KeyboardModifiers mask)
> MoveRequestCallback;

............
//virtual void dragContinuation(QPointF const& mouse_pos);
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask);
...........
protected:
............
//virtual void pointMoveRequest(QPointF const& widget_pos) {
virtual void pointMoveRequest(QPointF const& widget_pos, Qt::KeyboardModifiers mask) {
//m_moveRequestCallback(widget_pos);
m_moveRequestCallback(widget_pos, mask);
}
..........
}

C:\build\scantailor-0.9.11.1\interaction\DraggablePoint.cpp

void
//DraggablePoint::dragContinuation(QPointF const& mouse_pos)
DraggablePoint::dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask)
{
//pointMoveRequest(mouse_pos + m_pointRelativeToMouse);
pointMoveRequest(mouse_pos + m_pointRelativeToMouse, mask);
}

C:\build\scantailor-0.9.11.1\interaction\ObjectDragHandler.cpp

void
ObjectDragHandler::onMouseMoveEvent(
QMouseEvent* event, InteractionState& interaction)
{
if (interaction.capturedBy(m_interaction)) {
//m_pObj->dragContinuation(QPointF(0.5, 0.5) + event->pos());
m_pObj->dragContinuation(QPointF(0.5, 0.5) + event->pos(), event->modifiers());
}
}

C:\build\scantailor-0.9.11.1\interaction\InteractiveXSpline.h

class InteractiveXSpline : public InteractionHandler
{
...........
private:
...........
//void controlPointMoveRequest(int idx, QPointF const& pos);
void controlPointMoveRequest(int idx, QPointF const& pos, Qt::KeyboardModifiers mask);
...........
}

C:\build\scantailor-0.9.11.1\interaction\InteractiveXSpline.cpp

void
InteractiveXSpline::setSpline(XSpline const& spline)
{
..........

new_control_points[i].point.setMoveRequestCallback(
//boost::bind(&InteractiveXSpline::controlPointMoveRequest, this, i, _1)
boost::bind(&InteractiveXSpline::controlPointMoveRequest, this, i, _1, _2)
..........
}

void
//InteractiveXSpline::controlPointMoveRequest(int idx, QPointF const& pos)
InteractiveXSpline::controlPointMoveRequest(int idx, QPointF const& pos, Qt::KeyboardModifiers mask)
{
........

(mc(mat, 2, 2)*mc(pt, 2, 1)).write(pt);
//m_spline.moveControlPoint(i, pt + origin); // original line - now commented
//начало добавления
if (mask != Qt::ControlModifier) // default behavior
m_spline.moveControlPoint(i, pt + origin);
else // Control key is currently pressed
{
Vec2d shift_y = storage_pt - old_pos;
QPointF new_position = m_spline.controlPointPosition(i) + shift_y;
new_position.setX(m_spline.controlPointPosition(i).x());
m_spline.moveControlPoint(i, new_position);
}
//конец добавления
}
} else {
.........
}

C:\build\scantailor-0.9.11.1\interaction\DraggableLineSegment.h

class DraggableLineSegment : public DraggableObject
{
public:
..........
typedef boost::function<
//void (QLineF const& line)
void (QLineF const& line, Qt::KeyboardModifiers mask)
> MoveRequestCallback;
..........

//virtual void dragContinuation(QPointF const& mouse_pos);
virtual void dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask);
.............
protected:
..............
//virtual void lineSegmentMoveRequest(QLineF const& line) {
virtual void lineSegmentMoveRequest(QLineF const& line, Qt::KeyboardModifiers mask) {
//m_moveRequestCallback(line);
m_moveRequestCallback(line, mask);
}

C:\build\scantailor-0.9.11.1\interaction\DraggableLineSegment.cpp

void
//DraggableLineSegment::dragContinuation(QPointF const& mouse_pos)
DraggableLineSegment::dragContinuation(QPointF const& mouse_pos, Qt::KeyboardModifiers mask)
{
//lineSegmentMoveRequest(m_initialLinePos.translated(mouse_pos - m_initialMousePos));
lineSegmentMoveRequest(m_initialLinePos.translated(mouse_pos - m_initialMousePos), mask);
}

Вот перекомпилированный ST - уже подправленный:
http://rghost.ru/43062070
Кстати, там не только полезная область не двигалась - но и резак тоже.
Что-то с переводом не то - он кое-где слетел в моей сборке СТ, и консольную версию я не собираю - а там тоже надо что-то подправить - под мои фичи новые (если собирать консольную версию). Ну и tif-библиотеки я брал обычные при сборке СТ - а не те из главы по сборке СТ "Патчим libtiff", да и кое-какие проекты я поотключал при сборке СТ - чтобы быстрее компилился.

Короче, моя сборка СТ - это больше как прототип пока, если делать её всерьёз - надо взять снова чистые исходники, всё как положено там сделать, накатить аккуратно все правки и т.п.
« Последнее редактирование: 15 ПЭТРам 2013, 22:51:57 от monday2000 »

monday2000

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

Вот коды исправления:
C:\build\scantailor-0.9.11.1\zones\ZoneCreationInteraction.h

class ZoneCreationInteraction : public InteractionHandler
{
Q_DECLARE_TR_FUNCTIONS(ZoneCreationInteraction)
public:
..........
bool m_ctrl;//added
..........
private:
..........
QPointF m_nextVertexImagePos_mid1;//added
QPointF m_nextVertexImagePos_mid2;//added
}

C:\build\scantailor-0.9.11.1\zones\ZoneCreationInteraction.cpp

void
ZoneCreationInteraction::onPaint(QPainter& painter, InteractionState const& interaction)
{
........

painter.setPen(solid_line_pen);
painter.setBrush(Qt::NoBrush);

//begin of added by monday2000

if(m_nextVertexImagePos != m_nextVertexImagePos_mid1 &&
m_nextVertexImagePos != m_nextVertexImagePos_mid2 && m_ctrl)
{
m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos_mid1), m_visualizer.highlightBrightColor()
);

m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos_mid2), m_visualizer.highlightBrightColor()
);


QLineF const line1_mid1(
to_screen.map(QLineF(m_ptrSpline->lastVertex()->point(), m_nextVertexImagePos_mid1))
);
gradient.setStart(line1_mid1.p1());
gradient.setFinalStop(line1_mid1.p2());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line1_mid1);


QLineF const line2_mid1(
to_screen.map(QLineF(m_nextVertexImagePos_mid1, m_nextVertexImagePos))
);
gradient.setStart(line2_mid1.p1());
gradient.setFinalStop(line2_mid1.p2());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line2_mid1);


QLineF const line1_mid2(
to_screen.map(QLineF(m_ptrSpline->lastVertex()->point(), m_nextVertexImagePos_mid2))
);
gradient.setStart(line1_mid2.p1());
gradient.setFinalStop(line1_mid2.p2());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line1_mid2);


QLineF const line2_mid2(
to_screen.map(QLineF(m_nextVertexImagePos_mid2, m_nextVertexImagePos))
);
gradient.setStart(line2_mid2.p1());
gradient.setFinalStop(line2_mid2.p2());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line2_mid2);
}
else
{
//end of added by monday2000

for (EditableSpline::SegmentIterator it(*m_ptrSpline); it.hasNext(); ) {
...........
painter.setPen(gradient_pen);
painter.drawLine(line);

    }// added by monday2000

m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos), m_visualizer.highlightBrightColor()
);
}

void
ZoneCreationInteraction::onMouseReleaseEvent(QMouseEvent* event, InteractionState& interaction)
{
........
QPointF const screen_mouse_pos(event->pos() + QPointF(0.5, 0.5));
QPointF const image_mouse_pos(from_screen.map(screen_mouse_pos));

//begin of added by monday2000

if(m_nextVertexImagePos != m_nextVertexImagePos_mid1 &&
m_nextVertexImagePos != m_nextVertexImagePos_mid2 && m_ctrl)
{
m_ptrSpline->appendVertex(m_nextVertexImagePos_mid1);
m_ptrSpline->appendVertex(image_mouse_pos);
m_ptrSpline->appendVertex(m_nextVertexImagePos_mid2);
updateStatusTip();

// Finishing the spline.  Bridging the first and the last points
// will create another segment.
m_ptrSpline->setBridged(true);
m_rContext.zones().addZone(m_ptrSpline);
m_rContext.zones().commit();

makePeerPreceeder(*m_rContext.createDefaultInteraction());
m_rContext.imageView().update();
delete this;
}
else
{
//end of added by monday2000

if (m_ptrSpline->hasAtLeastSegments(2) &&
.......
m_ptrSpline->appendVertex(image_mouse_pos);
updateStatusTip();
}
}

}//added by monday2000

event->accept();
}

void
ZoneCreationInteraction::onMouseMoveEvent(QMouseEvent* event, InteractionState& interaction)
{
........
QPointF const last(to_screen.map(m_ptrSpline->lastVertex()->point()));

// begin

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());
m_nextVertexImagePos_mid1 = from_screen.map(screen_mouse_pos_mid1);

QPointF screen_mouse_pos_mid2;
screen_mouse_pos_mid2.setX(screen_mouse_pos.x());
screen_mouse_pos_mid2.setY(last.y());
m_nextVertexImagePos_mid2 = from_screen.map(screen_mouse_pos_mid2);
}
//end

if (Proximity(last, screen_mouse_pos) <= interaction.proximityThreshold()) {
.......

Вот собранный с этой фичей СТ:

http://rghost.ru/43112547 (4.5 МБ)

Естественно, он включает в себя все мои предыдущие правки.

SorokaSV

  • Пользователь
  • **
  • Сообщений: 56
    • Просмотр профиля
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #6 : 18 ПЭТРам 2013, 13:12:53 »
остальные 3 я всегда вручную убираю


Как Вам это удаётся? У меня ни разу не получилось. Они ещё и плодятся, как тараканы, стоит щёлкнуть случайно (чтобы жизнь раем не казалась, ещё и отмены нет?), потом не слушаются, всячески искривляя сетку.

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #7 : 18 ПЭТРам 2013, 19:43:37 »
SorokaSV
Цитировать
Как Вам это удаётся?
Ну как же, там ведь написано - навести мышку на красную точку и нажать Delete или D.
Цитировать
потом не слушаются, всячески искривляя сетку.
Да, что-то с этим недоработано явно в СТ. Я уже научился с ними успешно бороться. Лишние у меня не плодятся - если аккуратно двигать имеющиеся, то лишние не возникнут. При выставлении точек я применяю следующие приёмы:

1. Учитываю взаимное влияние точек. Практически это означает, что нельзя резко двинуть одну из точек - сразу искривятся соседние. Надо немного сдвинуть одну - и столько же немного соседние. При этом всё равно могут возникнуть изломы синей линии - их можно убрать, подвинув (в ту же сторону) ещё более дальних "соседей" двигаемой точки.

2. Частично удаляю и ставлю заново точки - если изломы никак не удаляются или если какая-либо точка перестаёт двигаться при двигании выбранной - её переставляю на то же место, где была.

3. Задавать искривление приходится не только сверху - но и снизу - если этого не сделать - то выпрямление оказывается недостаточным (даже если вроде бы искривление есть только наверху - на результате).

Однако, всё равно - ручное выставление красных точек - это большая морока, отнимающая уйму времени. Конечно, всё это следует понимать лишь как прототип реально-полезного dewarping.

SorokaSV

  • Пользователь
  • **
  • Сообщений: 56
    • Просмотр профиля
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #8 : 18 ПЭТРам 2013, 20:52:53 »
Сенкс.  Но не соглашусь - даже сейчас для сканирования фотоаппататом, очень удобная штука на самом деле.
И за сдвиг целой линии спасибо. Именно этого оказывается, не хватало.

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #9 : 18 ПЭТРам 2013, 23:08:22 »
Я сделал прямоугольное сдвигание углов зоны иллюстраций. Работает также через зажатый Ctrl. Задумано для прямоугольных зон. Теперь можно будет не слишком точно ставить прямоугольные зоны, а потом на большем масштабе уже их подгонять точно под размер.

Вот код исправления:

C:\build\scantailor-0.9.11.1\zones\ZoneVertexDragInteraction.cpp

void
ZoneVertexDragInteraction::onMouseMoveEvent(QMouseEvent* event, InteractionState& interaction)
{
QTransform const from_screen(m_rContext.imageView().widgetToImage());
m_ptrVertex->setPoint(from_screen.map(event->pos() + QPointF(0.5, 0.5) + m_dragOffset));
//begin of added by monday2000
Qt::KeyboardModifiers mask = event->modifiers();

if (mask == Qt::ControlModifier)
{
QPointF current = from_screen.map(event->pos() + QPointF(0.5, 0.5) + m_dragOffset);
QPointF prev = m_ptrVertex->prev(SplineVertex::LOOP)->point();
QPointF next = m_ptrVertex->next(SplineVertex::LOOP)->point();

prev.setX(current.x());
next.setY(current.y());

m_ptrVertex->prev(SplineVertex::LOOP)->setPoint(prev);
m_ptrVertex->next(SplineVertex::LOOP)->setPoint(next);
}
//end of added by monday2000

checkProximity(interaction);
m_rContext.imageView().update();
}
Сделал, наверное, вообще минут за 10.  :)

Вот собранный СТ:

http://rghost.ru/43134964  (4.5 МБ)

SorokaSV
Пожалуйста. :)

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #10 : 19 ПЭТРам 2013, 23:01:29 »
Немного подправил прямоугольные зоны. А именно, при создании такой зоны чуть изменил градиент окраски её линий - в первом варианте было так, что каждая из 4 линий зоны имела свой полный градиент - что было не очень красиво, а теперь у меня полный градиент раскидывается уже на 2 линии - а не на 4.

Т.е. сейчас градиент будет от начальной точки до конечной - а не отдельно по каждой грани зоны. Вот код исправления (вся функция целиком):

C:\build\scantailor-0.9.11.1\zones\ZoneCreationInteraction.cpp

void
ZoneCreationInteraction::onPaint(QPainter& painter, InteractionState const& interaction)
{
painter.setWorldMatrixEnabled(false);
painter.setRenderHint(QPainter::Antialiasing);

QTransform const to_screen(m_rContext.imageView().imageToWidget());
QTransform const from_screen(m_rContext.imageView().widgetToImage());

m_visualizer.drawSplines(painter, to_screen, m_rContext.zones());

QPen solid_line_pen(m_visualizer.solidColor());
solid_line_pen.setCosmetic(true);
solid_line_pen.setWidthF(1.5);

QLinearGradient gradient; // From inactive to active point.
gradient.setColorAt(0.0, m_visualizer.solidColor());
gradient.setColorAt(1.0, m_visualizer.highlightDarkColor());

QPen gradient_pen;
gradient_pen.setCosmetic(true);
gradient_pen.setWidthF(1.5);

painter.setPen(solid_line_pen);
painter.setBrush(Qt::NoBrush);

//begin of added by monday2000

QColor start_color = m_visualizer.solidColor();
QColor stop_color = m_visualizer.highlightDarkColor();
QColor mid_color;

int red = (start_color.red() + stop_color.red())/2;
int green = (start_color.green() + stop_color.green())/2;
int blue = (start_color.blue() + stop_color.blue())/2;

mid_color.setRgb(red, green, blue);

QLinearGradient gradient_mid1;
gradient_mid1.setColorAt(0.0, start_color);
gradient_mid1.setColorAt(1.0, mid_color);

QLinearGradient gradient_mid2;
gradient_mid2.setColorAt(0.0, mid_color);
gradient_mid2.setColorAt(1.0, stop_color);

if(m_nextVertexImagePos != m_nextVertexImagePos_mid1 &&
m_nextVertexImagePos != m_nextVertexImagePos_mid2 && m_ctrl)
{
m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos_mid1), m_visualizer.highlightBrightColor()
);

m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos_mid2), m_visualizer.highlightBrightColor()
);


QLineF const line1_mid1(
to_screen.map(QLineF(m_ptrSpline->lastVertex()->point(), m_nextVertexImagePos_mid1))
);
gradient_mid1.setStart(line1_mid1.p1());
gradient_mid1.setFinalStop(line1_mid1.p2());
gradient_pen.setBrush(gradient_mid1);
painter.setPen(gradient_pen);
painter.drawLine(line1_mid1);


QLineF const line2_mid1(
to_screen.map(QLineF(m_nextVertexImagePos_mid1, m_nextVertexImagePos))
);
gradient_mid2.setStart(line2_mid1.p1());
gradient_mid2.setFinalStop(line2_mid1.p2());
gradient_pen.setBrush(gradient_mid2);
painter.setPen(gradient_pen);
painter.drawLine(line2_mid1);


QLineF const line1_mid2(
to_screen.map(QLineF(m_ptrSpline->lastVertex()->point(), m_nextVertexImagePos_mid2))
);
gradient_mid1.setStart(line1_mid2.p1());
gradient_mid1.setFinalStop(line1_mid2.p2());
gradient_pen.setBrush(gradient_mid1);
painter.setPen(gradient_pen);
painter.drawLine(line1_mid2);


QLineF const line2_mid2(
to_screen.map(QLineF(m_nextVertexImagePos_mid2, m_nextVertexImagePos))
);
gradient_mid2.setStart(line2_mid2.p1());
gradient_mid2.setFinalStop(line2_mid2.p2());
gradient_pen.setBrush(gradient_mid2);
painter.setPen(gradient_pen);
painter.drawLine(line2_mid2);
}
else
{
//end of added by monday2000

for (EditableSpline::SegmentIterator it(*m_ptrSpline); it.hasNext(); ) {
SplineSegment const segment(it.next());
QLineF const line(to_screen.map(segment.toLine()));

if (segment.prev == m_ptrSpline->firstVertex() &&
segment.prev->point() == m_nextVertexImagePos) {
gradient.setStart(line.p2());
gradient.setFinalStop(line.p1());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line);
painter.setPen(solid_line_pen);
} else {
painter.drawLine(line);
}
}

QLineF const line(
to_screen.map(QLineF(m_ptrSpline->lastVertex()->point(), m_nextVertexImagePos))
);
gradient.setStart(line.p1());
gradient.setFinalStop(line.p2());
gradient_pen.setBrush(gradient);
painter.setPen(gradient_pen);
painter.drawLine(line);

    }// added by monday2000

m_visualizer.drawVertex(
painter, to_screen.map(m_nextVertexImagePos), m_visualizer.highlightBrightColor()
);
}
Собранный СТ будет чуть позже.

monday2000

  • Администратор
  • *****
  • Сообщений: 985
    • AOL клиент - -
    • Yahoo клиент - -
    • Просмотр профиля
    • Создание книг в электронном виде из бумажных книг (в формате DjVu)
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #11 : 20 ПЭТРам 2013, 14:49:10 »
Немного подправил прямоугольное двигание прямоугольных зон. Оно корректно работало только в случае использования левого нижнего угла зоны или правого верхнего. Вот код исправления:
C:\build\scantailor-0.9.11.1\zones\ZoneVertexDragInteraction.cpp

void
ZoneVertexDragInteraction::onMouseMoveEvent(QMouseEvent* event, InteractionState& interaction)
{
QTransform const from_screen(m_rContext.imageView().widgetToImage());
m_ptrVertex->setPoint(from_screen.map(event->pos() + QPointF(0.5, 0.5) + m_dragOffset));
//begin of added by monday2000
Qt::KeyboardModifiers mask = event->modifiers();

if (mask == Qt::ControlModifier)
{
QPointF current = from_screen.map(event->pos() + QPointF(0.5, 0.5) + m_dragOffset);
QPointF prev = m_ptrVertex->prev(SplineVertex::LOOP)->point();
QPointF next = m_ptrVertex->next(SplineVertex::LOOP)->point();

int dx = next.x() - prev.x();
int dy = next.y() - prev.y();

if ((dx > 0 && dy > 0) || (dx < 0 && dy < 0))
{
prev.setX(current.x());
next.setY(current.y());
}
else
{
next.setX(current.x());
prev.setY(current.y());
}

m_ptrVertex->prev(SplineVertex::LOOP)->setPoint(prev);
m_ptrVertex->next(SplineVertex::LOOP)->setPoint(next);
}
//end of added by monday2000

checkProximity(interaction);
m_rContext.imageView().update();
}

Вот собранный СТ:

http://rghost.ru/43175226

antabu

  • Новичок
  • *
  • Сообщений: 22
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #12 : 20 ПЭТРам 2013, 18:30:04 »
Хотелось бы на стадии полей легко находить самую широкую и самую высокую страницы в пакете.
« Последнее редактирование: 20 ПЭТРам 2013, 18:35:18 от antabu »

yuree

  • Постоялец
  • ***
  • Сообщений: 172
    • Просмотр профиля
    • E-mail
Re: Модифицирование Scan Tailor
« Ответ #13 : 20 ПЭТРам 2013, 18:42:00 »
Хотелось бы на стадии полей легко находить самую широкую и самую высокую страницы в пакете.

Так это и сейчас можно сделать. Сортировку по возрастающей высоте и/или ширине сделайте.
Или Вы не это имели ввиду?

antabu

  • Новичок
  • *
  • Сообщений: 22
    • Просмотр профиля
Re: Модифицирование Scan Tailor
« Ответ #14 : 22 ПЭТРам 2013, 12:51:05 »
Спасибо, нашёл.