Python 2 + PIL, sem erro, 313 307 bytes
from Image import*
I=open(sys.argv[1])
w,h=I.size;D=I.getdata()
B={i%w+i/w*1j for i in range(w*h)if D[i]!=D[0]}
n=d=1;o=v=q=p=max(B,key=abs)
while p-w:
p+=d*1j;e=2*({p}<B)+({p+d}<B)
if e!=2:e%=2;d*=1j-e*2j;p-=d/1j**e
if abs(p-q)>5:
t=(q-v)*(p-q).conjugate();q=p;w=o
if.98*abs(t)>t.real:n+=1;v=p
print n
Pega um nome de arquivo de imagem na linha de comando e imprime o resultado em STDOUT.
Dá o resultado correto para todos os testes en = 28 para o círculo.
Explicação
O algoritmo funciona caminhando ao longo do perímetro do polígono e contando o número de vértices encontrados (detectados como alterações na direção). Começamos no pixel mais distante da origem, o
que é garantidamente um vértice e, portanto, adjacente a uma aresta (ou seja, um limite entre um pixel em primeiro plano e um pixel em segundo plano). Nós controlamos nossa posição, p
o vértice mais recente v
e o "ponto de verificação" mais recente q
, todos inicialmente iguais a o
. Também acompanhamos a direção da borda d
, em relação ao pixel atual; d
inicialmente aponta para leste, o que é uma direção segura, pois sabemos que há uma borda a leste deo
, ou não seria o mais distante da origem. Nós nos movemos ao longo da borda, em uma direção perpendicular a d
, de modo que d
aponte para a esquerda, ou seja, no sentido horário. Sempre que "caímos da aresta", ou seja, em qualquer situação p
fora do polígono, ou onde o pixel à nossa esquerda (na direção de d
) esteja dentro do polígono, ajustamos p
e de d
acordo antes de continuar.
Toda vez que a distância entre p
e o último ponto de verificação q
fica maior que 5, tentamos determinar se passamos por um vértice entre q
e p
: Comparamos o ângulo entre vq
(ou seja, o vetor de v
para q
), que é a direção geral do lado do polígono em que estávamos caminhando quando alcançamos o último ponto de verificação e qp
o deslocamento entre o último ponto de verificação e a posição atual. Se o ângulo for maior que cerca de 10 °, concluímos que estamos andando por um lado diferente do polígono, aumentamos a contagem de vértices e ajustamos v
o vértice atual para p
. Em cada ponto de verificação, independentemente de detectarmos um vértice ou não, atualizamos q
o último ponto de verificação parap
. Continuamos dessa maneira até chegarmos o
ao ponto de partida e retornar o número de vértices encontrados (observe que a contagem de vértices é inicialmente 1, pois o ponto de partida o
é um vértice).
As imagens abaixo mostram os vértices detectados. Observe que p
, assumindo , a posição atual em cada ponto de verificação, como a posição do novo vértice, não é ideal, pois o vértice real provavelmente está em algum lugar entre o último ponto de verificação q
e p
ao longo do perímetro. Como você pode ver, todos os vértices que não sejam o primeiro (geralmente, o vértice inferior direito) estão um pouco fora. Corrigir isso custaria mais bytes, mas parece estar funcionando bem o suficiente. Dito isto, é um pouco difícil não se ajustar demais com apenas quatro casos de teste.