Uwaga : Anders Kaseorg otrzymał na razie zgodę, aby zwrócić uwagę na jego wspaniałą odpowiedź, ale wyzwanie to jeszcze nie koniec! W ofercie jest wciąż 400 punktów nagrody dla każdego, kto zdobędzie najwyższy wynik bez korzystania z wbudowanej kompresji.
Poniżej znajduje się 386x320
reprezentacja png Gwiaździstej nocy van Gogha.
Twoim celem jest odtworzenie tego obrazu tak blisko, jak to możliwe, w nie więcej niż 1024 bajty kodu. Na potrzeby tego wyzwania bliskość obrazów jest mierzona kwadratowymi różnicami wartości pikseli RGB, jak wyjaśniono poniżej.
To jest wyzwanie kodowe . Wyniki są obliczane przy użyciu skryptu sprawdzania poprawności poniżej. Najniższy wynik wygrywa.
Twój kod musi być zgodny z następującymi ograniczeniami:
- To musi być kompletny program
- Musi wyświetlać obraz w formacie, który można odczytać za pomocą poniższego skryptu sprawdzającego, działającego na moim komputerze. Skrypt korzysta z biblioteki PIL Pythona, która może ładować wiele różnych formatów plików , w tym png, jpg i bmp.
- Musi być całkowicie samowystarczalny, nie pobiera danych wejściowych i nie ładuje plików (poza importowaniem bibliotek, co jest dozwolone)
- Jeśli twój język lub biblioteka zawiera funkcję, która generuje Starry Night, nie możesz korzystać z tej funkcji.
- Powinien działać deterministycznie, za każdym razem wytwarzając tę samą wydajność.
- Wymiary obrazu wyjściowego muszą wynosić
386x320
- Aby uniknąć wątpliwości: prawidłowe odpowiedzi muszą używać języków programowania zgodnie ze zwykłymi zasadami PPCG . Musi to być program, który generuje obraz, a nie tylko plik obrazu.
Prawdopodobnie niektóre zgłoszenia zostaną wygenerowane przez kod. W takim przypadku w odpowiedzi należy podać kod użyty do przesłania zgłoszenia i wyjaśnić, jak to działa. Powyższe ograniczenia dotyczą wyłącznie przesłanego programu do generowania obrazów 1kB; nie dotyczą żadnego kodu użytego do jego wygenerowania.
Punktacja
Aby obliczyć wynik, weź obraz wyjściowy i oryginał powyżej i przekonwertuj wartości pikseli RGB na liczby zmiennoprzecinkowe w zakresie od 0 do 1. Wynik piksela to (orig_r-img_r)^2 +(orig_g-img_g)^2 + (orig_b-img_b)^2
, tj. Kwadratowa odległość w przestrzeni RGB między dwoma obrazami. Wynik obrazu jest sumą wyników jego pikseli.
Poniżej znajduje się skrypt w języku Python, który wykonuje te obliczenia - w przypadku jakichkolwiek niezgodności lub dwuznaczności ostateczny wynik to wynik obliczony przez ten skrypt działający na mojej maszynie.
Pamiętaj, że wynik jest obliczany na podstawie obrazu wyjściowego, więc jeśli użyjesz formatu stratnego, który wpłynie na wynik.
Im niższy wynik, tym lepiej. Oryginalny obraz Gwiaździstej Nocy miałby wynik 0. W astronomicznie mało prawdopodobnym przypadku remisu odpowiedź na największą liczbę głosów określi zwycięzcę.
Cele bonusowe
Ponieważ w odpowiedziach dominowały rozwiązania wykorzystujące wbudowaną kompresję, przyznałem serię nagród za odpowiedzi, które wykorzystują inne techniki. Następny będzie nagrodą w wysokości 400 punktów , która zostanie przyznana, jeśli i kiedy odpowiedź, która nie korzysta z wbudowanej kompresji, zajmuje najwyższe miejsce.
Wcześniej przyznane nagrody premiowe były następujące:
Za odpowiedź Nneonneo przyznano nagrodę w wysokości 100 punktów, ponieważ jest to odpowiedź o najwyższym wyniku, która nie korzystała wówczas z wbudowanej kompresji. W momencie przyznania miał 4852,87 punktów. Wyróżnienia należą się do 2012 rcampion, który podjął mężną próbę pokonania Nneonneo przy użyciu podejścia opartego na teselacji Voronoi , zdobywając 5076 punktów, oraz do Sleafara, którego odpowiedź była na prowadzeniu do końca, z 5052 punktami, stosując metodę podobną do nneonneo.
Nagrodę Strawdog otrzymał 200 punktów . Zostało to przyznane za strategię opartą na optymalizacji, która przewodziła wśród odpowiedzi niewbudowanych w kompresję i utrzymywała ją przez tydzień. Zdobył 4749.88 punktów za pomocą niezwykle sprytnej metody.
Skrypt oceny / weryfikacji
Poniższy skrypt w języku Python powinien zostać umieszczony w tym samym folderze co powyższy obraz (który powinien mieć nazwę ORIGINAL.png
) i uruchomić za pomocą polecenia formularza python validate.py myImage.png
.
from PIL import Image
import sys
orig = Image.open("ORIGINAL.png")
img = Image.open(sys.argv[1])
if img.size != orig.size:
print("NOT VALID: image dimensions do not match the original")
exit()
w, h = img.size
orig = orig.convert("RGB")
img = img.convert("RGB")
orig_pix = orig.load()
img_pix = img.load()
score = 0
for x in range(w):
for y in range(h):
orig_r, orig_g, orig_b = orig_pix[x,y]
img_r, img_g, img_b = img_pix[x,y]
score += (img_r-orig_r)**2
score += (img_g-orig_g)**2
score += (img_b-orig_b)**2
print(score/255.**2)
Uwaga techniczna: Obiektywne miary podobieństwa obrazów są trudne. W tym przypadku wybrałem taki, który każdy może łatwo wdrożyć, mając pełną świadomość, że istnieją znacznie lepsze środki.
Tabela liderów
źródło
pip unistall PIL
, a następniepip install pillow
) i zmiana pierwszej linii nafrom PIL import Image
.Odpowiedzi:
Pyth (bez wbudowanej kompresji), wynik
4695.074656.034444.82Jedyną funkcją związaną z obrazem w Pyth jest wbudowane narzędzie do zapisywania macierzy potrójnych RGB jako pliku obrazu. Tak więc szalonym pomysłem jest wytrenowanie małej głębokiej sieci neuronowej na funkcji ( x , y ) ↦ ( r , g , b ) reprezentującej obraz i uruchomienie jej na współrzędnych każdego piksela.
Plan
Obecna sieć zbudowana jest z 45 neuronów sigmoidalnych, przy czym każdy neuron jest podłączony do wejść x , y i do każdego poprzedniego neuronu, a ostatnie trzy neurony są interpretowane jako r , g , b . Jest trenowany przy użyciu algorytmu Adama bez grupowania. Parametry ważące 1125 połączeń są kwantowane do zakresu 93 możliwych wartości (z wyjątkiem stałych, które mają 93 2 możliwe wartości) przy użyciu wariantu kwantyzacji stochastycznej , przy czym podstawową odmianą jest to, że ustawiamy gradient dla skwantowanych parametrów na zero.
Wynik
Kod
1023 bajty, kodowane za pomocą
xxd
(dekodowanie za pomocąxxd -r
). Użyłem wersji 2016-01-22 o Pyth który był obecny, kiedy to wyzwanie został zwolniony. Możesz uruchomić kod bezpośrednio w Pyth, ale Pyth w PyPy3 (pypy3 pyth starry.pyth
) uruchamia go dziewięć razy szybciej, w ciągu około 3 minut. Obraz wyjściowy jest zapisywanyo.png
.Jak to działa
Trening
Podczas mojego ostatniego treningu użyłem znacznie wolniejszego harmonogramu kwantyzacji i trochę interaktywnie bawiłem się tym i szybkością uczenia się, ale użyty przeze mnie kod był mniej więcej następujący.
Wyobrażanie sobie
To zdjęcie pokazuje aktywacje wszystkich 45 neuronów w funkcji współrzędnych x , y . Kliknij, aby powiększyć.
źródło
Mathematica, wynik 14125.71333
Zapisuje ten obraz:
do
a.png
.źródło
Java, 7399.80678201
Przypomniało mi to projekt, który miałem kilka lat temu w mojej klasie obliczeń numerycznych, polegającą na narysowaniu sylwetki Mount Everest za pomocą interpolacji wielomianowej. Dokonano tego w MATLAB, ale MATLAB nie przepadam za bardzo, więc postanowiłem pracować w Javie. Podstawową ideą jest to, że wybrałem „inteligentne” punkty (czytane tutaj jako „losowe”) do interpolacji wielomianowej. Mając kilka bajtów, które mi pozostały, stworzyłem sposób na narysowanie gwiazd, co dzieje się przed narysowaniem góry. Może być możliwe skondensowanie kodu i dodanie innego wielomianu dla dna, aby poprawić wynik.
Edycja: Dodałem i zmieniłem niektóre wielomiany i dodałem wszystkie gwiazdy. Mój poprzedni wynik to 9807.7168935, więc, jak widać, jest to bardzo duża poprawa. Niestety, kod miał znaczny wpływ na jego czytelność, ponieważ musiałem wycisnąć kilka ostatnich bajtów, aby uzyskać wszystkie gwiazdy i nadać im grupy.
9807.7168935 punktów: 7399.80678201 punktów:
źródło
Python3.4 +, 4697.26
Użyłem tej samej metody, co w mojej odpowiedzi ImageMagick, ale z następującymi parametrami:
Za pomocą tych parametrów wygenerowałem następujący 1003 bajtowy program Python (nie znalazłem żadnych ulepszeń w stosunku do metody wyjściowej @ kennytm):
Co z kolei generuje ten obraz:
źródło
1
zelatin1
i zapisać spacje zimport *
przynajmniej. Ale gra w golfa nie była priorytetem (ponieważ ma mniej niż 1024 bajty).Python 3, wynik 5701,31
Po prostu przeskaluj obraz PNG 18 × 13.
źródło
Java, 8748.95
Inne podejście:
Stworzyłem klasę, która oblicza diagram Voronoi na podstawie określonego zestawu punktów. Ten zestaw punktów jest używany jako zestaw parametrów, który służy jako dane wejściowe dla Apache BOBYQAOptimizer . Funkcja oceny optymalizatora pobiera punkty i tworzy z nich diagram voronoi. Regiony voronoi są pokolorowane średnim kolorem odpowiedniego regionu oryginalnego obrazu.
Proces optymalizacji pokazano tutaj:
Ostateczny obraz to:
które osiągają wynik 8748.95
(Zmierzono to za pomocą mojej własnej funkcji, ale powinno być takie samo jak w przypadku skryptu oceny)
Wynikiem tego procesu jest tylko zestaw 8 punktów i odpowiadające im kolory. (Większa liczba punktów spowodowała gorsze wyniki, ale nie wypróbowałem tego zbyt szeroko).
Kod wynikowy jest pokazany tutaj (przepraszam, musiałem trochę zagrać w golfa, aby wycisnąć go do limitu 1kB):
(Wiem, istnieje kilka łatwych sposobów na osiągnięcie lepszych rezultatów, ale ... to było zabawne ...)
W odpowiedzi na komentarz dotyczący stylu artystycznego takiego obrazu voronoi z większą liczbą punktów: To rzeczywiście wygląda interesująco, a niektóre narzędzia do obrazowania faktycznie oferują to jako „Filtr mozaiki” - na przykład Filtr mozaiki w GIMP ( chociaż oferuje to opcje podkreślania krawędzi itp.).
Oto przykład obrazu Gwiaździstej Nocy z 256 punktami. (Są one wybierane losowo, ale przy większej liczbie punktów poprawa, którą można osiągnąć dzięki optymalizacji, zniknie).
To nie jest część konkursu (ponieważ nie mieści się w 1kB), tylko dla ciekawskich:
źródło
import java.util.*;
W rzeczywistości zmień wszystkie klasy importu na gwiazdki.AutoIt ,
9183,257882,53AKTUALIZACJA
Okazuje się więc, że przerysowanie obrazu jak (pijany) maluch jest bardziej skuteczne niż przechowywanie dowolnej wersji obrazu. (W każdym razie bardziej skuteczne niż moje stare rozwiązanie).
Każda linia, która rysuje element, ma kluczowe znaczenie dla obniżenia wyniku. Podejrzewam, że ten program jest w stanie osiągnąć wyniki znacznie poniżej 7000 z bardzo drobnymi modyfikacjami, ponieważ każda pojedyncza zmiana ma ogromny (~ 20 do 100 punktów) wpływ na wynik. Program korzysta z mojej
processing
biblioteki graficznej, która udostępnia zwięzłe nazwy funkcji do rysowania za pomocą GDI.Ponieważ to rozwiązanie wiąże się z przypadkowością, wysiewamy PRNG za pomocą stałej wartości
0
za pomocąSRandom(0)
. Dlaczego 0? Ponieważ jest do 50 punktów lepszy niż jakikolwiek inny,n<=100
którego próbowałem.Płótno zaczyna się jako puste
#587092
.Generowanie podłogi
Dolna część obrazu (która, jak się okazuje, zaczyna się dokładnie przy 233 pikslach [znowu z powodu punktów]) jest wypełniona dokładnie
int(1e4*2.9)
elipsami. Zmiana współczynnika tutaj (lub miejsca dziesiętnego współczynnika) może zmniejszyć i zwiększyć wynik o setki punktów. Po kilku próbach zdecydowałem się na 2.9. Oczywiście zajmie to trochę czasu (kilka sekund).Dostarczona jest pięciokolorowa paleta:
Krople na podłodze
Cztery elipsy służą do ustawiania kontrastowych akcentów w obszarze podłogi (
$4
jest wskaźnikiem funkcjiellipse()
):Generowanie akcentów na niebie
Niektóre linie są rysowane za pomocą grubszego pióra, aby reprezentować znaczące obszary koloru na niebie, które są zbyt rozciągnięte dla elips:
Ważenie pikseli
Po powyższym wszystko jest płukane i powtarzane, aż zabraknie bajtów. Następnie stosuje się rozmycie, aby oszukać metodę sprawdzania poprawności. Brutalną siłą ustalono, że promień dokładnie 20 zapewnia najlepszy wynik. Poprawia to wynik o około 1,5 tys. Rundy (!).
Ostateczny obraz
Kod, 985 bajtów
STARA ODPOWIEDŹ
Przechowuje 80 wartości kolorów, które składają się na obraz 10 x 8 pikseli. Ten surowy obraz ma wynik 10291. Ponieważ 10x8 to współczynnik pikselacji 40px, rozmycie Gaussa jest stosowane przy użyciu promienia 40px w celu obniżenia wyniku. W ten sposób skrypt osiąga 9183,25.
To są przechowywane dane:
Utworzony plik to True.png:
Program ma długość 998 bajtów :
źródło
1e4*2.9
równe29e3
?Plik BAT systemu Windows, wynik 4458,854
Rozmiar programu to 1024 bajty.
Konwertuje obraz BPG kodowany base64 na PNG.
Używa certutil.exe (standardowe narzędzie Windows) i bpgdec.exe dekodera obrazów jako bibliotek.
Kompresja:
źródło
C ++ 11,
7441.681261056997.654348335198.16107651Więcej aktualizacji
Elipsy z Perla tak bardzo mi się podobały, że musiałem je wypróbować w C ++ 11. Użyłem nieprzetworzonego ciągu, aby wprowadzić tam bajty, ale przez pewien czas otrzymywałem niewielką rozbieżność z oczekiwanym wynikiem i wygenerowanym kodem. Okazuje się, że tak naprawdę nie można wstawić surowego 0x0d (Carriage Return), ponieważ g ++ przekonwertuje to na 0x0a (New Line). Szczerze mówiąc, nie jestem pewien, jak uzasadnione jest to wygenerowane źródło, ale kompiluje się ono i działa na kilku moich komputerach.
Wypróbowałem także inny algorytm, adaptacyjne wyszukiwanie wymiarowe po tym, jak GA wyglądało, jakby utknął w martwym punkcie, tylko po to, by wypolerować lokalne minimum i być może mieć szczęście i wpaść do innej studni.
Dzięki temu C ++ 11 daje zaskakująco konkurencyjny wynik (znacznie lepszy, niż początkowo przypuszczałem) ... Jestem dość zaskoczony, że może to zrobić z fstream jako jedyną zawartością.
Tekst (tak, nowe wiersze znajdują się w rzeczywistym źródle ... Myślę, że mógłbym je usunąć):
Hexdump:
Ta odpowiedź łączy w sobie kilka podejść z poprzednich odpowiedzi, które wyjaśnię poniżej, niestety skończyło się na tym, że musiałem nieco pograć w program, aby zmieścił się w
944949 znakach (zgodnie zwc -c
), więc nie wygląda już tak jak C ++ (przeprasza to jest niezgodne z regułami wyzwania, zamierzam wkrótce wprowadzić kilka ulepszeń). Na początku nie planowałem tego, więc nadal nie jest to całkowicie nieczytelne i wciąż jest dużo nisko wiszących owoców.Zaktualizowane wyniki
Już samo uruchamianie algorytmu genetycznego na dłużej przyniosło nieco lepszy wynik; jednak biorąc pod uwagę, że konwergencja znacznie się spowolni, powiedziałbym, że ta konkretna metoda prawdopodobnie zaczyna się uzupełniać (lub wpadłam w jakieś głębokie lokalne minimum). Grałem jeszcze w program końcowy, aby wcisnąć kilka kolejnych prostokątów (generator pozostaje ten sam, z wyjątkiem tego, że maksymalna wielkość genomu została zwiększona).
Wdrożenie przejścia między osobami pomoże, jeśli problemem jest głębokie lokalne minimum, ale biorąc pod uwagę, że przez jakiś czas pozostawało w tym samym zakresie, zaczynam myśleć, że jest to tak dobre, jak to możliwe dla liczby prostokątów.
Wersja Voronoi, 7331.92407536, 989 znaków
Kiedyś Marco13 za Voronoi Pomysł z mojego kodu GA. To naprawdę nie działało tak dobrze, jak miałem nadzieję. Mogłem tylko wcisnąć kilka punktów więcej niż prostokąty. Myślę, że potencjalnie rozłączna natura prostokątów z powodu nakładania się pomaga trochę punktować. Niezależnie od tego, podoba mi się sposób, w jaki wygląda to znacznie lepiej, pomimo podobnego wyniku do mojego pierwszego wpisu.
Stare wyniki, 7441,68126105, 944 znaki
Podobnie jak niektóre inne wpisy, program po prostu rysuje nakładające się prostokąty. Używa binarnego PPM, ponieważ format jest prosty (wynik jest
a.ppm
, ale przesłałem wersję png, ponieważ SE nie lubił PPM) i jest całkowicie deterministyczny.Wyjaśnienie
Generowanie PPM pochłonęło sporą część kodu, co oznaczało, że nie mogłem mieć zbyt wielu prostokątów nawet po odrobinie golfa. Można tu jeszcze wsadzić kilka innych, aby jeszcze bardziej poprawić wynik.
Prawdziwą magią jest lista prostokątów. Podobnie do odpowiedzi Wolfganga, użyłem algorytmu genetycznego, aby je znaleźć. W rzeczywistości implementacja jest w dużej mierze niekompletna, ponieważ rekombinacja między osobnikami jeszcze nie występuje, ale mutacja wciąż ma miejsce, a ranking w stylu turniejowym według kondycji utrzymuje najlepsze organizmy w następnej rundzie. Wykorzystywany jest również elitaryzm, ponieważ kopia najlepszej osoby z ostatniej rundy jest przechowywana w następnej rundzie, więc najlepiej dopasowany organizm jest zawsze co najmniej tak samo sprawny, jak w poprzedniej rundzie.
Nie przyglądałem się zbytnio kodowi Wolfganga, odkąd zacząłem to wczoraj, ale wygląda na to, że pozwala on również zmieniać kolor, co może tłumaczyć różnicę punktacji.
Aby zmniejszyć przestrzeń wyszukiwania, patrzyłem tylko na położenie prostokąta; kolor jest obliczany na podstawie średniej widocznych pikseli z tego prostokąta na kanał, ponieważ mamy obraz źródłowy (nie sądzę, że możemy zrobić coś lepszego dla tego konkretnego prostokąta, ponieważ minimalizuje to odległość do kwadratu).
Jeśli będę dalej nad tym pracował, utworzę repozytorium github w następnych kilku edycjach, ale na razie kod (pojedynczy plik) jest na pastebin . Skompiluj to w trybie C ++ 11 (uwaga, jestem dość zawstydzona tym, jak bałagan jest nawet jednorazowy).
Będziesz także potrzebować zdjęcia P3 PPM z gwiaździstą nocą nazwanego,
ORIGINAL.ppm
aby to działało. Możesz pobrać plik z tego GitHub Gist .źródło
i r,g,b
zamiast osobno, a wiele miejsc można odrzucić). Nie byłem pewien, czy program powinien wygenerować plik bezpośrednio, czy przesyłanie strumieniowe do stdout / stderr było w porządku.#include <fstream>
? Ponadto upuść nowe wiersze i umieść to wszystko w jednym wierszu, ponieważ C ++ i tak potrzebuje średnikówImageMagick, 4551.71
Używa języka programowania „ImageMagick” przy użyciu następujących opcji (może być konieczne ucieczka
!
):Zakładając następujący 968 bajtowy plik źródłowy (podany jako zrzut heksowy):
Produkcja tego obrazu:
Prawdopodobnie zastanawiasz się, jak wygenerowałem plik wejściowy, a odpowiedź jest raczej prosta. Zmień rozmiar na 48x40 z filtrem Lanczos, użyj palety 20 indeksowanych kolorów i zoptymalizuj wynikowy PNG.
Korzysta
convert
,pngquant
,optipng
iadvdef
.źródło
=(tail +2 $0)
sztuczki z mojej odpowiedzi , możesz utworzyć pojedynczy skrypt ZSH, który zawiera zarówno skrypt ImageMagick, jak i plik wejściowy PNG.Python 2, 4749.88
1018 bajtów
Prawdopodobnie wszyscy już zapomnieli o tym problemie, oprócz mnie ....
Problem ten zbytnio mnie zainteresował, szczególnie, że szybko stało się jasne, że podejścia wykorzystujące algorytmy kompresji obrazu były zdecydowanie na czele, ale jednak w jakiś sposób niezadowalające z estetycznego punktu widzenia. Podejścia oparte na optymalizacji zestawu prymitywów rysunkowych były w jakiś sposób bardziej przyjemne z punktu widzenia estetyki kodu, ale wydawały się zablokowane tuż powyżej wyniku 5000.
Metoda nneonneo, która nie używała kompresji obrazu z półki, pokonuje znak 5000, ale robi to poprzez zakodowanie małego obrazu i zwiększenie go.
Oto program, który używa tylko prymitywów rysunkowych, jest generowany automatycznie metodą optymalizacji i udaje mu się uzyskać wynik 4749.88.
który wygląda następująco:
oraz zrzut heksowy kodu:
Wykorzystuje szereg sztuczek używanych wcześniej tutaj:
Jako pierwszy prymityw umieszczam linię horyzontu, dzieląc obraz na dwa bloki o różnych kolorach. Następnie, jako podstawowy element prymitywny, użyłem koła przeciągniętego między dwoma punktami. Dla mnie wyglądało to trochę jak pociągnięcie pędzla, ale było możliwe wyrażenie w 7 bajtach. Do wyszukiwania wykorzystałem wyszukiwanie wzorców z przewodnikiem. Postępuje przez naprzemienne dodawanie prymitywu i optymalizację jego parametrów. Operacja pierwotna jest dodawana w punkcie, w którym zamazany podpisany błąd jest najwyższy. Parametry są optymalizowane przez wyczerpującą optymalizację linii w małej domenie, jedna po drugiej. Dodaje się od czterdziestu do pięćdziesięciu prymitywów i optymalizuje je indywidualnie. Następnie lista prymitywów jest przycinana do wielkości przez wyrzucenie prymitywów, które najmniej pomagają w uzyskaniu wyniku.
To wciąż nie bije wyniku Nneonneo. Aby pokonać ten wynik, wymagana była optymalizacja drugiego etapu, która ponownie przechodzi przez proces dodawania prymitywów na każdym z kilku poziomów filtrowania i wyrzucania prymitywów w celu przycięcia wygenerowanego programu do rozmiaru. Interesujący był dla mnie pomysł zastosowania tego do innych obrazów. Zastosowałem go do kilku innych obrazów i przedstawiłem dalsze szczegóły i animacje prymitywów narysowanych tutaj na moim blogu .
Dwa programy użyte do wygenerowania tego nie zmieszczą się w miejscu dostępnym na postach Stack Exchange, ale są na github: https://github.com/str4w/starrynight/tree/StackExchange
najpierw uruchamiana jest funkcja gwiaździsta, a następnie etap2optymalizacja. Powstały program znajduje się również w tym samym katalogu.
źródło
Matlab, wynik 5388,3
Bez wbudowanej kompresji. Głębia kolorów jest zmniejszana, dzięki czemu każdy piksel może być reprezentowany przez jeden znak do wydrukowania. Rozdzielczość jest zmniejszona. Jest to następnie zakodowane jako ciąg. Sam kod odwraca cały proces. Operacja zmiany rozmiaru używa jądra interpolacyjnego Lanczos 3.
źródło
zsh + bpgdec, 4159.061760861207
Tak, kolejne rozwiązanie BPG. Myślę, że w zasadzie służy to udowodnieniu, że BPG jest najlepszym obecnie dostępnym narzędziem do kompresji obrazu. Rozważ to ulepszenie w stosunku do oryginalnego rozwiązania BPG firmy Yallie .
Plik ma długość 1024 bajtów, dokładnie na granicy. Składa się z linii
a następnie nieprzetworzone dane wyjściowe BPG z
W systemie szesnastkowym jest to skrypt:
Plik wynikowy to
out.png
(bpgdec
lokalizacja domyślna), który wygląda następująco:Wydaje mi się to dość niesamowite
bpg
w zaledwie 996 bajtów dokładnie odtworzył ostre kontury drzewa po lewej stronie i wzgórza po prawej stronie. Ma nawet znaczące przybliżenie do wieży kościelnej! Poziom szczegółowości jest (jak dla mnie) imponujący w przypadku małego rozmiaru pliku. Oczywiście,bpgdec
sam w sobie nie jest małym programem, ale dla mnie jasne jest, że BPG jest o rząd wielkości lepszy niż JPEG do kompresji obrazu.Ponieważ to wykorzystuje
bpgdec
, ta odpowiedź oczywiście nie kwalifikuje się do nagrody.ZMIENIONO: Dodano
-n
argument,tail
aby był kompatybilny z GNUtail
.źródło
tail: cannot open ‘+2’ for reading
. Na Ubuntu potrzebuje-n +2
, co daje 1025 bajtów = /-n+2
co daje dokładnie 1024 bajty - spróbuj i daj mi znać, czy to działa. Zmienię swoją odpowiedź na zgodność.C, 6641
999 bajtów, używając tylko
stdio.h
imath.h
.Zrobiłem funkcję wypełnionego koła,
d()
która rysuje koncentryczne kolorowe koła RGB nad wartościami promienia r..0. Użyto tutaj 21 kręgów. Mógłbym wcisnąć jeszcze kilka, jeśli usunę więcej białych spacji, ale podoba mi się względna czytelność w obecnej postaci.Zorientowałem się, w jaki sposób zgrubnie ustawiam okrąg za pomocą warstw Gimp w
Difference
trybie. Poszukaj jasnych punktów, dodaj okrąg, powtórz. UżytoHistogram
narzędzia do wyboru, aby określić początkowe kolory do użycia.Korzystając z powyższego, uzyskałem wynik około 7700, ale pomyślałem, że mogę zrobić lepiej, poprawiając wartości koloru i promienia, więc napisałem trochę kodu rusztowania, aby zoptymalizować każdą wartość, modyfikując ją -10 .. + 10, ponownie renderowanie, uruchamianie walidatora (który przepisałem w C dla prędkości) i zapisywanie wartości, która dała najniższy wynik. Na koniec zrzuca tablicę wartości, którą wklejam z powrotem do kodu i rekompiluję. Przebiegłem kilka podań, co obniżyło wynik o około 1000. Następnie usunąłem kod rusztowania.
Kod,
źródło
((x-xo)*(x-xo) + (y-yo)*(y-yo)) <= (r*r)
) wydaje się krótsza i usuń zależność odmath.h
. Przy takim rozmiarze obrazu nie sądzę, aby coś miało szansę na przepełnienie.Python 3, wynik 5390,25, 998 bajtów
Użyłem symulowanego programu wyżarzania, aby dopasować prostokąty do kształtu Gwiaździstej Nocy. Następnie wykorzystuje rozmycie gaussowskie do wygładzenia prostych prostokątnych krawędzi.
Aby zaoszczędzić trochę bajtów, skompresowałem dane prostokąta do bazy 94.
źródło
Python 2, 5238.59 punktów
Nadszedł czas, aby opublikować własną odpowiedź. Oto zdjęcie
Kod wygląda następująco
Lub jako zrzut heksowy:
Po prostu rozpakowuje ten długi ciąg do parametrów rysowania 95 półprzezroczystych elips.
Podobnie jak wiele innych odpowiedzi, kod jest generowany przy użyciu algorytmu genetycznego. Korzysta z określonego rodzaju algorytmu genetycznego, który wymyśliłem, który nazywam „algorytmem puli genowej”, chociaż jest całkiem możliwe, że ktoś inny również go wynalazł i nadał mu inną nazwę. Zamiast populacji pojedynczych mamy 95 „pul genowych”, po jednej dla każdego genu. Każda pula genów zawiera 10000 różnych wersji genu. Gen zawiera parametry dla jednej elipsy (pozycja, kształt, kolor, alfa i jego miejsce w kolejności Z). Na każdej iteracji tworzymy dwa obrazy, wybierając jeden gen z każdej z 95 pul, a geny z obrazu o najniższej punktacji zastępują geny z obrazu o najgorszym wyniku z niewielką mutacją.
Uruchomiłem go do około 378 000. iteracji, co zajęło kilka dni. W tym momencie wynik wciąż spadał, ale naprawdę bardzo powoli, więc wątpię, że poprawi się znacznie bez pewnych zmian w algorytmie.
Oto kod algorytmu genetycznego:
Na koniec jest animacja pokazująca działanie algorytmu. Pokazuje najlepszy obraz wygenerowany do tej pory po każdych 1000 iteracjach. (Plik gif był zbyt duży, aby osadzić go w tym poście).
Prawdopodobnie można to poprawić poprzez (1) użycie sztuczki kodowania nneonneo w celu wtłoczenia większej ilości danych do łańcucha; (2) dodanie rozmycia gaussowskiego na końcu kodu renderowania (ale spowoduje to spowolnienie) i (3) dalsze ulepszanie algorytmu. W tej chwili bardzo szybko osiąga przyzwoity wynik, ale potem zmienia się naprawdę powoli - jeśli zwolnię w jakiś sposób początkową zbieżność, może w końcu znaleźć lepszy wynik. Być może w pewnym momencie zaimplementuję te rzeczy.
źródło
Starry ,
11428.189450210904.307927710874.1307958Gwiaździsta może nie być najlepszym językiem do tego, jednak z pewnością jest najbardziej odpowiedni.
Możesz wypróbować online, jednak wydaje się, że dane wyjściowe są obcięte, więc nie otrzymasz pełnego obrazu.
Ten program wysyła nieskompresowane pliki ppm do standardowego wyjścia.
Oto wyjście programu:
Wyjaśnienie
Aby program wypisał wszystkie wymagane 123.520 pikseli, podzieliłem obraz na 8 poziomych pasm i utworzyłem 7 pętli, każda pierwsza 6 drukuje pasmo, podczas gdy ostatnia drukuje dwa pasma tego samego koloru. Kod składa się z nagłówka, który informuje plik ppm, jak się sformatować, oraz 7 wyżej wymienionych pętli.
źródło
Python 2, 4684.46
1021 bajtów.
Używa bardzo podobnej metody dekodowania do kilku innych odpowiedzi, ale jest w Pythonie 2, więc jest to dane zakodowane w standardzie base64 zamiast base85.
Zakodowane dane to obraz w formacie 64 x 48 WebP.
Oto kod, za pomocą którego znalazłem najlepsze ustawienie rozmiaru i jakości obrazu. Ograniczyłem przestrzeń wyszukiwania, więc uruchomienie nie zajmuje więcej niż kilka minut.
źródło
Python 2,
5098.245080.044869,154852.874755.88589004Nie zastosowano wbudowanej dekompresji! Wystarczy narzędzie zmiany rozmiaru PIL i ręcznie dekodowany 16-kolorowy obraz. Dlatego powinien kwalifikować się do nagrody.
Program zawiera osadzone znaki inne niż ASCII. Ma 1024 bajty długości i wygląda następująco:
i szesnastkowo:
i generuje to zdjęcie:
Ten program narusza fakt, że możesz w zasadzie wrzucić surowe bajty do kodu źródłowego Pythona, pod warunkiem, że unikniesz NUL i odwrotnych ukośników.
Sam program składa się z 16-wejściowej palety (
|
oddzielny ciąg) i 16-kolorowego obrazu 40x41 (kodowanego 4 bitami na piksel i dekodowanego przez nadużycie.encode('hex')
). Rozmiar obrazu jest zmieniany do odpowiedniego rozmiaru za pomocą filtra bicubic i to wszystko.Rzeczywisty obraz został wygenerowany za pomocą ImageMagick:
i dane palety i obrazu zostały wyodrębnione z uzyskanego BMP. (Pamiętaj, że żądamy 18 kolorów od ImageMagick, ponieważ IM automatycznie wstawia niektóre nieużywane wpisy).
Paleta została nieco zmieniona, aby zmniejszyć liczbę znaków zmiany znaczenia w danych binarnych, a końcowe dane binarne zostały nieco zmodyfikowane ręcznie, aby całość zmieściła się w 1024 bajtach.
ZMIENIONO: trochę poprawiono kod i poprawiono dokładność, żądając 17 kolorów od ImageMagick.
ZMIENIONO: Wyłączenie ditheringu spowodowało ogromne poprawę wyników. Teraz osiąga wyniki znacznie poniżej 5000 i staje się konkurencyjny dzięki gotowym algorytmom kompresji!
ZMIENIONO: Dodanie
-filter Cosine
daje kolejną znaczną poprawę. Agresywne granie w golfa, dzięki @primo za sztuczkę BOM UTF-8, pozwoliło mi przypiąć kolejny rząd na obrazie, co jeszcze bardziej poprawiło wynik.źródło
!
Jest po obu stronach flankowany przez niedrukowalne postacie. Pełny kolor to#1d211e
lekko niebieskawy ciemny szary.#coding:latin
wiersz można zastąpić znakiem kolejności bajtów UTF-8:
(0xEF, 0xBB, 0xBF).zsh + FLIF + ImageMagick, 4358.14
Ponieważ BPG kradnie światło reflektorów jako stratny kodek, udoskonaliłem moje bezstratne, ekskluzywne podejście do korzystania z FLIF zamiast PNG, używając sztuczki Zsh @ nneonneo. ImageMagick jest tutaj używany tylko jako upscaler.
Szesnastkowy zrzut (tym razem z
xxd
, nie zdawałem sobie sprawy, żehexdump
w mojej ostatniej odpowiedzi był niestandardowy):Wygenerowałem skrypt używając ... innego skryptu:
źródło
Mathematica, 5076.54
Ważąc dokładnie 1024 bajty, w końcu udało mi się pokonać wynik Nneonneo ... dopóki nie poprawił go godzinę temu = (
Nie korzysta z „gotowych” algorytmów kompresji.
(Lepszy opis później)
źródło
HTML / JavaScript,
10855.838000.55 (± ~ 5, w zależności od przeglądarki)Wyniki mogą się nieznacznie różnić z powodu różnic w przeglądarce lub GPU.
Musisz kliknąć prawym przyciskiem myszy> Zapisz obraz jako, aby zapisać dane obszaru roboczego jako obraz, ale to jedyna wymagana interakcja.
Użyłem GIMP, aby wybrać określone obszary i znaleźć ich średnią. W szczególności bardzo pomocne były narzędzie próbnika kolorów i funkcja „różnicowania warstw” .
Próba nr 1 (10855,83)
Próba # 2 (8000,55)
źródło
Scala, 6003,56
993 znaków. Jednym importem jest biblioteka obrazów Scala . Drugi import to koder podstawowy 91 .
To są dane base91:
źródło
Java, wynik 12251,19
Na podstawie tej odpowiedzi Mathematica , ale z większą ilością prostokątów. Prawdopodobnie będę dalej to modyfikować później.
Wyniki:
Niektóre poprzednie wersje:
źródło
Python 2 (bez wbudowanej kompresji), wynik 4497.730
Wykorzystuje to samo ręcznie zdekodowane 16-kolorowe podejście do obrazu, jak w mojej poprzedniej odpowiedzi , ale tym razem faktycznie zastosowałem strategię optymalizacji spadku gradientu, aby znacznie poprawić wynik. Poprzednie zgłoszenie uzyskało 4755,886 punktów, podczas gdy nowe zgłoszenie uzyskało wynik lepszy o ponad 250 punktów, pokonując w tym procesie wiele wbudowanych metod kompresji.
Tak jak poprzednio, końcowy program ma dokładnie 1024 bajty. W rzeczywistości nieprzetworzone dane wyjściowe algorytmu optymalizacji zawierały cztery bajty, które zostały oznaczone znakiem ucieczki (
\0
) i które musiałem „podkręcić”, aby zmniejszyć liczbę bajtów do 1024 bajtów. Bez krówki program 1028 bajtów uzyskałby 4490,685 - 7 punktów lepiej.Podstawową ideą jest wspólna optymalizacja palety i danych. W jednej iteracji przeszukuję wszystkie poprawki palety (w zasadzie każdą zmodyfikowaną paletę, która różni się o 1 w pewnym składniku koloru) i wybieram zmodyfikowaną paletę, która najlepiej poprawia wynik. Następnie przeszukuję wszystkie poprawki danych (każdą zmodyfikowaną tablicę indeksów, w której jeden piksel jest zmieniany na inny wpis palety) i wybieram modyfikację, która zmniejsza wynik (tutaj nie dbam o najlepsze, ponieważ nie chcesz bezowocnie przeszukiwać całą przestrzeń ponad 25 000 poprawek podczas każdej iteracji).
Na koniec, podczas generowania końcowego wyniku programu, uruchamiam kolejny przebieg optymalizacji, który przestawia paletę, aby zminimalizować liczbę ukośników wstecznych wymaganych w końcowym wyniku (np. W przypadku programu pokazanego poniżej, paleta została przestawiona za pomocą tabeli szesnastkowej „0e3428916b7df5ca”).
To podejście przyniosło zarówno znaczącą poprawę liczbową, jak i percepcyjną w stosunku do poprzedniego naiwnego podejścia ImageMagick. Wyniki poprzednich zgłoszeń:
I nowy wynik wysyłania:
Nowe podejście oparte na optymalizacji ma znacznie więcej szczegółów i dokładniejsze odwzorowanie kolorów.
Oto zrzut końcowy programu końcowego:
Nadal jest miejsce do poprawy. Na przykład prosty histogram pokazuje, że niektóre kolory są rzadko używane:
Sugeruje to, że ponownie zrównoważona paleta może poprawić wydajność, być może wystarczająca, aby złapać rozwiązanie BPG 5. miejsca. Wątpię jednak, czy to podejście optymalizacyjne (a właściwie wszystko, co nie wymaga nadzwyczajnej maszynerii H.265) może zająć pierwsze miejsce w implementacji BPG.
źródło
Perl,
5955.968781245149.56218378Patrząc na podejście „niezadrukowanego bełkotu”, zdecydowałem, że mogę spróbować również w Perlu. Znów nie znam Perla, więc jestem pewien, że można to poprawić (w rzeczywistości najbardziej oczywista poprawa, zmniejszenie do 7 bajtów na elipsę poprzez pominięcie kanału alfa, została już zaimplementowana dla następnej wersji, ale „ Nadal pracuję nad innymi częściami tego kodu; myślę też, że cały biznes pop / push można bardziej pograć w golfa).
Nie sądzę, aby to faktycznie działało na komputerach z systemem Windows (nie mogę przetestować), ponieważ nie mogłem wymyślić łatwego sposobu otwarcia sekcji DANYCH w trybie binarnym - jednak działało to na moich komputerach z systemem Linux.
Byłem w stanie nadal używać tego samego kodu GA do tworzenia:
Gdzie wyjście xxd to:
Który generuje obraz:
Interesujące jest to, że pomimo lepszych wyników, obraz wygląda dla mnie nieco gorzej - zbyt wiele dzieje się ze wszystkimi dodatkowymi elipsami, w jakiś sposób łatwiej jest sobie z nimi poradzić wizualnie.
Stare wyniki
Po zobaczeniu odpowiedzi jamieguinana użyłem elips jako prymitywnego rysunku, ponieważ w Perlu mam dostęp do biblioteki GD do rysowania. W ogóle nie jestem ekspertem od Perla, więc wszelkie sugestie byłyby przydatne. Użyłem algorytmu genetycznego zmodyfikowanego z mojej odpowiedzi w C ++ .
Wygląda na to, że działa dobrze, ale szczerze mówiąc, jestem trochę rozczarowany wynikiem. Prawdopodobnie mógłbym pozwolić, aby działał trochę dłużej, ponieważ łatwo można zauważyć, że kilka elips nie jest w optymalnych pozycjach. Jednak nawet teraz wygląda mi to ładniej w porównaniu do rozwiązania opartego na prostokącie.
źródło
Bash + Netpbm,
4558,54394,1AKTUALIZACJA: Poprawiłem wynik, używając SPIHT zamiast FIASCO.
SPIHT to akronim oznaczający partycjonowanie zbiorów w drzewach hierarchicznych. Jest to bardzo wydajny format kompresji obrazu oparty na falkach.
I skurczył oryginalnej PNG przez kwartał, a następnie konwertowane do PNM korzystania
pngtopnm
z Netpbm v. 10.68, a następnie usuwa nagłówek i przekształcone dane RAW do SPIHT zcodecolr in.raw out.spi 80 96 0.95
i dostał plik obrazu 913-bajtowy. Następnie przekonwertowałem go z powrotem na RAW przy użyciudecdcolr -s out.spi out.raw 0.95
, następnie przekonwertowałem na format PNM przy użyciurawtoppm -bgr 96 80
, odwróciłem przy użyciupamflip -tb
, skalowałem do oryginalnego rozmiaru przy użyciupamscale -xsize 386 -ysize 320 -filter sinc
i zapisałem w pliku PNM, który jest odczytywany przez PIL. Oto mój skrypt (1 KB):Oto dane wyjściowe PNG:
Poniżej moja wcześniejsza odpowiedź:
FIASCO to skrót od Fractal Image And Sequence COdec, jest to wysoce wydajna implementacja stratnej kompresji fraktalnej .
I skurczył oryginalnej PNG przez kwartał, a następnie konwertowane do PNM korzystania
pngtopnm
z Netpbm v. 10.68, a następnie konwertowane na fiasko zpnmtofiasco -q=14 -z=3
i dostał plik obrazu 969-bajtowy. Następnie przekonwertowałem go z powrotem dofiascotopnm
formatu PNM , powiększając go do rozmiaru oryginalnego, używającpamscale -xyfill 386 320
i zapisałem w pliku PNM, który jest odczytywany przez PIL. Oto mój skrypt (1 KB):Właściwie po raz pierwszy zrobiłem to w systemie Windows
cmd
, ale skrypt nie zmieścił się w 1 KB. Potem przepisałem tobash
. Oto dane wyjściowe PNG:źródło
Ruby, 7834.38
To była zabawa!
Użyłem programu generatora ruby do napisania skryptu ruby w następujący sposób:
Podczas gdy wygenerowany plik ruby ma mniej niż 1024 bajty:
Zauważ, że mój algorytm oceniania losowo próbkuje wiązkę kolorów i wybiera ten, który daje najmniejszą różnicę w stosunku do ORIGINAL.png. Ponieważ jest to probabilistyczne, kilka razy powtórzyłem skrypt i wybrałem najniższy wynik.
Oto mój najlepszy skrypt do tej pory:
Wygenerował następujący obraz, który uzyskał 7834 punkty:
Aby zobaczyć, jak wymyślił to mój algorytm, oto animowany plik GIF pokazujący, jak dzieli prostokąty:
Mój kod jest cały w GitHub na https://github.com/jrotter/starry_night_contest
źródło
Java, 9215.38294502
Odczytywanie obrazu jako GIF-a o rozmiarze 8x6 pikseli (!) Z ciągu zakodowanego w Base64 (zawierającego 368 znaków) i skalowanie go dwuliniowo.
EDYCJA: Wynik, zgodnie z żądaniem w komentarzach, pokazano na tym obrazku:
źródło
Python 3, 5797.125628604383
Program kompresji najpierw obcina bity obrazu, a następnie konwertuje bazę 2 na bazę 36.
Program dekodujący robi to w odwrotnej kolejności i zmienia rozmiar obrazu.
źródło