Кто такая Лена в мире обработки изображений

(на случай, если вы не знали)

Каждый, кто начинает обрабатывать цифровые изображения, видел или работал с этой фотографией:

Оригинальное изображение в формате TIFF

Девушку на фотографии зовут Лена Сёдерберг, а вот перевод статьи с историей её появления в научных статьях:

Изображение Лены (Lena или Lenna) — одно из наиболее часто используемых в алгоритмах сжатия стандартных тестовых изображений. Сайт comp.compression FAQ сообщает следующее:

Для любопытных: «Лена» или «Ленна» — оцифрованный разворот Плейбоя ноября 1972 года. (Ленна — имя, использованное в Плейбое, Лена с одной «н» — шведское имя.) Лена Сёдерберг (Lena Soderberg) по последним сведеньях живёт в её родной Швеции, счастлива замужем, имеет 3-х детей и работу в региональной алкогольной монополии. В 1988 её впервые опрашивали несколько шведских изданий, связанных с компьютерными технологиями, и её приятно повеселило, что случилось с её фотографией. Тогда она впервые узнала об использовании фотографии в сфере компьютерных технологий.

Почитайте чудесную статью в Newsletter от мая 2001 за авторством Джейми Хатчинсон на IEEE Professional Communication Society, если хотите знать больше. Вот небольшая выдержка:

Александр Савчук рассказывает, что был июнь или июль 1973, когда он, будучи ассистентом профессора электроинженерии в институте обработки сигналов и изображений (USC SIPI), спешно искал в лаборатории хорошее изображение для сканирования в статью своего коллеги на конференцию. Они просмотрели их набор стандартных тестовых изображений, но хотелось чего-нибудь отпечатанного на глянцевой бумаге журнала, чтобы быть уверенными в хорошем динамическом диапазоне выходного изображения; и им нужно было лицо. Именно тогда, кто-то зашёл с последним выпуском Плейбоя.

Инженеры оторвали верхнюю треть разворота, чтобы она могла поместиться вокруг барабана их сканера широкоформатных изображений, подсоединённого к установке из аналогово-цифровых преобразователей (по одному на красный, зелёный и синий каналы) и миникомпьютера Hewlett Packard 2100. Сканер имел фиксированное разрешение в 100 линий на дюйм, и, поскольку инженеры хотели получить изображение размером 512 на 512 точек, они ограничили сканирование в 5.12 дюймов, чего хватило для оцифровки разворота вплоть до плеч модели.

Исходное изображение до сих пор доступно как часть коллекции изображения USC SIPI.

На протяжении многих лет шли дискуссии об использовании этого изображения. Часть экспертов предлагали запретить использование этого изображения из-за его происхождения. Помимо этого Плейбой угрожал судебными разбирательствами за несанкционированное использование изображения. Почитайте об этом в редакторской статье журнала SPIE инженеров оптики или в записке бывшего главного редактора в соглашении об обработке изображений IEEE. Согласно Wired Magazine, Плейбой прекратил преследование за нарушения прав использования этого изображения, но по-прежнему остаётся их владельцем.

Ещё один любопытный факт о выпуске с Леной (Мисс Ноябрь 1972) — это самый продаваемый выпуск за всю историю Плейбоя (продано 7 161 561 копий).

А в мае 1997 года Лена присутствовала на юбилейной конференции IS&T (50 лет) и вот как это прошло.

«Учил, но забыл»: ошибочное и истинное знание

Как понять, усвоил ученик материал, или просто обладает иллюзией знания? А главное, как сделать так, чтобы он сам научился различать эти вещи?

https://newtonew.com/school/incomplete-knowledge

— новая иконка сайта

Полезные вещи при разработке темы WordPress

Во время отпуска я наконец-то принялся за дела, которые долго откладывал. Совсем недавно я сделал Эмодзи Клипборд ???, а сегодня закончил обновлять дизайн сайта. Внешне, он, конечно, мало изменился, но внутри теперь соответствуют моим требования. Например, у меня нестандартная главная страница, и иногда я меняю на ней фотографию, автора фотографии и приветственное сообщение. Менять это на адаптированной теме Spacious Pro было муторно и долго, и я страдал.

Среди прочих требований были и такие:

  • изменить адаптивное меню для мобильных устройств;
  • иметь отдельные категории записей: галерею, как у лучших фотографов, короткие сообщения большими буквами, как на Хабре, посты-ссылки;
  • просматривать фотографии в полном размере, не уходя со страницы;
  • создавать целые страницы проектов без использования поддомена (сравните: apps.markoutte.me/emoji и markoutte.me/projects/emoji);
  • подчёркивание ссылок, как у лучших дизайнеров;
  • создавать страницы-документы, которые можно тут же распечатать (так сделана страница контактов и резюме);
  • изменять главную страницу через настройки темы.

Вордпрес-тему, которая была написана за чистые 40 часов работы (~3 дня), я назвал Waistcoat и никуда не выложил:

А теперь полезные материалы, сниппеты и ссылки, которые мне очень пригодились.

1. WordPress Theme Development плейлист на ютубе

Вордпрес написан на php, поэтому и разрабатывать тему придётся на этом языке. Опыт использования этого языка у меня был после работы с организаторами Правобереги. На деле, написать тему с имеющимися у меня знаниями оказалось нетрудно. Этот видеокурс помог мне быстро разобраться, что к чему:

2. CSS-фреймворк для красивой разметки

Вы бы выбрали бутстрап? Я — нет. На носу 4-я версия, которая на текущий момент имеет номер сборки v4.0.0-alpha.6. Не хочется использовать у себя проект, находящийся на этапе постоянных доработок и исправлений. Перспективы обновлять тему, когда выйдет новая версия, не привлекают. Использовать 3-ю версию уже нет смысла, поскольку после выхода 4-й я буду облизываться на новый функционал и снова буду переписывать тему. Проблем слишком много, поэтому я просто взял Kube, который мне почти не пришлось допиливать напильником, за что им огромное спасибо.

3. jQuery и плагины

Kube использует jQuery. И тут я совершенно с ним согласен. Что касается современных лайтбоксов (просмотр фотографии поверх затемнённой страницы), то тут меня ждало разочарование — все хорошие реализации стоят денег, причём зарубежных. Хотя один свободный я всё-таки нашёл, и Chocolat радует меня возможностями, внешним видом и простотой в использовании.

4. Расширение функциональности Вордпреса

Для некоторых функциональных задач хочется иметь специфичные вещи. Вот некоторые, упростившие мою жизнь:

function plural_form($number, $after) {
  $cases = array (2, 0, 1, 1, 1, 2);
  echo $number.' '.$after[ ($number%100>4 && $number%100<20)? 2: $cases[min($number%10, 5)] ];
}
    
Простейшая функция склонения слов после числительных
/**
 * Title         : Aqua Resizer
 * Description   : Resizes WordPress images on the fly
 * Version       : 1.2.1
 * Author        : Syamil MJ
 * Author URI    : http://aquagraphite.com
 * License       : WTFPL - http://sam.zoy.org/wtfpl/
 * Documentation : https://github.com/sy4mil/Aqua-Resizer/
 *
 * @param string  $url      - (required) must be uploaded using wp media uploader
 * @param int     $width    - (required)
 * @param int     $height   - (optional)
 * @param bool    $crop     - (optional) default to soft crop
 * @param bool    $single   - (optional) returns an array if false
 * @param bool    $upscale  - (optional) resizes smaller images
 * @uses  wp_upload_dir()
 * @uses  image_resize_dimensions()
 * @uses  wp_get_image_editor()
 *
 * @return str|array
 */

if(!class_exists('Aq_Resize')) {
    class Aq_Exception extends Exception {}

    class Aq_Resize
    {
        /**
         * The singleton instance
         */
        static private $instance = null;

        /**
         * Should an Aq_Exception be thrown on error?
         * If false (default), then the error will just be logged.
         */
        public $throwOnError = false;

        /**
         * No initialization allowed
         */
        private function __construct() {}

        /**
         * No cloning allowed
         */
        private function __clone() {}

        /**
         * For your custom default usage you may want to initialize an Aq_Resize object by yourself and then have own defaults
         */
        static public function getInstance() {
            if(self::$instance == null) {
                self::$instance = new self;
            }

            return self::$instance;
        }

        /**
         * Run, forest.
         */
        public function process( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
            try {
                // Validate inputs.
                if (!$url)
                    throw new Aq_Exception('$url parameter is required');
                if (!$width)
                    throw new Aq_Exception('$width parameter is required');

                // Caipt'n, ready to hook.
                if ( true === $upscale ) add_filter( 'image_resize_dimensions', array($this, 'aq_upscale'), 10, 6 );

                // Define upload path & dir.
                $upload_info = wp_upload_dir();
                $upload_dir = $upload_info['basedir'];
                $upload_url = $upload_info['baseurl'];
                
                $http_prefix = "http://";
                $https_prefix = "https://";
                $relative_prefix = "//"; // The protocol-relative URL
                
                /* if the $url scheme differs from $upload_url scheme, make them match 
                   if the schemes differe, images don't show up. */
                if(!strncmp($url,$https_prefix,strlen($https_prefix))){ //if url begins with https:// make $upload_url begin with https:// as well
                    $upload_url = str_replace($http_prefix,$https_prefix,$upload_url);
                }
                elseif(!strncmp($url,$http_prefix,strlen($http_prefix))){ //if url begins with http:// make $upload_url begin with http:// as well
                    $upload_url = str_replace($https_prefix,$http_prefix,$upload_url);      
                }
                elseif(!strncmp($url,$relative_prefix,strlen($relative_prefix))){ //if url begins with // make $upload_url begin with // as well
                    $upload_url = str_replace(array( 0 => "$http_prefix", 1 => "$https_prefix"),$relative_prefix,$upload_url);
                }
                

                // Check if $img_url is local.
                if ( false === strpos( $url, $upload_url ) )
                    throw new Aq_Exception('Image must be local: ' . $url);

                // Define path of image.
                $rel_path = str_replace( $upload_url, '', $url );
                $img_path = $upload_dir . $rel_path;

                // Check if img path exists, and is an image indeed.
                if ( ! file_exists( $img_path ) or ! getimagesize( $img_path ) )
                    throw new Aq_Exception('Image file does not exist (or is not an image): ' . $img_path);

                // Get image info.
                $info = pathinfo( $img_path );
                $ext = $info['extension'];
                list( $orig_w, $orig_h ) = getimagesize( $img_path );

                // Get image size after cropping.
                $dims = image_resize_dimensions( $orig_w, $orig_h, $width, $height, $crop );
                $dst_w = $dims[4];
                $dst_h = $dims[5];

                // Return the original image only if it exactly fits the needed measures.
                if ( ! $dims && ( ( ( null === $height && $orig_w == $width ) xor ( null === $width && $orig_h == $height ) ) xor ( $height == $orig_h && $width == $orig_w ) ) ) {
                    $img_url = $url;
                    $dst_w = $orig_w;
                    $dst_h = $orig_h;
                } else {
                    // Use this to check if cropped image already exists, so we can return that instead.
                    $suffix = "{$dst_w}x{$dst_h}";
                    $dst_rel_path = str_replace( '.' . $ext, '', $rel_path );
                    $destfilename = "{$upload_dir}{$dst_rel_path}-{$suffix}.{$ext}";

                    if ( ! $dims || ( true == $crop && false == $upscale && ( $dst_w < $width || $dst_h < $height ) ) ) {
                        // Can't resize, so return false saying that the action to do could not be processed as planned.
                        throw new Aq_Exception('Unable to resize image because image_resize_dimensions() failed');
                    }
                    // Else check if cache exists.
                    elseif ( file_exists( $destfilename ) && getimagesize( $destfilename ) ) {
                        $img_url = "{$upload_url}{$dst_rel_path}-{$suffix}.{$ext}";
                    }
                    // Else, we resize the image and return the new resized image url.
                    else {

                        $editor = wp_get_image_editor( $img_path );

                        if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) ) {
                            throw new Aq_Exception('Unable to get WP_Image_Editor: ' . 
                                                   $editor->get_error_message() . ' (is GD or ImageMagick installed?)');
                        }

                        $resized_file = $editor->save();

                        if ( ! is_wp_error( $resized_file ) ) {
                            $resized_rel_path = str_replace( $upload_dir, '', $resized_file['path'] );
                            $img_url = $upload_url . $resized_rel_path;
                        } else {
                            throw new Aq_Exception('Unable to save resized image file: ' . $editor->get_error_message());
                        }

                    }
                }

                // Okay, leave the ship.
                if ( true === $upscale ) remove_filter( 'image_resize_dimensions', array( $this, 'aq_upscale' ) );

                // Return the output.
                if ( $single ) {
                    // str return.
                    $image = $img_url;
                } else {
                    // array return.
                    $image = array (
                        0 => $img_url,
                        1 => $dst_w,
                        2 => $dst_h
                    );
                }

                return $image;
            }
            catch (Aq_Exception $ex) {
                error_log('Aq_Resize.process() error: ' . $ex->getMessage());

                if ($this->throwOnError) {
                    // Bubble up exception.
                    throw $ex;
                }
                else {
                    // Return false, so that this patch is backwards-compatible.
                    return false;
                }
            }
        }

        /**
         * Callback to overwrite WP computing of thumbnail measures
         */
        function aq_upscale( $default, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
            if ( ! $crop ) return null; // Let the wordpress default function handle this.

            // Here is the point we allow to use larger image size than the original one.
            $aspect_ratio = $orig_w / $orig_h;
            $new_w = $dest_w;
            $new_h = $dest_h;

            if ( ! $new_w ) {
                $new_w = intval( $new_h * $aspect_ratio );
            }

            if ( ! $new_h ) {
                $new_h = intval( $new_w / $aspect_ratio );
            }

            $size_ratio = max( $new_w / $orig_w, $new_h / $orig_h );

            $crop_w = round( $new_w / $size_ratio );
            $crop_h = round( $new_h / $size_ratio );

            $s_x = floor( ( $orig_w - $crop_w ) / 2 );
            $s_y = floor( ( $orig_h - $crop_h ) / 2 );

            return array( 0, 0, (int) $s_x, (int) $s_y, (int) $new_w, (int) $new_h, (int) $crop_w, (int) $crop_h );
        }
    }
}





if(!function_exists('aq_resize')) {

    /**
     * This is just a tiny wrapper function for the class above so that there is no
     * need to change any code in your own WP themes. Usage is still the same :)
     */
    function aq_resize( $url, $width = null, $height = null, $crop = null, $single = true, $upscale = false ) {
        /* WPML Fix */
        if ( defined( 'ICL_SITEPRESS_VERSION' ) ){
            global $sitepress;
            $url = $sitepress->convert_url( $url, $sitepress->get_default_language() );
        }
        /* WPML Fix */

        $aq_resize = Aq_Resize::getInstance();
        return $aq_resize->process( $url, $width, $height, $crop, $single, $upscale );
    }
}
    
Страница проекта Aqua Resizer
function raw_foramtter($content) {
    $new_content = '';
    $pattern_full = '{(\[raw\].*?\[/raw\])}is';
    $pattern_contents = '{\[raw\](.*?)\[/raw\]}is';
    $pieces = preg_split($pattern_full, $content, -1, PREG_SPLIT_DELIM_CAPTURE);

    foreach ($pieces as $piece) {
        if (preg_match($pattern_contents, $piece, $matches)) {
            $new_content .= $matches[1];
        } else {
            $new_content .= wptexturize(wpautop($piece));
        }
    }

    return $new_content;
}

remove_filter('the_content', 'wpautop');
remove_filter('the_content', 'wptexturize');

add_filter('the_content', 'raw_foramtter', 99);
    
Отображает кусок кода в заметке Вордпреса без обработки

Итог

Радости моей нет предела. Давно я не сидел с утра до ночи, увлёкшись конкретной целью. Теперь приятно иметь контроль над внешним видом сайта и использовать полезные функциональные возможности.

Слегка освежил дизайн сайта

Скопировать эмодзи в буфер обмена? Легко!

Дополнительная клавиатура эмодзи на айОС причиняет мне множество неудобств, когда включена. Эппл не предусмотрела дополнительной кнопки на клавиатуре для неё, поэтому она добавлена как отдельная клавиатура. И чтобы переключиться на неё требуется нажимать один или более раз кнопку переключения языка. Неприятно, что когда раскладки располагаются в таком порядке: английская → русская → эмодзи, то для переключения с русской на английскую необходимо миновать эмодзи, но в отличии от других текстовых раскладок эмодзи имеет смещённую кнопку переключения языка:

Это очень неудобно, когда не хочется заморачиваться с выбором раскладки по удержании кнопки смены языка или когда переключаешь язык на ходу. Мне не хотелось устанавливать сторонние клавиатуры ради возможности открытия списка эмодзи без переключения языка, но и жить с дополнительной раскладкой-эмодзи я уже не мог. Поначалу я просто копировал эмодзи из других диалогов, если находились подходящие. Потом я создал заметку, в которую занёс все доступные мне эмодзи и копировал уже оттуда. Но копирование оттуда никогда не было быстрым процессом — чтобы скопировать эмодзи я должен был: поставить курсор, выделить нужный смайл, тапнуть по смайлу, выбрать скопировать. С форс-тачем выделять удобней и быстрее, но не везде он есть.

Какое-то время я даже собрался изучить Свифт для айОС, чтобы написать приложение с виджетом, откуда я бы копировал эмодзи одним нажатием, но передумал. В итоге, в один прекрасный день у меня получился Эмодзи клипборд — веб-приложение, которое многое умеет:

  • имеет максимально полный набор эмодзи;
  • копирует выбранный эмодзи сразу в буфер обмена;
  • меняет вид эмодзи в зависимости от системы: у Эппл они одни, у Андроида — другие;
  • устанавливается на телефон в виде веб-приложения;
  • сохраняет данные в оффлайне и работает с ними без интернета;
Скриншоты приложения в Safari
Скриншоты приложения в Edge
Требования

Пока приложение работает в режиме беты. Я проверил его в Safari 10, Google Chrome 57, Microsoft Edge 15, а также в мобильном Safari (iOS 10.3). Оно точно не работает в Internet Explorer 11 (и я не хочу разбираться почему).

Эмодзи Клипборд

https://markoutte.me/projects/emoji

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: