당신의 임무는 흑백 윤곽선 이미지를 가져 와서 컬러로 채우는 프로그램을 만드는 것입니다. 각 지역을 구분하는 방법과 채울 색상을 결정하는 것은 사용자에게 달려 있습니다 (RNG를 사용할 수도 있음).
예를 들면 다음과 같습니다.
보시다시피 저는 MS Paint와 관련하여 뛰어난 칼리버의 예술가입니다.
채점
이것은 인기 경연 대회이므로 가장 많은 순 투표 수를 얻은 답이 이깁니다. 유권자들은 다음과 같이 답변을 판단하도록 권장됩니다
- 입력 기준 : 흰색 / 밝은 회색 배경과 검은 색 / 진한 회색 윤곽선으로 구성된 모든 이미지
- 채색이 얼마나 잘 이루어 졌습니까? 위와 달리 흰색이 거의 없거나 전혀없는 것을 의미합니다 (구름에 흰색을 사용하려는 경우가 아니라면)
- 특정 섹션에 사용 된 색상의 사용자 정의
- 시스템이 다양한 이미지에서 다양하게 작동하는 정도
- 이미지 당 프로그램 소요 시간을 게시하십시오. 우리는 코드 골프를하지 않을 수도 있지만 더 짧고 빠르며 효율적인 코드는 더 나은 것으로 간주해야합니다
- 새 이미지를 화면이나 파일로 출력해야합니다 (대답에 표시 될 수 있도록 2MB 이하).
- 해당 이미지 유형으로 출력하기로 선택한 이유를 정당화하고 코드 작동을 설명 / 설명하십시오.
- 적용되는 각 모양에 사용되는 색상의 적용 성 (실제 색상 구성, 즉 잔디는 녹색, 나무 울타리는 갈색 등)
“나는 각 영역을 무작위로 채색 할 수 있지만,”울타리 “를 식별하고 유사하게 채색 할 수 있다면 공감할 가치가있는 것입니다. -나단 메릴
이 같은보고 되고 인기도 대회, 당신은 또한 선택적으로 판단 할 수있다 :
- 전반적인 호소 (이미지가 얼마나 좋아 보이는지)
- 예술적 감각; 음영 또는 수채화 스타일의 채색 등으로 프로그래밍 할 수있는 경우
일반적으로 금식 프로그램과 최고 대중 투표로 최고 품질의 가장 작은 출력 이미지 (파일 크기)가 이길 것입니다.
사용해야 할 다른 판단 사양이있는 경우이 게시물의 주석에서 권장하십시오.
예
나는 아무것도 소유하지 않는다. 모든 예제 이미지는 크리에이티브 커먼즈 라이센스입니다.
출처 : https://pixabay.com/ro/stejar-arbore-schi%C5%A3%C4%83-natura-303890/
출처 : http://www.freestockphotos.biz/stockphoto/10665
출처 : http : / /crystal-rose1981.deviantart.com/art/Dragon-Tattoo-Outline-167320011
출처 : http://jaclynonacloudlines.deviantart.com/art/Gryphon-Lines-PF-273195317
출처 : http://captaincyprus.deviantart.com / art / Dragon-OutLine-331748686
출처 : http://electric-meat.deviantart.com/art/A-Heroes-Farewell-280271639
출처 : http://movillefacepalmplz.deviantart.com/art/Background-The-Pumpkin -오래 된 농장 -342865938
편집 : 비 흑백 / 픽셀 및 흑백 / 회색 대신 회색을 포함 할 수있는 일부 이미지를 일으키는 선의 앤티 앨리어싱으로 인해 처리 할 수있는 보너스 과제가 있습니다. 내 의견으로는 충분히 쉬워야한다.
답변
스펙트럼 에어 브러싱 (Python, PIL, scipy)
이것은 정교한 수학적 알고리즘을 사용하여 다채로운 넌센스를 생성합니다. 이 알고리즘은 Google의 PageRank 알고리즘과 관련이 있지만 웹 페이지 대신 픽셀에 적용됩니다.
홍수 접근 기반 방법과 달리 닭과 나무와 같은 이미지에 완전히 대처할 수 없을 것이라고 생각했기 때문에이 접근법을 취했습니다. 검은 선으로 완전히 둘러싸이지 않은 모양이 있습니다. 보시다시피, 그것은 하늘의 다른 부분에서 다른 색상으로 착색되는 경향이 있지만 일종의 작품입니다.
수학적으로 생각하는 것 : 그것이하는 일은 본질적으로 이미지의 while 픽셀의 인접 그래프를 구성 한 다음 그래프 Laplacian의 상위 25 개의 고유 벡터를 찾는 것입니다. (어둡지 않은 픽셀을 포함하기 때문에 연결의 가중치를 줄입니다. 앤티 앨리어싱을 처리하는 데 도움이되고 일반적으로 더 나은 결과를 제공하는 것처럼 보입니다.) 고유 벡터를 발견하면 역 고유 값으로 가중치를 부여하여 출력 이미지의 RGB 구성 요소를 형성하는 임의의 선형 조합.
계산 시간을 고려하여 이미지를 모두 축소하기 전에 축소 한 다음 다시 확대 한 다음 원래 이미지를 곱합니다. 그래도 입력 이미지에 따라 내 컴퓨터에서 약 2 분에서 10 분 정도 걸리지 만 어떤 이유로 닭은 17 분이 걸렸습니다.
실제로 각 고유 벡터의 색상과 강도를 제어 할 수있는 대화식 앱을 만들어이 아이디어를 유용한 것으로 바꿀 수 있습니다. 이렇게하면 하늘을 여러 섹션으로 나누고 이미지의 관련 기능을 선택하는 섹션을 페이드 아웃 할 수 있습니다. 그러나 나는 이것을 직접 할 계획이 없다 🙂
출력 이미지는 다음과 같습니다.
(호박에서는 잘 작동하지 않았으므로 생략합니다.)
그리고 여기 코드가 있습니다 :
import sys
from PIL import Image
import numpy as np
import scipy.sparse as sp
import scipy.sparse.linalg as spl
import os
import time
start_time = time.time()
filename = sys.argv[1]
img = Image.open(filename)
orig_w, orig_h = img.size
# convert to monochrome and remove any alpha channel
# (quite a few of the inputs are transparent pngs)
img = img.convert('LA')
pix = img.load()
for x in range(orig_w):
for y in range(orig_h):
l, a = pix[x,y]
l = (255-a) + a*l/255
a = 255
pix[x,y] = l,a
img = img.convert('L')
orig_img = img.copy()
# resize to 300 pixels wide - you can get better results by increasing this,
# but it takes ages to run
orig_w, orig_h = img.size
print "original size:", str(orig_w)+ ', ' + str(orig_h)
new_w = 300
img = img.resize((new_w, orig_h*new_w/orig_w), Image.ANTIALIAS)
pix = img.load()
w, h = img.size
print "resizing to", str(w)+', '+str(h)
def coords_to_index(x, y):
return x*h+y
def index_to_coords(i):
return (int(i/h), i%h)
print "creating matrix"
A = sp.lil_matrix((w*h,w*h))
def setlink(p1x, p1y, p2x, p2y):
i = coords_to_index(p1x,p1y)
j = coords_to_index(p2x,p2y)
ci = pix[p1x,p1y]/255.
cj = pix[p2x,p2y]/255.
if ci*cj > 0.9:
c = 1
else:
c = 0.01
A[i,j] = c
return c
for x in range(w):
for y in range(h):
d = 0.
if x>0:
d += setlink(x,y,x-1,y)
if x<w-1:
d += setlink(x,y,x+1,y)
if y>0:
d += setlink(x,y,x,y-1)
if y<h-1:
d += setlink(x,y,x,y+1)
i = coords_to_index(x,y)
A[i,i] = -d
A = A.tocsr()
# the greater this number, the more details it will pick up on. But it increases
# execution time, and after a while increasing it won't make much difference
n_eigs = 25
print "finding eigenvectors (this may take a while)"
L, V = spl.eigsh(A, k=n_eigs, tol=1e-12, which='LA')
print "found eigenvalues", L
out = Image.new("RGB", (w, h), "white")
out_pix = out.load()
print "painting picutre"
V = np.real(V)
n = np.size(V,0)
R = np.zeros(n)
G = np.zeros(n)
B = np.zeros(n)
for k in range(n_eigs-1):
weight = 1./L[k]
R = R + V[:,k]*np.random.randn()*weight
G = G + V[:,k]*np.random.randn()*weight
B = B + V[:,k]*np.random.randn()*weight
R -= np.min(R)
G -= np.min(G)
B -= np.min(B)
R /= np.max(R)
G /= np.max(G)
B /= np.max(B)
for x in range(w):
for y in range(h):
i = coords_to_index(x,y)
r = R[i]
g = G[i]
b = B[i]
pixval = tuple(int(v*256) for v in (r,g,b))
out_pix[x,y] = pixval
out = out.resize((orig_w, orig_h), Image.ANTIALIAS)
out_pix = out.load()
orig_pix = orig_img.load()
for x in range(orig_w):
for y in range(orig_h):
r,g,b = out_pix[x,y]
i = orig_pix[x,y]/255.
out_pix[x,y] = tuple(int(v*i) for v in (r,g,b))
fname, extension = os.path.splitext(filename)
out.save('out_' + fname + '.png')
print("completed in %s seconds" % (time.time() - start_time))
답변
파이썬 2 + PIL도, 나의 첫 색칠 공부
import sys, random
from PIL import Image
def is_whitish(color):
return sum(color)>500
def get_zone(image, point, mask):
pixels = image.load()
w, h = image.size
s = [point]
while s:
x, y = current = s.pop()
mask[current] = 255
yield current
s+=[(i,j) for (i,j) in [(x,y-1),(x,y+1),(x-1,y),(x+1,y)] if 0<=i<w and 0<=j<h and mask[i,j]==0 and is_whitish(pixels[i,j])]
def get_zones(image):
pixels = I.load()
mask = Image.new('1',image.size).load()
w,h = image.size
for y in range(h):
for x in range(w):
p = x,y
if mask[p]==0 and is_whitish(pixels[p]):
yield get_zone(image, p, mask)
def apply_gradient(image, mincolor, maxcolor, points):
minx = min([x for x,y in points])
maxx = max([x for x,y in points])
miny = min([y for x,y in points])
maxy = max([y for x,y in points])
if minx == maxx or miny==maxy:
return
diffx, diffy = (maxx - minx), (maxy-miny)
stepr = (maxcolor[0] - mincolor[0] * 1.0) / diffy
stepg = (maxcolor[1] - mincolor[1] * 1.0) / diffy
stepb = (maxcolor[2] - mincolor[2] * 1.0) / diffy
r,g,b = mincolor
w, h = (abs(diffx+1),abs(diffy+1))
tmp = Image.new('RGB', (w,h))
tmppixels = tmp.load()
for y in range(h):
for x in range(w):
tmppixels[x,y] = int(r), int(g), int(b)
r+=stepr; g+=stepg; b+=stepb
pixels = image.load()
minx, miny = abs(minx), abs(miny)
for x,y in points:
try:
pixels[x,y] = tmppixels[x-minx, y-miny]
except Exception, e:
pass
def colors_seq():
yield (0,255,255)
c = [(255,0,0),(0,255,0),(0,0,139)]
i=0
while True:i%=len(c);yield c[i];i+=1
def colorize(image):
out = image.copy()
COLORS = colors_seq()
counter = 0
for z in get_zones(image):
c1 = COLORS.next()
c2 = (0,0,0) if counter == 0 else (255,255,255)
if counter % 2 == 1:
c2, c1 = c1, c2
apply_gradient(out, c1, c2, list(z))
counter +=1
return out
if __name__ == '__main__':
I = Image.open(sys.argv[-1]).convert('RGB')
colorize(I).show()
나는 지역을 ‘그라데이션’으로 채우고 다른 색주기를 사용한다는 점을 제외하고는 CarpetPython과 거의 동일했습니다.
내 컴퓨터의 계산 시간 :
-
이미지 1 (중국 용) : 실제 0m2.862s 사용자 0m2.801s sys 0m0.061s
-
이미지 2 (그리폰) : 실제 0m0.991s 사용자 0m0.963s sys 0m0.029s
-
이미지 3 (유니콘 드래곤) : 실제 0m2.260s 사용자 0m2.239s 시스템 0m0.021s
답변
파이썬 2와 PIL : 사이키델릭 월드
간단한 알고리즘을 사용하여 하얀색 영역을 순환 팔레트의 색상으로 채 웁니다. 결과는 매우 화려하지만 실제와는 다릅니다.
이 그림에서 “흰색”부분은 흰색이 아닙니다. 회색 음영도 테스트해야합니다.
파이썬 2.7의 코드 :
import sys
from PIL import Image
WHITE = 200 * 3
cs = [60, 90, 120, 150, 180]
palette = [(199,199,199)] + [(R,G,B) for R in cs for G in cs for B in cs]
def fill(p, color):
perim = {p}
while perim:
p = perim.pop()
pix[p] = color
x,y = p
for u,v in [(x+dx, y+dy) for dx,dy in [(-1,0), (1,0), (0,1), (0,-1)]]:
if 0 <= u < W and 0 <= v < H and sum(pix[(u,v)]) >= WHITE:
perim.add((u,v))
for fname in sys.argv[1:]:
print 'Processing', fname
im = Image.open(fname)
W,H = im.size
pix = im.load()
colornum = 0
for y in range(H):
for x in range(W):
if sum(pix[(x,y)]) >= WHITE:
thiscolor = palette[colornum % len(palette)]
fill((x,y), thiscolor)
colornum += 1
im.save('out_' + fname)
사진 예 :
답변
MATLAB
function [output_image] = m3(input_file_name)
a=imread(input_file_name);
b=im2bw(a,0.85);
c=bwlabel(b);
h=vision.BlobAnalysis;
h.MaximumCount=10000;
ar=power(double(step(h,b)),0.15);
ar=[ar(1:max(max(c))),0];
f=cat(3,mod((ar(c+(c==0))-min(ar(1:end-1)))/ ...
(max(ar(1:end-1))-min(ar(1:end-1)))*0.9+0.8,1),c*0+1,c*0+1);
g=hsv2rgb(f);
output_image=g.*cat(3,c~=0,c~=0,c~=0);
우리는 HSV 색 공간을 사용하고 흰색 영역 사이의 상대적인 크기에 따라 각 영역 색조를 선택합니다. 가장 큰 지역은 파란색 ( Hue = 0.7
)이고 가장 작은 지역은 보라색 ( Hue = 0.8
)입니다. 이 두 크기 사이의 영역에는 색조가 표시됩니다 0.7 -> 1=0 -> 0.8
. 범위의 색조는 기능과 관련하여 선형으로 선택됩니다 area^0.15
. 검은 색이 아닌 픽셀마다 채도와 값이 항상 1입니다.
이미지를 색칠하는 데 1 초도 걸리지 않습니다.
알고리즘이 제대로 작동하는 닫힌 영역이있는 3 개의 그림 :
그리고 나머지 이미지들 :
이 이미지에는 큰 흰색 연결 영역이 있으며 여러 색상으로 이상적으로 색칠해야합니다 (이 문제는 Nathaniel의 솔루션 에서 잘 해결되었습니다 .
답변
베개와 파이썬 3
이 답변에는 코드가 약간 길지만 여기에 요점이 있습니다 .
- 입력 이미지를 가져와 알파 채널이 있으면 흰색 배경에 합성하십시오. (전체 이미지는 검은 색이고 투명성만으로 구분되기 때문에 최소한 닭고기 이미지에는 필요하므로 알파를 삭제하는 것은 도움이되지 않았습니다.)
- 결과를 그레이 스케일로 변환합니다. 우리는 압축이나 앤티 앨리어싱 아티팩트, 또는 회색 라인이 그다지 엉망이 아닌 것을 원치 않습니다.
- 결과의 2 단계 (흑백) 사본을 만듭니다. 회색 음영은 이미지에서 흰색과 가장 어두운 음영 사이의 구성 가능한 컷오프 임계 값에 따라 검은 색 또는 흰색으로 변환됩니다.
- 이미지의 모든 흰색 영역을 홍수로 채 웁니다. 플러드 필 작업의 시작점 위치를 고려한 선택 가능한 팔레트를 사용하여 색상을 임의로 선택합니다.
- 가장 가까운 이웃 색상으로 검은 선을 채우십시오. 이것은 모든 유색 영역이 칙칙한 검은 색으로 경계하지 않도록하여 앤티 앨리어싱을 다시 도입하는 데 도움이됩니다.
- 2 단계에서 회색조 이미지를 가져 와서 알파 마스크를 만듭니다. 가장 어두운 색상은 완전히 불투명하고 가장 밝은 색상은 완전히 투명합니다.
- 이 알파 마스크를 사용하여 단계 5의 컬러 이미지에 그레이 스케일 이미지를 합성합니다.
불행히도, 마지막 몇 단계는 여전히 어두운 색 영역에서 볼 수있는 더 밝은 “후광”을 제거하지는 않았지만 적어도 눈에 띄는 차이를 만들었습니다. 이미지 처리는 결코 내 연구 분야가 아니 었으므로 내가 여기서 시도한 것을 수행하는 데 더 성공적이고 효율적인 알고리즘이 있다는 것을 알고 있습니다.
지금까지 4 단계에서 선택할 수있는 팔레트는 두 가지뿐입니다. 순전히 임의의 팔레트와 매우 거친 “자연적인”팔레트는 상단 모서리에 하늘색, 하단 모서리에 잔디 색, 갈색 (바위 또는 나무)을 지정하려고합니다. )의 색상은 각면의 중간에 표시되고 가운데의 색상은 다양합니다. 성공은 … 제한적입니다.
용법:
usage: paint_by_prog.py [-h] [-p PALETTE] [-t THRESHOLD] [-f | -F] [-d]
FILE [FILE ...]
Paint one or more line-art images.
positional arguments:
FILE one or more image filenames
optional arguments:
-h, --help show this help message and exit
-p PALETTE, --palette PALETTE
a palette from which to choose colours; one of
"random" (the default) or "natural"
-t THRESHOLD, --threshold THRESHOLD
the lightness threshold between outlines and paintable
areas (a proportion from 0 to 1)
-f, --proper-fill fill under black lines with proper nearest-neighbour
searching (slow)
-F, ---no-proper-fill
fill under black lines with approximate nearest-
neighbour searching (fast)
-d, --debug output debugging information
시료:
paint_by_prog.py -t 0.7 Gryphon-Lines.png
paint_by_prog.py Dragon-Tattoo-Outline.jpg
paint_by_prog.py -t 0.85 -p natural The-Pumpkin-Farm-of-Good-old-Days.jpg
paint_by_prog.py -t 0.7 Dragon-OutLine.jpg
paint_by_prog.py stejar-arbore-schiţă-natura.png
치킨은 잘 보이지 않으며 링크 이미지에 대한 가장 최근의 결과는 최고가 아닙니다. 이전 버전의 코드에서 나온 코드는 대체로 옅은 노란색이며 흥미로운 사막 분위기가 있습니다 …
공연:
각 이미지는 기본 설정으로 처리하는 데 몇 초가 걸립니다. 이는 대략적인 가장 가까운 이웃 알고리즘이 5 단계에 사용됨을 의미합니다. 가장 가까운 이웃은 상당히 느려서 0.5 분 정도 걸립니다 (실제로 시간을 정하지는 않았습니다).
답변
자바
선택한 팔레트에서 원하는 색상을 선택하십시오.
경고 : 흰색 영역이 비정상적으로 작지 않으면 영역 검색이 현재 매우 느립니다.
import java.awt.Color;
import java.awt.image.*;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.Scanner;
import java.util.function.Supplier;
import javax.imageio.ImageIO;
public class Colorer{
public static boolean isProbablyWhite(int x,int y){
Color c=new Color(image.getRGB(x, y));
if(c.getRed()<240)return false;
if(c.getBlue()<240)return false;
if(c.getGreen()<240)return false;
return true;
}
static class Point{
int x,y;
public boolean equals(Object o){
if(o instanceof Point){
Point p=(Point)o;
return x==p.x&&y==p.y;
}
return false;
}
public Point(int x,int y){
this.x=x;
this.y=y;
}
}
static BufferedImage image;
static int W,H;
public static void check(Point p,List<Point>l1,List<Point>l2,List<Point>l3){
if(!isProbablyWhite(p.x,p.y))return;
if(l1.contains(p))return;
if(l2.contains(p))return;
if(l3.contains(p))return;
l1.add(p);
}
public static void process(int x,int y,Color c){
List<Point>plist=new LinkedList<>();
int rgb=c.getRGB();
plist.add(new Point(x,y));
List<Point>l3=new LinkedList<>();
int k=0;
for(int i=0;i<W*H;i++){
System.out.println(k=l3.size());
List<Point>l2=new LinkedList<>();
for(Point p:plist){
int x1=p.x;
int y1=p.y;
if(x1>0){
check(new Point(x1-1,y1),l2,plist,l3);
}
if(y1>0){
check(new Point(x1,y1-1),l2,plist,l3);
}
if(x1<W-1){
check(new Point(x1+1,y1),l2,plist,l3);
}
if(y1<H-1){
check(new Point(x1,y1+1),l2,plist,l3);
}
}
while(!plist.isEmpty()){
l3.add(plist.remove(0));
}
if(l3.size()==k)break;
plist=l2;
}
plist=l3;
for(Point p:plist){
image.setRGB(p.x,p.y,rgb);
}
}
public static void main(String[]args) throws Exception{
Random rand=new Random();
List<Supplier<Color>>colgen=new ArrayList<>();
colgen.add(()->{return new Color(rand.nextInt(20),50+rand.nextInt(200),70+rand.nextInt(180));});
colgen.add(()->{return new Color(rand.nextInt(20),rand.nextInt(40),70+rand.nextInt(180));});
colgen.add(()->{return new Color(150+rand.nextInt(90),10+rand.nextInt(120),rand.nextInt(5));});
colgen.add(()->{int r=rand.nextInt(200);return new Color(r,r,r);});
colgen.add(()->{return Arrays.asList(new Color(255,0,0),new Color(0,255,0),new Color(0,0,255)).get(rand.nextInt(3));});
colgen.add(()->{return Arrays.asList(new Color(156,189,15),new Color(140,173,15),new Color(48,98,48),new Color(15,56,15)).get(rand.nextInt(4));});
Scanner in=new Scanner(System.in);
image=ImageIO.read(new File(in.nextLine()));
final Supplier<Color>sup=colgen.get(in.nextInt());
W=image.getWidth();
H=image.getHeight();
for(int x=0;x<W;x++){
for(int y=0;y<H;y++){
if(isProbablyWhite(x,y))process(x,y,sup.get());
}
}
ImageIO.write(image,"png",new File("out.png"));
}
}
파일 이름과 팔레트 ID라는 두 가지 입력이 필요합니다. 앤티 앨리어싱 보정을 포함하지만 투명 픽셀에 대한 로직은 포함하지 않습니다.
다음과 같은 팔레트가 현재 인식됩니다.
0: Blue and greeen
1: Blue
2: Red
3: Greyscale
4: Three-color Red, Green, and Blue
5: Classic Game Boy pallette (four shades of green)
결과 :
드래곤, 게임 보이 팔레트 :
다른 용, 파랑 + 녹색 팔레트 :
GOL 정물 모나리자 ( 이 프로그램 으로 렌더링 됨 ), 3 색 팔레트 :