guetzli

I brist på andra sätt att prokrastinera har jag börjat bekanta mig med verktyget guetzli (”Perceptual JPEG encoder”), som är en riktig rackare på att göra JPEGs mindre i storlek. Som med mycket annan häftig alternativt skrämmande teknik idag är det Google som har finansierat detta projekt.

Originalbilden är ca 4 megapixel, är ca 1.7 megabyte stor i sitt ursprungliga utförande. Den innehåller rätt mycket fin textur och är rätt skarp, trots att den är tagen på frihand. Originalbilden är förresten att ta i, originalbilden är på 24 megapixel och jag har exporterat en nedskalad version som jag sedan använt här.

Jag lät guetzli tugga sig igenom originalbilden på den lägsta tillåtna kvalitetsnivån 84, väntade ut processen och jämförde produkten med originalet.

  • Original: 1.7 megabyte
  • Guetzli, kvalitetsnivå 84: 784 kilobyte
  • Guetzli, kvalitetsnivå 95: 1.2 megabyte

Det är en rätt stor skillnad!

När jag sedan jämförde bilderna vid 100% förstoring tog det mig först en liten stund att se skillnaden — men nu är den rätt solklar för den lägsta inställningen, men inte för den förinställda.


Vad är då nackdelen med guetzli? Tid, samt CPU- och minnesåtgång på maskinen som kör programmet. Det tog 11 minuter att exportera bilden på 84% på min bärbara dator, och när jag lät en lite mer kraftfull maskin göra jobbet tog det fortfarande lång tid:

  • Kvalitet 84, 7 minuter 37 sekunder
  • Kvalitet 87, 8 minuter 6 sekunder
  • Kvalitet 90, 8 minuter 36 sekunder
  • Kvalitet 93, 9 minuter 6 sekunder
  • Kvalitet 95, 9 minuter 31 sekunder

Ovan för en bild på 4 megapixel! Just nu processar samma dator en bild på 24 megapixel vid ett par olika kvalitetsinställningar, men jag tänker inte vänta ut att den körningen går klart innan jag hittar på något annat. 🙂


Jag väntade ut bilden innan jag publicerade detta. Det tog drygt en timme (!) per variant.

  • Originalbilden, ca 12 megabyte
  • Kvalitetsnivå 95, 7.4 megabyte
  • Kvalitetsnivå 90, 4.8 megabyte
  • Kvalitetsnivå 84, 4 megabyte

Jag vet inte vad som är rimligast av nivåerna ovan. Jag har bara märkt att mina bilder just nu är rätt stora och att jag borde minska ner dessa lite. Jag blev lite nyfiken på att sätta upp ’objektiva’ kvalitetsmått likt det som beskrivs i guetzlis Wikipedia-sida...men det är ett äventyr för en annan kväll.

Vårvinter

Första timmarna av min första vårvinter, här någonstans runt Jukkasjärvi.

Fram tills väldigt nyligen var konceptet vårvinter helt främmande för mig, även första gången jag upplevde den. Men, som med mycket annat är det nu svårt att vara utan den när upptäckten väl har gjorts.

Emilia visar hur en låtsas förbereda pimplingshål, för att sedan låtsas pimpla.
Obligatorisk hundbild i familjestugan
Möteslokalen saknade både el och rinnande vatten, dock var 4G-mottagningen riktigt god.

MF68

Detta är nog mitt minst konstiga tangentbord i samlingen, och det första mekaniska tangentbord jag faktiskt betalade för med egna pengar. Gångerna innan hade jag lyckats få eller hitta mina — mer om detta en annan gång.

Tangentbordet började sitt liv som ett Magicforce 68, ett tangentbord som rekommenderas för nybörjare som mig — litet, bra byggkvalitet och lätt att hitta nyare keycaps till. Mitt exemplar började sitt liv med Gateron-brytare av den bruna sorten.

Det gick dock rätt snabbt utför för detta tangentbord. Jag upptäckte att det är lätt att beställa specialtillverkade kretskort från Kina och hittade en mall för just denna modell; jag hittade nya keycaps; jag upptäckte det här med after market-fjädrar och slutligen även dämpningsmaterial. Idag är det några originaldelar kvar, typ hölje och brytare.

Dämpningsmatta mellan chassi och kretskort

Det var ett kul projekt, och det går fortfarande att skriva på tangentbordet. Jag föredrar dock andra tangentbord så använde mitt MF68 ett tag efter att ha moddat klart, där efter återgick jag till mitt Iris.

Skriet

Det är i dagarna 5 år sedan jag tog den här bilden. Jag har kopierat den flera gånger, och alla gånger har den blivit ljusare än det skannade negativet ovan. Jag vill gärna tro att jag har lärt mig något av att hålla på — typ att jag inte behöver mörka ner bilden särskilt mycket för att få balans i den, eller att det är okej att byxorna är helt svarta.

Det enda jag hoppas att jag har lärt mig är att känna mig klar med en bild, att jag inte behöver göra denna bild gång, på gång, på gång. Att det finns 1000 andra bilder att bråka med, och 1000 till att ta.

Fult ska det va’

Jag tar fula bilder också. Här är några tagna i kvarteret jag bor i, alla tagna hösten 2018.

Behovet av att ta fula bilder av fina saker är inte enbart för att ironisera (även om det är kul), men som någon slags motpunkt till all Bo01-inspirerad bebyggelse. Mina kvarter är roliga att beskåda — varierat, bitvis inspirerat och fint att åtminstone delar av området är hyresrätter. Men allt är byggt i ett tidigare halvsunkigt industriområde där vi nu försöker glömma bort vad vi byggt våra hem på.

Jag tror mina bilder bara är kommentarer om detta. Om inte annat var det skönt att ta dem.

När vatten ska fotograferas är det långa slutartider som gäller. Det vet alla.

Det kanske svåraste uppdraget kommer att bli att ta fina bilder av min omgivning. Det kommer ju alltid vara lättare att spela på ironi-kortet än att genuint försöka skapa något fint. Vi tar det en annan dag…

Fint ska det va’

Jag har några säsongsbetonade traditioner, huvudsakligen relaterade till fotografi. En är vårens maniska vitsippefotografering, sommarens blasémässiga löften om att fotografera mer av prakten, samt höstens lätt melankoliska dokumentation av den svunna prakten (många pretentiösa ord i luften nu!).

Här är några av årets traditionsenliga höstbilder. Inget speciellt alls.

Barnbassäng

Göra backuper i NixOS

Ingen behöver nog påminnas om vikten av backuper, men ibland behöver en kanske påminnas om hur en går till väga för att göra det, särskilt för smalare distributioner som NixOS.

Första steget är att importera ett framtida nix-expression från NixPKGs. Detta kommer nog inte vara nödvändigt att göra i framtida versioner, men i skrivande stund fanns inte denna fil i rätt gren för att kunna användas i hos mig.

{ config, lib, pkgs, ... }:

with lib;
{
  options.services.restic.backups = mkOption {
    description = ''
      Periodic backups to create with Restic.
    '';
    type = types.attrsOf (types.submodule ({ name, ... }: {
      options = {
        passwordFile = mkOption {
          type = types.str;
          description = ''
            Read the repository password from a file.
          '';
          example = "/etc/nixos/restic-password";
        };

        s3CredentialsFile = mkOption {
          type = with types; nullOr str;
          default = null;
          description = ''
            file containing the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
            for an S3-hosted repository, in the format of an EnvironmentFile
            as described by systemd.exec(5)
          '';
        };

        repository = mkOption {
          type = types.str;
          description = ''
            repository to backup to.
          '';
          example = "sftp:backup@192.168.1.100:/backups/${name}";
        };

        paths = mkOption {
          type = types.listOf types.str;
          default = [];
          description = ''
            Which paths to backup.
          '';
          example = [
            "/var/lib/postgresql"
            "/home/user/backup"
          ];
        };

        timerConfig = mkOption {
          type = types.attrsOf types.str;
          default = {
            OnCalendar = "daily";
          };
          description = ''
            When to run the backup. See man systemd.timer for details.
          '';
          example = {
            OnCalendar = "00:05";
            RandomizedDelaySec = "5h";
          };
        };

        user = mkOption {
          type = types.str;
          default = "root";
          description = ''
            As which user the backup should run.
          '';
          example = "postgresql";
        };

        extraBackupArgs = mkOption {
          type = types.listOf types.str;
          default = [];
          description = ''
            Extra arguments passed to restic backup.
          '';
          example = [
            "--exclude-file=/etc/nixos/restic-ignore"
          ];
        };

        extraOptions = mkOption {
          type = types.listOf types.str;
          default = [];
          description = ''
            Extra extended options to be passed to the restic --option flag.
          '';
          example = [
            "sftp.command='ssh backup@192.168.1.100 -i /home/user/.ssh/id_rsa -s sftp'"
          ];
        };

        initialize = mkOption {
          type = types.bool;
          default = false;
          description = ''
            Create the repository if it doesn't exist.
          '';
        };
      };
    }));
    default = {};
    example = {
      localbackup = {
        paths = [ "/home" ];
        repository = "/mnt/backup-hdd";
        passwordFile = "/etc/nixos/secrets/restic-password";
        initialize = true;
      };
      remotebackup = {
        paths = [ "/home" ];
        repository = "sftp:backup@host:/backups/home";
        passwordFile = "/etc/nixos/secrets/restic-password";
        extraOptions = [
          "sftp.command='ssh backup@host -i /etc/nixos/secrets/backup-private-key -s sftp'"
        ];
        timerConfig = {
          OnCalendar = "00:05";
          RandomizedDelaySec = "5h";
        };
      };
    };
  };

  config = {
    systemd.services =
      mapAttrs' (name: backup:
        let
          extraOptions = concatMapStrings (arg: " -o ${arg}") backup.extraOptions;
          resticCmd = "${pkgs.restic}/bin/restic${extraOptions}";
        in nameValuePair "restic-backups-${name}" ({
          environment = {
            RESTIC_PASSWORD_FILE = backup.passwordFile;
            RESTIC_REPOSITORY = backup.repository;
          };
          path = with pkgs; [
            openssh
          ];
          restartIfChanged = false;
          serviceConfig = {
            Type = "oneshot";
            ExecStart = "${resticCmd} backup ${concatStringsSep " " backup.extraBackupArgs} ${concatStringsSep " " backup.paths}";
            User = backup.user;
          } // optionalAttrs (backup.s3CredentialsFile != null) {
            EnvironmentFile = backup.s3CredentialsFile;
          };
        } // optionalAttrs backup.initialize {
          preStart = ''
            ${resticCmd} snapshots || ${resticCmd} init
          '';
        })
      ) config.services.restic.backups;
    systemd.timers =
      mapAttrs' (name: backup: nameValuePair "restic-backups-${name}" {
        wantedBy = [ "timers.target" ];
        timerConfig = backup.timerConfig;
      }) config.services.restic.backups;
  };
}

Importera sedan denna fil i din globala nixos/configuration.nix och kontrollera att allt blev rätt, exempelvis genom en nixos-rebuild dry-run.

Skapa en ny tjänstedefinition för dina backuper. Här får du kika i manualen för restic för att förstå format och liknande.

services.restic.backups = {
  backup = {
    paths = [ "/folders/to/backup" "/more/folders" ];
    repository = "sftp:your-backup-host.com:/path/to/backup/repository/";
    passwordFile = "/root/.secrets";
    initialize = false;
    # extraOptions = [ "sftp.command='sftp -F /root/.ssh/config -i /root/.ssh/id_rsa preconfigured-host'" ];
    timerConfig = {
      OnCalendar = "03:30";
      # RandomizedDelaySec = "5h";
    };
  };
};

Ändra värden så att det blir relevant för dig. Några saker att tänka på:

  • Du måste skapa förrådet (”repositoriet”) innan du kan börja ta backuper. Se restic-manualen för hur du gör detta
  • Du måste använda ssh-nycklar, annars kommer restic krasha om den får en lösenordsfråga
  • Du måste spara ner lösenordsfrasen till en fil, annars händer samma sak som för ssh (detta är även en design-feature)
  • Det utkommenterade kommandot är om du behöver finjustera inställningarna för hur du ansluter med sftp till din backup-nod, exempelvis om du behöver prata på en annan port än TCP 22

När du knåpat ihop din tjänstedefinition är det bara att sjösätta den. Tipset är att du schemalägger den till att köra några minuter in i framtiden så att du kan se att den verkligen körs, och sedan ändrar tiden till något mer relevant när du vet att det funkar.

Nackdelen med denna lösning är att du inte har total kontroll över din backupstrategi – fördelen är dock att dina backuper körs medan du funderar på den ultimata strategin. 🙂

IKEA Trådfri gateway

Det tycks som att IKEA har gjort mycket rätt med sitt system Trådfri – det är baserat på relevanta standarder, det är rimligt prissatt, det kräver inga molntjänster och det är designat för att inte gå ut på internet, annat än för att uppdatera sin mjukvara. Det verkar såhär långt vara fint.

Jag hittade dock ingen som nämnde vilka portar som krävs för att prata med gatewayen i systemet, och väntade mig att jag skulle sitta med tcpdump halva dagen för att få igång det hela i mitt foliehattsnätverk hemma.

Tji fick jag. Det räckte med att öppna UDP 5684 mellan mitt gästnätverk och Trådfri-gatewayen.

Vad ska jag nu göra med min dag? (」゚ロ゚)」