
Dois cubos
Compramos dois cubos 3×3 para casa.
- Cubo oficial: cada casa tem uma borda preta, e a cor fica concentrada no meio da casa. O centro branco tem o logotipo da marca, e o brilho dele reflete a luz.
- Cubo lançado na Coreia: é leve e gira com suavidade. Por outro lado, é de plástico fluorescente sem adesivos, então as cores são intensas, e ele é fino o bastante para as cores de trás aparecerem por transparência.
Primeiro com um VLM
Conectamos o Qwen3-VL-8B, que usávamos no go. Mostramos as duas fotos de uma vez e pedimos que ele respondesse em letras as cores das 9 casas de cada face.
O resultado foi pior que o da leitura comum. Ele quase não distinguia as cores, e muitas vezes respondia preenchendo uma face com uma única cor, como RRRRRRRRR. Não servia para uma tarefa que exige apontar com precisão a posição das 9 casas. Hoje a leitura por VLM ficou só como um botão auxiliar, que descarta faces vazias ou preenchidas com uma cor só e as substitui pelo resultado da leitura comum.
Segmentação de cores em C++
Então passamos a ler diretamente em C++.
- Encontra a área do cubo na foto e reduz fotos grandes para 1280 px.
- Ajusta o balanço de branco, monta vários candidatos de grade para as três faces e tira a média da cor dentro de cada casa. As bordas ficam de fora para evitar os contornos e os reflexos.
- Classifica em uma das seis cores com condições de HSV e RGB.
- Define cada face pela cor do centro e confirma uma face por vez na ordem amarelo → vermelho → verde → laranja → azul → branco.
- Qual foto tem o branco em cima é descoberto pelo logotipo da marca e pela proporção entre amarelo e branco. Trocando a ordem das fotos, o resultado foi o mesmo.
Acertar um quebra o outro
Ajustamos primeiro o cubo lançado na Coreia. Corrigimos um por um problemas como o verde fluorescente sendo lido como amarelo e as cores transparentes sendo lidas como cores de outra face, até as primeiras fotos serem lidas totalmente certas.
Depois colocamos o cubo oficial, que parecia mais fácil, mas no começo só três das seis faces foram encontradas.

Esta é a tela da primeira leitura do cubo oficial. As faces de cima, da esquerda e da direita não foram encontradas.
A borda preta ajuda a separar as casas, mas basta sair um pouco de dentro da casa para o preto e a cor da mesa se misturarem, confundindo o branco com o verde. No cubo oficial, o laranja e o vermelho também são parecidos. Ao colocar condições para o cubo oficial, as fotos do cubo lançado na Coreia quebravam de novo, e ficamos indo e voltando, ajustando os dois cubos.
O CubeModel.cpp, que contém a classificação de cores, tem 230 limiares decimais, e o CubePhotoReconstructor.cpp, o arquivo de reconhecimento de fotos, tem 379. São valores escolhidos à mão para se ajustarem a algumas fotos cuja resposta era conhecida.
O código real desta etapa
Esta é a condição que separa o amarelo do branco. O amarelo precisa ter o componente verde parecido com o vermelho, e é isso que também o separa do laranja.
src/CubeModel.cpp
// --- 강한/옅은 특징 ---
const bool strongWhite = (v > 0.62 && s < 0.22 && spread < 0.20)
|| (v > 0.72 && s < 0.28 && spread < 0.25);
const bool paleWhite = (v > 0.55 && s < 0.32 && spread < 0.28)
|| (v > 0.60 && s < 0.38 && deg >= 170.0 && deg < 255.0 && !blueChrome && spread < 0.32);
const bool strongYellow = (v > 0.40 && r > 0.42 && g > 0.38 && b < 0.50
&& (r + g) > b * 2.4 && deg >= 40.0 && deg < 95.0
&& g >= r * 0.72 // 노랑: G≈R, 주황(G≪R) 제외
&& !(g > r + 0.14 && s > 0.45));
const bool paleYellow = (v > 0.38 && r > 0.35 && g > 0.32 && b < 0.58
&& (r + g) > b * 1.9 && deg >= 38.0 && deg < 100.0
&& g >= r * 0.70 && !greenChrome);Esta é a função que encontra o logotipo no centro branco do cubo oficial. Ela olha a proporção entre pontos claros e escuros.
src/CubePhotoReconstructor.cpp
bool hasWhiteCenterLogo(const QImage &img, int cx, int cy, int radius)
{
if (img.isNull() || radius < 2)
return false;
int bright = 0, dark = 0, n = 0;
const int r = std::max(4, radius);
for (int dy = -r; dy <= r; ++dy) {
for (int dx = -r; dx <= r; ++dx) {
if (dx * dx + dy * dy > r * r)
continue;
const int x = std::clamp(cx + dx, 0, img.width() - 1);
const int y = std::clamp(cy + dy, 0, img.height() - 1);
const QColor c = img.pixelColor(x, y);
const int sum = c.red() + c.green() + c.blue();
const int spr = std::max({c.red(), c.green(), c.blue()}) - std::min({c.red(), c.green(), c.blue()});
++n;
if (sum > 400 && spr < 55)
++bright;
else if (sum < 160)
++dark;
}
}
if (n < 20)
return false;
return bright * 5 >= n * 2 && dark * 20 >= n && dark * 4 <= n;
}O próximo post é "3. O que 54/54 realmente significava, a taxa real de reconhecimento e a distribuição".
COMMENTS · CHAPTER 02
Segmentação de cores em vez de um VLM, e dois cubos com personalidades diferentes 0 comentários
Ainda não há comentários. Deixe uma pergunta ou algo que queira acrescentar.
Para publicar um comentário, comece com o Google e registre um apelido e um e-mail.
Começar com o Google