Site icon Co robię w wolnym czasie

Aplikacja: oznaczanie zdjęcia swoim logo

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?

  1. Na OSX nie znalazlam narzędzia które by zamieszczało znak wodny w odpowiedniej jakości, w trybie bulk
  2. 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

  1. Jak zainstalować środowisko programistyczne oparte na Ruby znajdziesz na stronie GoRails:
    rails new project-name
    cd project-name
    git init
  2. Instalacja ruby
  3. Inicjalizacja appki rails, usuniecie strony startowej i setup routingu
  4. Dodanie bootstrapa
  5. Autoryzacja użytkownika za pomocą gemu Devise
  6. 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ć:

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

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

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"

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.

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

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 🙂

Exit mobile version