Wiem, że na temat umieszczania logo/znaku wodnego na zdjęciach jest źródłem kontrowersji. Osobiście nie mam nic do tego, aby zamieścić firmowe logo na fotce, na której widnieje produkt naszego autorstwa. Z biznesowego punku widzenia ma to jak najbardziej sens, gdyż zaznajamiamy potencjalnego odbiorcę z naszą marką. Z technicznego punktu widzenia, jednym z głównych argumentów niestosowania znaku wodnego jest łatwość jego usunięcia. Są jednak algorytmy, ktre to zadania znacznie utrudniają. Ciekawy artykuł odnośnie wspomnianych algorytmów znajdziesz tutaj.
Dlaczego chcę napisać tą appke?
- Na OSX nie znalazlam narzędzia które by zamieszczało znak wodny w odpowiedniej jakości, w trybie bulk
- Chcę przetestować w działaniu nowe bezpieczniejsze algorytmy umieszczające logo na zdjęciach. A tak naprawdę poznać możliwości biblioteki do obróbki graficznej pod ruby 🙂
Pierwsze kroki
- Jak zainstalować środowisko programistyczne oparte na Ruby znajdziesz na stronie GoRails:
rails new project-name cd project-name git init
- Instalacja ruby
- Inicjalizacja appki rails, usuniecie strony startowej i setup routingu
- Dodanie bootstrapa
- Autoryzacja użytkownika za pomocą gemu Devise
- Setup testow
Stawiam na trójkę: Minitest, Factory Girl oraz Faker
Więcej o setupie testów dla nowych aplikacji w RoR oraz przetestowaniu modelu User z gemem Devise napiszę wkrótce.
Odpalenie projektu na produkcji
Na szybko wybrałam kosting na Heroku. Jak się okazało później, nie był to najlepszy wybór dla aplikacji tego typu. W darmowej wersji przestrzeń plików jest co jakiś czas resetowana do konfiguracji z repozytorium. Czy inaczej – wszystkie uploadowane pliki są kasowane mimo istniejących referencji w bazie.
Instalacja Heroku-CLI dla środowiska staging/production jest opisana tutaj:
heroku login heroku git:remote -a project-name
Przydatne komendy, gdy pracujesz z Heroku:
git push heroku master heroku run rake db:migrate
Specyfikacja aplikacji
Rozbijając funkcjonalność na ficzery, mogę stwierdzić, że aplikacja powinna mieć:
-
Możliwość oddawania zdjęć do galerii
Do zapisywania zdjęć w bazie wykorzystam gem Paperclip, update: w najnowszej wersji Rails (5.2) zalecane jest użycie wbudowanego we framework Active Storage
Na razie nie decyduję się na przechowywanie danych w chmurze, dlatego nie zmieniam domyślnego configu (storage.yml). Nowy model Gallery
będzie mógł zawierać wiele zdjęć:
rails active_storage:install rails g scaffold Gallery description:text
class Gallery < ActiveRecord::Base has_many_attached :photos end
Formularz dodawania zdjęć do galerii będzie bazował na Direct Uploads, do którego dodałam js i css zgodnie z dokumentacją dla Active Storage. Dzięki temu pojawią się ładne animacje uploadowania plików
-
Możliwość załadowania znaku wodnego/logo
Znów przyda się scaffold:
rails g scaffold Logo
Model zawierający logo będzie wyglądał tak:
class Logo < ApplicationRecord has_one_attached :image end
-
Połączenie Galerii i Logo z użytkownikiem
Model użytkownika będzie w relacji z Galerią (może mieć ich wiele), tak samo jak i z Logo. Do przyszłego algorytmu będzie wykorzystywane tylko ostatnio dodane logo, dlatego zdefiniowałam osobny scope :last_logo
:
User: has_many :galleries, dependent: :destroy has_many :logos, dependent: :destroy has_one :last_logo, -> { order 'created_at DESC' }, class_name: "Logo"
-
Możliwość naniesienia znaku wodnego na zdjęcie
Jak już wspomniałam wyżej, ostatnio stworzone logo będzie domyślnie tym, które zostanie naniesione na zdjęcie. Przycisk przy galerii umożliwi uruchomienie algorytmu i zmodyfikowanie wszystkich zdjęć z danej galerii za jednym razem.
-
Algorytm modyfikacji znaku wodnego/logo
Do edycji obrazków użyję gemu mini-magick. wymaga on biblioteki imagemagick. Na OSX zaistalujesz ją wpisujac:
brew install imagemagick
Na heroku jest już zainstalowana jedna z jej wersji, możesz to sprawdzić wpisując w konsolę polecenie convert
:
heroku run convert
Wybrałam 5 modyfikatorów, które transformują logo z zachowaniem rozpoznawalności. Innymi słowy wprowadzają transformację w stopniu na tyle niewielkim, aby nadal logo wyglądało w miarę podobnie do oryginału.
scale swirl wave rotate distort
Algorytm będzie polegał na wybraniu losowo jednego do pięciu modyfikatorów. Parametry dla każdego z nich również zostaną wybrane losowo.
Do obsługi modyfikacji logo oraz naniesienia go na fotografię stworzyłam osobny serwis LogoProcessing
. Metoda call
wywoływana jest za pomocą wspomnianego przycisku przy galerii zdjęć. Wielkość zdjęc ograniczyłam ze względu na szerokość. Przekraczające ustalony rozmiar zostaną zeskalowane do szerokości 1024 px. Lubię metaprogramowanie więc postanowiłam użyć metod dynamicznych. Najpierw wtorzę losowo ciąg modyfikatorów a potem po kolei wywołuję na jego elementach metodę send aby zaaplikować poszczególne modyfikacje na logo.
def call @gallery.photos_watermarked.purge return unless @last_logo @gallery.photos.each do |photo| logo_img = MiniMagick::Image.read(@last_logo.image.download) source_img = MiniMagick::Image.read(photo.download) source_img.resize "1024x>" logo_img.resize "256x>" mods_to_apply = choose_mods mods_to_apply.each do |mod| send(mod, logo_img) end result = source_img.composite(logo_img) do |c| c.compose "Over" c.geometry "+#{rand(5..20)}+#{rand(5..20)}" c.gravity "SouthEast" c.dissolve rand(85..100) end file = File.open(result.path) image_file = ActiveStorage::Blob.build_after_upload(io: file, filename: "watermarked_" + photo.filename.to_s) @gallery.photos_watermarked.attach(image_file) end end private def modifiers %w(scale swirl wave distort rotate) end def choose_mods modifiers.sample(1 + rand(modifiers.count)) end
Gotowe obrazki zostaną zapisane w tym samym modelu z którego pochodzą. Ostatecznie klasa Gallery wygląda tak:
class Gallery < ApplicationRecord has_many_attached :photos has_many_attached :photos_watermarked belongs_to :user end
-
Możliwość pobrania gotowych zdjęć
Musiałam przemyśleć, jak zaprezentować gotowe zdjęcia do pobrania. Postanowiłam, że jesli w danej galerii naniesiono już logo, to zamiast bazowych zdjęć pojawią się te oznaczone. Dodatkowo będą posiadały link do pobrania na dysk.
Gotowa aplikacja do nanoszenia zmodyfikowanego znaku wodnego
Gotowa aplikacja znajduje się tutaj
Po zaimplementowaniu ficzerów wymienionych w specyfikacji postawiłam produkcję na Heroku. Na chwilę obecną brakuje walidacji typu pliku, nie ma komunikatu, jeżli nie załadowaliśmy wcześniej logo. Brakuje też walidacji minimalnych wymiarów logo oraz zdjęcia w galerii. Te i inne poprawki być może zrealizuję późniejszym czasie 🙂