company_banner

12-мегапиксельное фото козы и пакетное изменение размеров файлов на Mac

Автор оригинала: Giacomo Balli
  • Перевод
В прошлом месяце мы с женой поехали на выходные в Напу. Это — хорошее место. Там много чем можно занять себя на воздухе, да и ехать туда из Сан-Франциско недалеко.

Мы отлично провели время. А когда вернулись домой и я сбросил на компьютер фотографии, которые снял в путешествии, я обратил внимание на то, что они занимают страшно много места. Я — вовсе не фотограф, хотя мне и очень нравится делать снимки, главная цель которых — сохранить память о чём-то хорошем.

Один из снимков меня прямо-таки зацепил. Это была 12-мегапиксельная фотография козы размером почти в 10 Мб. Такая качественная, что невооружённым глазом этого толком и не оценить. Это навело меня на размышления. Мне хотелось бы сохранить этот снимок — как часть воспоминаний о том, чем мы занимались, но перспектива тратить на него столько дискового пространства меня вовсе не радовала. Снимки такого размера способны довольно быстро заполнить даже немаленький диск. Я понял, что мне нужна программа для пакетного изменения размеров подобных файлов.


Фото козы (3024 × 4032, HEIC), которое было преобразовано в JPG с уменьшением разрешения

Я, кроме прочего, люблю, чтобы вокруг был бы порядок, чтобы у всего было бы своё место. Мне нужно было не только поменять размеры изображений в пакетном режиме. Мне хотелось ещё и сохранить метаданные снимков и EXIF-данные (дату, время, место съёмки и так далее).

Мне не удалось найти инструмент для решения этой простой, но очень важной для меня задачи. Поэтому я задался целью сделать такой инструмент своими силами. После того, как я подобрал подходящие средства разработки и написал, а потом довёл до ума код, сделав его максимально простым и эффективным, у меня получилось то, что показано ниже. В коде имеются комментарии, которые помогут тому, кому это интересно, в нём разобраться. Он поддерживает обработку изображений в форматах JPG и HEIC.

#!/bin/bash

#задаём максимальную ширину изображения в пикселях
maxwidth=2000

#где находятся фотографии?
if [ $# -eq 0 ]; then
  imgFolder="untitled folder"
else
  imgFolder=$1
fi

cd "$imgFolder"
pwd

#создаём временную папку для хранения изменённых фотографий
if [ -d new ]; then 
  rm -rf new
fi
mkdir new

#находим все JPG-изображения в заданной папке
fileCnt=$(ls *.[jJ][pP]*[Gg] | wc -l)
cnt=0
echo "Will search for JPGs"
find . -maxdepth 1 -iname '*.jp*g' |
while read file
do 
  ((cnt++))
  imgwidth=`sips --getProperty pixelWidth "$file" | awk '/pixelWidth/ {print $2}'`
  if [ $imgwidth -gt $maxwidth ]; then
    imgheight=`sips --getProperty pixelHeight "$file" | awk '/pixelHeight/ {print $2}'`
    #пропускаем панорамные снимки
    if [ $((imgwidth / imgheight)) -lt 2 ]; then
      echo "$cnt/$fileCnt $file $imgwidth → $maxwidth"
      sips -Z $maxwidth "$file" --out new/"$file" > /dev/null 2>&1
      touch -r "$file" new/"$file"
    fi
  fi
done

#находим все HEIC-изображения в заданной папке
fileCnt=$(ls *.HEIC | wc -l)
cnt=0
echo "Will search for HEICs"
find . -maxdepth 1 -iname '*.heic' |
while read file
do 
  ((cnt++))
  imgwidth=`sips --getProperty pixelWidth "$file" | awk '/pixelWidth/ {print $2}'`
  if [ $imgwidth -gt $maxwidth ]; then
    imgheight=`sips --getProperty pixelHeight "$file" | awk '/pixelHeight/ {print $2}'`
    if [ $((imgwidth / imgheight)) -lt 2 ]; then
      echo "$cnt/$fileCnt $file $imgwidth → $maxwidth"
      sips -s format jpeg -Z $maxwidth "$file" --out new/"$file".jpg  > /dev/null 2>&1
      touch -r "$file" new/"$file".jpg
      rm "$file"
    fi
  fi
done

echo ""
echo "Will replace originals..."
echo "Done!"

rsync -a new/ .
rm -rf new

Если вы решите воспользоваться этим скриптом — сохраните вышеприведённый код в файле resizeImages.sh, настройте максимальную ширину изображения в пикселях, а потом запустите скрипт из терминала, выполнив команду вида ./resizeImages.sh photos. Здесь photos — это имя папки, в которую были скопированы снимки с iPhone. Для сброса снимков с iPhone на компьютер рекомендую пользоваться программой Image Capture.

Как вы подходите к обработке и хранению больших фотографий?

RUVDS.com
VDS/VPS-хостинг. Скидка 10% по коду HABR

Комментарии 41

    +5

    И что в итоге? 3 Мб вместо 10 Мб?
    Не удивлюсь, если раньше люди думали "не буду снимать на цветную пленку, хватит и черно-белой. Тем более она дешевле", а теперь их потомки натравливают на эти фото ИИ чтобы раскрасить.

      +5
      > Один из снимков меня прямо-таки зацепил. Это была 12-мегапиксельная фотография козы размером почти в 10 Мб. Такая качественная, что невооружённым глазом этого толком и не оценить. Это навело меня на размышления. Мне хотелось бы сохранить этот снимок — как часть воспоминаний о том, чем мы занимались, но перспектива тратить на него столько дискового пространства меня вовсе не радовала. Снимки такого размера способны довольно быстро заполнить даже немаленький диск.

      Двадцать первый век, 4-терабайтные винчестеры по 100 долларов, а человеку настолько жалко потратить 10 мегабайт на запавший в душу снимок, что он ажно программу для пережатия написал. Так на винчестер поместится не 400 тысяч фотографий, а целый миллион! Вдруг у меня будет миллион любимых фотографий, но не будет лишних 100 долларов на ещё один винчестер? «Tech Entrepreneur | Advisor | Consultant Over a decade of mobile experience at your service. I help business owners navigate and leverage technology.» ну никак не может позволить себе потратить лишние 100 у. е. на какие-то там картинки.

      И коли уж так сурово жаба душит, то что мешало в настройках айфона выставить меньшее разрешение?
        +6
        1. При просмотре снимки меньшего размера быстрей открываются/пролистываются, т.к. нужно меньше данных читать.
        2. Фотографии должны быть не только на винчестере, но и на телефоне, чтобы в любой момент можно было вернуться к ним. А там с терабайтами не всё так просто.
        3. У телефона далеки от идеала алгоритмы сжатия, поэтому всегда снимаю в максимальном качестве и потом уменьшаю, если надо. Да и потом можно забыть, что выставил мЕньшее разрешение, и наснимать ценных кадров в уменьшенном разрешении.

        Не знаю как на Мак, а на Вин уже лет 10 пользуюсь XnView, там и пакетная обработка с фильтрами, и возможность сочетанием клавиш прямо при просмотре поворачивать фотографии без перекодировки.
          +1
          У меня вообще на SD-карте cmd-файл с утилитой nconvert.
          Одно нажатие — и фото в приемлемом (для меня, т.е. max 3MPix) качестве на диске С.
          +4
          Еще умножьте как минимум на два — свой архив должен быть в двух местах как минимум. Если конечно он вам дорог.
            +3
            Ну да, 200 долларов — это уже непозволительная роскошь.
              –3
              Поставим вопрос так 100 или 200 долларов в месяц (многие покупают облако по подписке)
            +5
            То, что здесь показано в imagemagick делается просто.
            +7
            И да, там в оригинале не «Один из снимков меня прямо-таки зацепил», а

            > One picture in particular caught my attention: a goat.

            То бишь привлёк внимание. А в переводе читается так, как будто этот несчастный снимок так покорил сердце автора, что он ну никак не может выделить ему лишние 10 мегабайт места на диске.
              +3
              Кстати пакетно пережимать на винде умеет FastStone Image Viewer, кому надо.
                +10

                Да куча софта это умеют делать. Не понимаю, зачем писать свой прекрасный скрипт.

                +3
                Велосипеды даже бывает интереснее изобретают, чем описанный здесь способ.
                Нет конечно статью то надо слепить, пусть даже из фото козы
                Голосую за XnView.
                  +1

                  Тогда XnConvert. Он быстрее, мощнее и удобнее для пакетной обработки "не глядя".

                  +3
                  Мне не удалось найти инструмент для решения этой простой, но очень важной для меня задачи.

                  За умение пользоваться гуглом твердая двойка)).

                  XnView, IrfanView и думаю еще многие другие умеют пакетно переживать фото
                    0
                    Выше уже привели примеры софта, который умеет решать проблему из статьи. У меня другая проблема. Очень хотелось каким-то стандартным способом проставлять фото тэги и потом искать по ним нужные фото на диске. Да, с тэгами много кто умеет работать, но есть нюансы. Во-первых, часто тэги хранятся в виде отдельной БД, во-вторых, хочется кроссплатформенное решение. Под кроссплатформенностью я имею в виду следующий сценарий: фоткаешь на телефон (android); сразу добавляешь к фото нужный тэг, например «коза»; потом скидываешь фото на комп (windows) и хочешь искать по этим тэгам фото в папке. Искать в телефоне по этим же тэгам тоже было бы полезно. В идеале записывать тэги в EXIF (т.е. чтобы тэги были частью фото), но вот такого мало встречал. Если кто-то пользуется чем-то подобным, просьба поделиться решением. Писать велосипед нет желания.
                      0
                      На данный момент пользую Shotwell (linux), вроде есть сборка под win…

                      Не всё устраивает в качестве фотоархива, но умеет писать и читать теги из JPEG(exif)/XMP, хранить в своей базе _или_ записывать их в файлы. Какую-то часть РАВ-ов читает сам, но меньше, чем хотелось.

                      Организация фото по: тегам, событиям («типа группа»), датам, каталогам на диске, оценкам и «меткам». Теги может писать/читать в файлы, остальное — на уровне БД (оценками не пользуюсь, мож и их пишет)

                      Можно запускать с разными конфигами — типа «архив с телефона» и «архив фото-любимого-клуба». Есть минимальная правка по яркости-цветности и кучка плагинов (не пользуюсь, не знаю как оно) для экспорта в соцсети и прочие внешние хранилища
                        0
                        А как писать тэги на телефоне?
                          0
                          Гугл в помощь — (по крайней мере под андроид) редакторов есть каких-то.

                          Сам даже не думал ставить тэги в телефоне. Максимум — сразу раскидываю по папкам (был mi-gallery — там это группами, что ли, зовётся. Сейчас «Simple gallery» — умеет перемещать в папки).
                          Дальше rsync-wrapper-ом в комп, а там посматриваю при импорте и правлю тег на всё-из-папки если надо
                            0
                            Гугл в помощь — (по крайней мере под андроид) редакторов есть каких-то.

                            Спасибо за ценный совет.
                          0
                          Есть DigiKam. Правда, он в составе KDE Plasma.
                          0

                          Бесплатная XnView MP может читать теги из фотографий и записывать в IPTC/XMP. Только надо в настройках это включить на вкладке Metadata. Теги там зовутся Categories, можно как фильтровать по ним, так и назначать. Есть два режима — либо галочками проставлять теги из списка, либо в Category Sets самому раскидать как удобно (например Животные: коза, собака, кошка. Время суток: утро, день, ночь).


                          Под Android можно вбить в поиск "IPTC tagger" и выбрать что-то из выдачи. Там немного, но есть.

                            0
                            Спасибо, попробую. На первый взгляд это то, что нужно.
                            0

                            У QNAP есть автоматическое растаскивание фото по папкам на основе распознанных на них объектов.

                              0
                              Прочитайте внимательно, мне не надо ничего распознавать. Я хочу сам проставить тэг. Причем не всегда очевидный, например, «документы».
                            +3
                            Любопытно, а по какой причине жпеги с айфона весят по 8-10 МиБ? Я понимаю, если равки с зеркалок на те же 12 Мп, они по 14-15 МиБ весят. Но это равки. Жпеги оттуда же от силы 5 МиБ. А тут в чём причина? ПО айфона добавляет к фото больше ненужностей?
                              0
                              Мда… Мой комментарий может кого-то задевать, что кто-то его минуснул? Где логика?
                                0

                                Покупайте наше облако

                                  0

                                  Там HEIC, он тяжёлый

                                    0

                                    HEIC весит JPEG при прочих равных

                                      0

                                      *весит меньше

                                    0
                                    Сейчас посмотрел размер последних фото: HEIC — 1.5–1.7 МБ, DNG (ProRAW) — 22–27 МБ, жпег нынче вообще по умолчанию не используется
                                      0
                                      Для экономии батареи фотоаппарата/телефона алгоритм сжатия оптимизирован на скорость а не на объём файла?

                                      А вообще да — купите у нас облако побольше!
                                      +2
                                      Ну, посмотрел я скрипт. Конвертируем жипеги в жипеги… Ок, можно было лучше, ну лан.
                                      И из прогрессивного (всмсл современного) HEIC в жипег… На будущее, типа. В жипег. Ага. Почему не в bmp сразу?
                                      Вот такие сеньоры-помидоры обитают в силиконовой долине, трактористу на заметку.
                                        +4

                                        Какой-то сборник вредных советов "как грохнуть свой домашний архив".


                                        Даже если отвлечься от темы "а что, если я захочу десяток фото распечатать и сделать фотокнигой или календарём на память", никогда, слышите, НИКОГДА не удаляйте исходные файлы автоматом после пакетной обработки.


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

                                          +1

                                          Второе, почему это плохой совет — не указывается качество JPEG, это верный способ угробить фотографию.


                                          У sips есть параметр


                                          formatOptions    [low|normal|high|best|]

                                          который я не ставил бы ниже best (вроде можно и в процентах, тогда >85)


                                          Ещё есть параметр quality, но я не понял, что он значит. Тоже бы ставил в best, возможно, это качество масштабирования.


                                          Ну и третье, напоследок — скрипт, стирающий любые директории без предупреждения и подтверждения, особенно если речь о папке с частым названием new, нельзя писать никогда.

                                            +4
                                            shellcheck нет, не слышали?

                                            Про ключ -e у echo видимо тоже?

                                            echo -e "\nWill replace originals...\n"


                                            Файл использует camelSpace стиль, но не везде, придерживаться одного стиля не учили в именах переменных? Это является хорошим тоном в программировании. Обработку исключений обрабатывать в shell скриптах не учили? sips? Это же тулза из mac os, для linux кросплатформенности есть imagemagick. Одну и туже команду запускать только ради получения отдельно ширины и высоты не разумно, правильнее положить выполнение команды в переменную, и потом из нее уже извлекать. folder вы серьезно? Термин folder не применим к POSIX системам. Это чисто Windows замута!

                                            Вот мой вариант только может еще рекурсивно обрабатывать директории. И если заменить -resize "${maxWidth}x" на -resize "${maxWidth}@" то можно делать ресайз наибольшей стороны, а не только по ширине.

                                            
                                            #!/bin/bash
                                            
                                            imgsDirectory="."
                                            destDirectory="./new"
                                            quality=75
                                            maxWidth="2000"
                                            
                                            if [ ! -d "${imgsDirectory}" ]; then
                                              echo "Image directory: ${imgsDirectory} not exist!"
                                              exit 1
                                            fi
                                            
                                            if [ -d "${destDirectory}" ]; then
                                              rm -rf "${destDirectory}"
                                            fi
                                            
                                            # Making directory tree. Making only not empty directories.
                                            mkdir -p "${destDirectory}"
                                            find "${imgsDirectory}" -type d -not -empty | cut -c$((${#imgsDirectory} + 2))- | xargs -I {} mkdir -p "${destDirectory}/{}"
                                            
                                            # Image files processing.
                                            IFS=$'\n'
                                            cnt=0
                                            imageFiles=$(find "${imgsDirectory}" -type f -regextype egrep -iregex ".*\.(jpe{0,1}g|heic)$" | cut -c$((${#imgsDirectory} + 2))-)
                                            for image in $imageFiles; do
                                              ((cnt++))
                                              imageData=$(magick identify -quiet "${imgsDirectory}/${image}" | grep -ioP -m1 "\w+\s+[0-9]+x[0-9]+" | head -n1)
                                              imageType=$(echo "${imageData}" | cut -f1 -d\ )
                                              imageWidth=$(echo "${imageData}" | cut -f2 -d\  | cut -f1 -dx)
                                              imageHeight=$(echo "${imageData}" | cut -f2 -d\  | cut -f2 -dx)
                                              if [[ -z ${imageWidth} || -z ${imageHeight} ]]; then
                                                echo -e "[${cnt}] Skiping image ${image} - \E[1m\E[31mBROKEN IMAGE\E[0m"
                                                continue
                                              fi
                                            
                                              if echo "${imageType}" | grep -iq "heic"; then
                                                imageName=$(echo "${image}" | sed -n "s/.\w*$/.jpg/I;p")
                                              else
                                                imageName=${image}
                                              fi
                                            
                                              if [[ "${imageWidth}" -gt "${maxWidth}" && $((imageWidth / imageHeight)) -lt 2 ]]; then
                                                magick "${imgsDirectory}/${image}" -quiet -resize "${maxWidth}x" -quality "${quality}" "${destDirectory}/${imageName}"
                                                touch -r "${imgsDirectory}/${image}" "${destDirectory}/${imageName}"
                                                echo -e "[${cnt}] Resizing Image ${image} \E[1m\E[33m${imageWidth} → ${maxWidth}\E[0m - \E[1m\E[32mSUCCESS\E[0m"
                                              fi
                                            
                                            done
                                            
                                            # Removing empty directories in destionation. Directories which can be contain bad image files.
                                            find "${destDirectory}" -type d -empty -delete
                                            
                                            echo -e "\nReplacing original images...\n"
                                            
                                            #rsync -a "${destDirectory}/" "${imgsDirectory}"
                                            rm -rf "${destDirectory}"
                                            
                                            # vim:set ts=2 sw=2 et:
                                            

                                              0
                                              Кстати забыл указать, что еще можно использовать mozjpeg для лучшего сжатия.

                                              Вместо:

                                              magick "${imgsDirectory}/${image}" -quiet -resize "${maxWidth}x" -quality "${quality}" "${destDirectory}/${imageName}"
                                              


                                              Надо использовать такую команду:

                                              convert "${imgsDirectory}/${image}" -quiet -resize "${maxWidth}x" pnm:- | \
                                                cjpeg -quality "${quality}" > "${destDirectory}/${imageName}"
                                              
                                              0
                                              Еще решение было, кроссплатформенное — phatch (PHoto & bATCH). Жаль что умерло.
                                                0
                                                никого не смутило, что фотограф находится со стороны козы?
                                                  0

                                                  вариант "стоит за изгородью с вытянутой рукой (селфипалкой)" не рассматривается?

                                                  +1
                                                  Имею желание купить большой диск, но коза говорит «Сначала шубу»

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

                                                  Самое читаемое