Giting.
낮의 Giting

홈 · 생성 아트 · 이미지 · 기하 도형 · procgen · Go · MIT

primitive 대표 이미지 0373c21 · 코어 마지막 커밋 2020-05-03 (repo pushed 2024-07-22) · shallow clone(depth 1000) 기준, commitCount 는 하한값
분석 리포트실측 정보사이트적용점원문 README

도형을 하나씩 얹어, 사진을 다시 그린다

primitive 는 이미지를 기하 도형으로 재현하는 생성 아트 도구다(★13,222, MIT). 빈 화면에 도형을 하나씩 추가하는데, 매번 원본과의 픽셀 차이를 가장 많이 줄이는 위치와 색을 언덕오르기와 담금질로 찾는다. 삼각형·타원·사각형·회전 사각형·베지어·다각형 6종 중에 고를 수 있다. 도형 200개쯤 얹으면 원본을 닮았으면서도 완전히 새로운 추상화가 나온다. 저자는 생성 그래픽스계의 거장 Michael Fogleman 이다.

재현 예시 (원문 README · michaelfogleman.com, MIT)

말보다 그림이 빠르다. 아래는 저장소 README 에 실린 실제 결과다. 전체 이미지는 이 페이지의 '원문 README' 탭에서 볼 수 있다.

도형이 하나씩 쌓이는 과정 · Pencils
도형이 하나씩 쌓이는 과정 · Pencils
삼각형 200개로 재현한 초상 · Example
삼각형 200개로 재현한 초상 · Example
도형 재현 예시 · Example
도형 재현 예시 · Example
다른 도형 모드 · Pyramids
다른 도형 모드 · Pyramids

작고 완성됐다

규모는 2,568줄인데 성격이 특이하다. 핵심 알고리즘 전체가 Go 1,955줄이고, 나머지는 트위터 봇 파이썬 몇 백 줄이다. 큰 프레임워크가 아니라 잘 벼려진 아이디어다. 그리고 코어 코드가 2020년 이후 멈췄고 테스트도 CI 도 없다. 방치가 아니라 완성이다. 끝난 프로젝트라 업데이트를 기대할 대상은 아니고, 이슈 응답도 느리다. 표본 25건 중 15건에 답이 달렸고 중앙값이 538시간, 약 22일이다.

우리 독자에게

이미지를 기하 아트로 바꾸는 효과를 고민한다면 이건 정본 레퍼런스다. 일회성 아트라면 지금도 Go 바이너리로 바로 돌아가고, 제품 효과로 넣고 싶으면 2천 줄짜리 알고리즘을 이해하고 자기 스택으로 옮기면 된다. 핵심은 '도형을 하나 추가하고, 오차가 줄면 채택'을 반복하는 탐욕적 최적화다. 이 루프만 잡으면 도형도 목적함수도 얼마든지 변주할 수 있다. 판정은 참고용, 거장의 작고 완성된 고전이다.

실측 정보

HEAD 커밋
0373c21 · 코어 마지막 커밋 2020-05-03 (repo pushed 2024-07-22) · shallow clone(depth 1000) 기준, commitCount 는 하한값
무엇
이미지를 기하 도형으로 재현하는 생성 아트 도구. 빈 화면에 도형(삼각형·타원·사각형·회전 사각형·베지어·다각형 6종)을 하나씩 추가하며, 원본과의 픽셀 차이를 줄이는 방향으로 언덕오르기와 담금질(hill climbing + simulated annealing)을 돌린다. N개의 도형만으로 사진을 추상화한다. Go 라이브러리 겸 CLI
규모 / 실측
2,568줄(아주 작다) · 파일 30 · Go 18파일 1,955줄(핵심 알고리즘 전부) + Python 3파일 420줄(그중 bot/main.py 269 = @PrimitivePic 트위터 봇) · 테스트 0 · CI 0
활발 / 유지
커밋 152 · 코어 마지막 2020-05-03 · 최근 90일 0 → 사실상 완성돼 멈춘 프로젝트 · 태그 0 · ⚠️ 이슈 응답 느림(표본 25건 중 15건 응답·중앙값 538시간, 약 22일)
저자 / 계보
Michael Fogleman. 생성 그래픽스계에서 ln·pt·gg·nes 등을 만든 거장이다. primitive 는 데모 사이트 primitive.lol, 트위터 봇 @PrimitivePic, 맥 앱으로 확장됐던 히트작이다
라이선스 / 실행
MIT. 실행에는 Go 가 필요하다(바이너리를 받아 CLI 로 이미지에 돌린다). 이 리포트는 클론 실측이며 실행 시연은 환경 제약으로 생략했다

사이트 실측

주소
primitive.lol
응답 · 첫 화면
200 · 1.4초 (데스크톱)
요청 · 전송량
10개 · 96KB
깨진 요청 · 콘솔 오류
1개 · 0개
폰(390px)
가로 넘침 없음
디자인
어두운 화면 · sans-serif

홈페이지를 실제 크롬으로 띄워 첫 화면까지만 쟀습니다(2026-10-16 · 데스크톱 1440px · 폰 390px). 로그인 · 입력 같은 동작은 하지 않았고, 사이트 상태는 리포 품질과 따로 봅니다. 색 칸은 바탕 · 면 · 글자 · 흐린 글자 · 강조 · 선, 점선은 「못 잼」.

적용점

  • 이럴 때 유용사진을 기하 아트로 바꾸는 효과가 필요할 때. 일회성 아트라면 지금도 CLI 로 바로 돌아간다. 무엇보다 알고리즘이 2천 줄이 안 돼 읽고 배우기 좋고, MIT 라 자기 스택으로 옮겨 제품 효과로 넣기도 자유롭다. 실제로 여러 언어 포팅이 있다.
  • 이럴 땐 주의유지보수되는 의존성으로 붙일 대상이 아니다. 코어 코드가 2020년 이후 멈췄고 테스트 0·CI 0이며 이슈 응답 중앙값이 22일이다(방치가 아니라 '끝난' 프로젝트의 성격). 실시간 대용량 처리가 목적이라면 도형마다 반복 최적화라 느릴 수 있다는 것도 감안한다.
  • 먼저 볼 것값은 코드 의존이 아니라 아이디어에 있다. 핵심은 '도형을 하나 추가하고, 그게 원본과의 오차를 줄이면 채택'을 반복하는 탐욕적 최적화다. 이 루프를 이해하면 삼각형 대신 다른 도형, 다른 목적함수로 얼마든지 변주할 수 있다. Go 바이너리로 쓰든, 이식하든 선택이다.
◈ 참고용

판정은 참고용이다. '쓰는 도구'라기보다 읽고 배우는 정본에 가깝기 때문이다. 먼저 왜 눈길을 끄는지 보자. 저자가 Michael Fogleman 이다. 생성 그래픽스에서 ln, pt, gg 같은 작은 명작들을 연달아 내놓은 사람이고, primitive 는 그중에서도 가장 널리 퍼진 히트작이다. 사진 한 장을 도형 200개로 다시 그리면 원본을 닮았으면서도 완전히 새로운 추상화가 나온다. 그 알고리즘 전체가 Go 2천 줄이 안 된다는 게 이 프로젝트의 우아함이다. 큰 프레임워크가 아니라 잘 벼려진 아이디어라, 읽으면 하루 만에 이해하고 다른 언어로 옮길 수 있다. 다만 성격을 알고 봐야 한다. 코어 코드가 2020년 이후 멈췄고 테스트도 CI 도 없으며 이슈 응답 중앙값이 22일이다. 이건 방치가 아니라 완성이다. 끝난 프로젝트라 업데이트를 기다리거나 패키지처럼 의존할 대상이 아니다. 일회성 아트가 필요하면 지금도 CLI 로 돌리면 되고, 제품 효과로 넣고 싶으면 2천 줄짜리 알고리즘을 이해하고 이식하는 게 맞다. 이미지 콘텐츠를 다루는 팀이라면 '사진을 기하 아트로' 같은 효과의 정본 레퍼런스로 오래 참고할 값이 있다. MarkovJunior 와 같은 계열이다. 거장의 작고 완성된 고전.

원문 README

아래는 fogleman/primitive 저장소의 README 원문입니다(GitHub 렌더, 수집 시점 기준). 저작권은 원 프로젝트에 있으며 원 저장소의 라이선스를 따릅니다. GitHub에서 보기

README 전문 펼치기

Primitive Pictures

Reproducing images with geometric primitives.

Example

How it Works

A target image is provided as input. The algorithm tries to find the single most optimal shape that can be drawn to minimize the error between the target image and the drawn image. It repeats this process, adding one shape at a time. Around 50 to 200 shapes are needed to reach a result that is recognizable yet artistic and abstract.

Primitive for macOS

Now available as a native Mac application!

https://primitive.lol/

Twitter

Follow @PrimitivePic on Twitter to see a new primitive picture every 30 minutes!

The Twitter bot looks for interesting photos using the Flickr API, runs the algorithm using randomized parameters, and posts the picture using the Twitter API.

You can tweet a picture to the bot and it will process it for you.

Command-line Usage

Run it on your own images! First, install Go.

go get -u github.com/fogleman/primitive
primitive -i input.png -o output.png -n 100

Small input images should be used (like 256x256px). You don't need the detail anyway and the code will run faster.

Flag Default Description
i n/a input file
o n/a output file
n n/a number of shapes
m 1 mode: 0=combo, 1=triangle, 2=rect, 3=ellipse, 4=circle, 5=rotatedrect, 6=beziers, 7=rotatedellipse, 8=polygon
rep 0 add N extra shapes each iteration with reduced search (mostly good for beziers)
nth 1 save every Nth frame (only when %d is in output path)
r 256 resize large input images to this size before processing
s 1024 output image size
a 128 color alpha (use 0 to let the algorithm choose alpha for each shape)
bg avg starting background color (hex)
j 0 number of parallel workers (default uses all cores)
v off verbose output
vv off very verbose output

Output Formats

Depending on the output filename extension provided, you can produce different types of output.

  • PNG: raster output
  • JPG: raster output
  • SVG: vector output
  • GIF: animated output showing shapes being added - requires ImageMagick (specifically the convert command)

For PNG and SVG outputs, you can also include %d, %03d, etc. in the filename. In this case, each frame will be saved separately.

You can use the -o flag multiple times. This way you can save both a PNG and an SVG, for example.

Progression

This GIF demonstrates the iterative nature of the algorithm, attempting to minimize the mean squared error by adding one shape at a time. (Use a ".gif" output file to generate one yourself!)

Static Animation

Since the algorithm has a random component to it, you can run it against the same input image multiple times to bring life to a static image.

Pencils

Creative Constraints

If you're willing to dabble in the code, you can enforce constraints on the shapes to produce even more interesting results. Here, the rectangles are constrained to point toward the sun in this picture of a pyramid sunset.

Pyramids

Shape and Iteration Comparison Matrix

The matrix below shows triangles, ellipses and rectangles at 50, 100 and 200 iterations each.

Matrix

How it Works, Part II

Say we have a Target Image. This is what we're working towards recreating. We start with a blank canvas, but we fill it with a single solid color. Currently, this is the average color of the Target Image. We call this new blank canvas the Current Image. Now, we start evaluating shapes. To evaluate a shape, we draw it on top of the Current Image, producing a New Image. This New Image is compared to the Target Image to compute a score. We use the root-mean-square error for the score.

Current Image + Shape => New Image
RMSE(New Image, Target Image) => Score

The shapes are generated randomly. We can generate a random shape and score it. Then we can mutate the shape (by tweaking a triangle vertex, tweaking an ellipse radius or center, etc.) and score it again. If the mutation improved the score, we keep it. Otherwise we rollback to the previous state. Repeating this process is known as hill climbing. Hill climbing is prone to getting stuck in local minima, so we actually do this many different times with several different starting shapes. We can also generate N random shapes and pick the best one before we start hill climbing. Simulated annealing is another good option, but in my tests I found the hill climbing technique just as good and faster, at least for this particular problem.

Once we have found a good-scoring shape, we add it to the Current Image, where it will remain unchanged. Then we start the process again to find the next shape to draw. This process is repeated as many times as desired.

Primitives

The following primitives are supported:

  • Triangle
  • Rectangle (axis-aligned)
  • Ellipse (axis-aligned)
  • Circle
  • Rotated Rectangle
  • Combo (a mix of the above in a single image)

More shapes can be added by implementing the following interface:

type Shape interface {
	Rasterize() []Scanline
	Copy() Shape
	Mutate()
	Draw(dc *gg.Context)
	SVG(attrs string) string
}

Features

  • Hill Climbing or Simulated Annealing for optimization (hill climbing multiple random shapes is nearly as good as annealing and faster)
  • Scanline rasterization of shapes in pure Go (preferable for implementing the features below)
  • Optimal color computation based on affected pixels for each shape (color is directly computed, not optimized for)
  • Partial image difference for faster scoring (only pixels that change need be considered)
  • Anti-aliased output rendering

Inspiration

This project was originally inspired by the popular and excellent work of Roger Johansson - Genetic Programming: Evolution of Mona Lisa. Since seeing that article when it was quite new, I've tinkered with this problem here and there over the years. But only now am I satisfied with my results.

It should be noted that there are significant differences in my implementation compared to Roger's original work. Mine is not a genetic algorithm. Mine only operates on one shape at a time. Mine is much faster (AFAIK) and supports many types of shapes.

Examples

Here are more examples from interesting photos found on Flickr.

Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example Example

의견

판정에 대한 반론, 도입 경험, 정정 제보를 환영합니다. GitHub 계정으로 참여합니다.

이것도 재봤습니다

전체 리포트 →