Ростелеком прислал по электронной почте счет за телефон

Немного смущает, что даются ссылки на платную программу WinRar (она ведь еще платная, не?), но это мелочи. Самый прикол в приложении к письму: размер приложения 536Kb, внутри платежка размером 127Kb и спам картинка на 410kb. Потратить 75% размера на рекламу — это гениально, я считаю. К тексту письма претензий нет, приведу его здесь целиком:

Уважаемый абонент!

Петербургский филиал ОАО «Ростелеком» высылает счет/комплект платежных документов по E-mail за апрель 2013 года.

Рекомендации:

1. Если полученный файл (Attachments.zip) не удается открыть при просмотре почтового ящика, сохраните его на компьютер и затем откройте.

2. Если при выводе на печать из программы Adobe Reader (http://get.adobe.com/reader/) документ невозможно прочитать (символы вместо букв), то необходимо воспользоваться альтернативной программой просмотра PDF-файлов Foxit Reader (http://www.foxitsoftware.com/downloads/).

3. Для открытия архива и просмотра документов рекомендуем использовать следующие программы:
— для распаковки архива программу WinRar версия 3.30 и выше или аналоги (например 7-zip).
— для просмотра документов Adobe Reader версия 8.0 и выше или Foxit Reader

4. Необходимые программы можно скачать по адресам:
Win Rar — http://www.win-rar.ru/download/winrar/
7-zip — http://7-zip.org.ua/ru/
Adobe Reader — http://get.adobe.com/reader/
Foxit Reader http://www.foxitsoftware.com/downloads/

Если Вы не получите наше письмо в 1-ой декаде месяца, проверьте папку Нежелательная почта (СПАМ).

Мы рады сотрудничеству с Вами!

Данное письмо сформировано программным модулем и не требует ответа.

Если указанные выше рекомендации не позволили Вам открыть вложение, обращайтесь по тел. 8-118-88.

В целом, остался доволен. Наконец-то наступил XXI век в отдельно взятом гос. учреждении..

Совет сотрудникам, которые добавляют рекламу в платежку — если сохранять такую картинку сразу в PNG формат, то размер будет меньше.

UPD: прислали новую платежку за сентябрь. Сложно сказать есть ли прогресс: платежка теперь в формате PDF (это плюс), реклама теперь с разрешением 2482×3508 и размером 950Kb (это минус).

Почему Thread.sleep() — это прошлый век

Допустим, есть задача: создать поток, который скачивает что-то из интернета, а из основного потока ему валятся задания. Стандартный путь реализации такой:

class DownloadThread extends Thread {
    public interface Listener {
        void onImageLoaded(Image image);
    }
 
    private String reqUrl_ = null;
    private final Listener listener_;
    private boolean isFinished_ = false;
 
    DownloadThread(Listener listener) {
        listener_ = listener;
    }
 
    public void imageRequest(String url) {
        synchronized (this) {
            // Запрос на загрузку изображения
            reqUrl_ = url;
        }
    }
 
    @Override
    public void run() {
        while (!isFinished_)
            String url = null;
 
            synchronized (this) {
                // Проверка: есть ли запрос?
                if (null != reqUrl_) {
                    url = reqUrl_;
                    reqUrl_ = null;
                }
            }
 
            if (null != url) {
                // Запрос есть, загружаем изображение
                Image image = download(url);
                listener_.onImageLoaded(image);
            }
 
            try {
                // Поспим одну милисекунду, чтоб не грузить CPU
                sleep(1);
            }
            catch (InterruptedException e) {
                isFinished_ = true;
                break;
            }
        }
    }
 
    public void stopThread() {
        isFinished_ = true;
    }
}

Почему этот плохой, негодный пример? Потому что, когда заданий много, между ними одна миллисекунда бездействия минимум — это во-первых. Во-вторых, если задач для потока нет, то он работает без всякой пользы, создавая лишнюю нагрузку процессору.

Рассмотрим пример номер два:

class DownloadThread extends Thread {
    public interface Listener {
        void onImageLoaded(Image image);
    }
 
    private String reqUrl_ = null;
    private final Listener listener_;
    private boolean isFinished_ = false;
 
    DownloadThread(Listener listener) {
        listener_ = listener;
    }
 
    public void imageRequest(String url) {
        synchronized (this) {
            // Запрос на загрузку изображения
            reqUrl_ = url;
            // Уведомляем поток, что есть задание
            notify();
        }
    }
 
    @Override
    public void run() {
        while (!isFinished_)
            String url = null;
 
            synchronized (this) {
                if (isFinished_) {
                    break;
                }
                // Проверка: есть ли запрос?
                if (null != reqUrl_) {
                    url = reqUrl_;
                    reqUrl_ = null;
                }
                else {
                    try {
                        // Ждем вызова notify
                        wait();
                    }
                    catch (InterruptedException e) {
                        isFinished_ = true;
                        break;
                    }
                }
            }
 
            if (null != url) {
                // Запрос есть, загружаем изображение
                Image image = download(url);
                listener_.onImageLoaded(image);
            }
        }
    }
 
 
    public void stopThread() {
        synchronized (this) {
            isFinished_ = true;
            notify();
        }
    }
 
}

Почему этот пример лучше первого? Во-первых, когда заданий нет, поток находится в режиме ожидания без вызовов sleep(). Во-вторых, когда задание приходит, поток реагирует моментально просыпаясь.

Обратите внимание, что метод stopThread() слегка изменился. Ввиду того, что поток не выйдет из состояния wait() без вызова notify().

UPD: По совету друзей исправил метод run() для корректного завершения потока.

В обоих примерах не учитывается, что изображения нам нужны пачкой сразу, то есть очередь запросов выкинута для упрощения кода.

P.S. Кстати, в C/C++ для тех же целей можно использовать pthread_cond_wait() и pthread_cond_signal()

Извлекаем вектор и угол из матрицы поворота

В продолжение темы кватерниона

Итак, есть матрица 4×4, где три оси ортогональны друг другу (по-нашенски, по-деревенски — это означает, что три оси перпендикулярны каждая с каждой, представьте оси прямоугольной системы координат, так вот это оно и есть).

Выглядит матрица вот так:

M=\begin{bmatrix}A&E&I&M\\B&F&J&N\\C&G&K&O\\D&H&L&P\end{bmatrix}

В ней, как я уже рассказывал, «живут» три вектора ABC (ось X), EFG (ось Y), IJK (ось Z). Также есть три смещения вдоль XYZ — это M, N и O соответственно.

Для того, чтобы извлечь вектор и угол из этой матрицы я использую вот такую конструкцию (псевдокод):

def getAngleAxis(m):
    xx = m[A]
    yy = m[F]
    zz = m[K]
 
    # Сумма элементов главной диагонали
    traceR = xx + yy + zz
 
    # Угол поворота
    theta = acos((traceR - 1) * 0.5)
 
    # Упростим вычисление каждого элемента вектора
    omegaPreCalc = 1.0 / (2 * sin(theta))
 
    # Вычисляем вектор
    w.x = omegaPreCalc * (m[J] - m[G])
    w.y = omegaPreCalc * (m[C] - m[I])
    w.z = omegaPreCalc * (m[E] - m[B])
 
    # Получаем угол поворота и ось, 
    # относительно которой был поворот
    return (theta, w)

Мне довелось применять этот метод на матрице, где вектора ABC, EFG и IJK нормализованы. Масштаб хранится отдельно. Если вы храните масштаб внутри матрицы, то перед применением формулы надо нормализовать вектора ABC, EFG и IJK).

Простой путь создания анимации из набора фотографий

Захотелось мне намедни создать GIF анимацию из набора фотографий. Фотографии вот такие (это между прочим анимация на библиотеке в Минске, если кто не знает):

Библиотека в Минске. Набор кадров

Все они сфотографированы примерно из одной позиции примерно одного объекта. Если создать из этих фотографий анимацию (делал с помощью сайта gifmaker.me), то получится вот такая неприглядная GIF-ка:

Сырая анимация библиотеки в Минске

Разумеется, это было мало похоже на анимацию, скорее на набор кадров. Так что пришлось вручную подгонять каждую фотографию к одному виду в графическом редакторе. Потратив на это три с лишним часа, стало понятно, что так дальше продолжаться не может — кадры скакали чуть меньше, но все равно очень заметно. Ведь фотографии отличаются не только сдвигом фотоаппарата относительно объекта, но и масштабом с искажением.

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

Оно плохо работает, если кадров мало, но это лучше, чем ничего. Делюсь с вами, вдруг пригодится.

  • устанавливаем программу Hugin (ссылка для скачивания)
  • создаем файл проекта output.pto с помощью утилиты align_image_stack (align_image_stack.exe -p output.pto in_0.jpg in_1.jpg in_N.jpg). Названия файлов надо указывать по-одному, маска файлов программой не поддерживается.
  • открываем получившийся output.pto в программе Hugin
  • нажимаем «Объединить» (Hugin должен пересчитать контрольные точки для изображений), закрываем открывшееся после процедуры окно
  • после этого кнопку «Создать панораму»

После нажатия кнопки «Создать панораму», откроется диалоговое окно, где надо указать папку для сохранения исправленных изображений. По умолчанию формат изображений TIFF.

Получаются вот такие изображения (можно их обрезать с помощью любого редактора, например IrfanView):

Выровненные изображения библиотеки в Минске

Создаем GIF-ку из получившихся фотографий:

Анимация после обработки программой Hugin

P.S. Важный момент — лучше использовать оригинальные изображения с фотоаппарата, потому что они содержат информацию о фокусном расстоянии камеры, которое необходимо программе Hugin для правильной работы.

Блог Евгения Жирнова