Eu tentei criar um arquivo de imagem, assim:
uint8_t raw_r[pixel_width][pixel_height];
uint8_t raw_g[pixel_width][pixel_height];
uint8_t raw_b[pixel_width][pixel_height];
uint8_t blue(uint32_t x, uint32_t y)
{
return (rand()%2)? (x+y)%rand() : ((x*y%1024)%rand())%2 ? (x-y)%rand() : rand();
}
uint8_t green(uint32_t x, uint32_t y)
{
return (rand()%2)? (x-y)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
uint8_t red(uint32_t x, uint32_t y)
{
return (rand()%2)? (y-x)%rand() : ((x*y%1024)%rand())%2 ? (x+y)%rand() : rand();
}
for (y=0; y<pixel_height; ++y)
{
for (x=0; x<pixel_width; ++x)
{
raw_b[x][y]=blue(x, y);
raw_g[x][y]=green(x, y);
raw_r[x][y]=red(x, y);
}
}
Eu esperava obter algo aleatório (ruído branco). No entanto, a saída é interessante:
Você sabe o motivo?
Editar
Agora, está claro que isso não tem nada a ver rand()
.
Tente também este código:
for (x=0; x<pixel_width; ++x)
for (y=0; y<pixel_height; ++y)
{
r[x][y] = (x+y);
g[x][y] = (y-x);
/* b[x][y] = rand()%2? x : y; */
}
Respostas:
Inicialmente, eu teria a mesma resposta que todo mundo tinha e atribuiria isso aos problemas
rand()
. No entanto, achei melhor fazê-lo e, em vez disso, analisei a distribuição que sua matemática está realmente produzindo.TL; DR: O padrão que você vê não tem nada a ver com o gerador de números aleatórios subjacente e, em vez disso, é simplesmente devido à maneira como seu programa está manipulando os números.
Vou manter a sua função azul, uma vez que são todos semelhantes.
Cada valor de pixel é seleccionado a partir de um de três funções:
(x + y) % rand()
,(x - y) % rand()
erand()
;Vejamos as imagens produzidas por cada uma delas sozinha.
rand()
Isto é o que você esperaria, apenas barulho. Chame isso de "Imagem C"
(x + y) % rand()
Aqui você adiciona as coordenadas de pixel e evita que o restante seja dividido por um número aleatório. Se a imagem for 1024x1024, a soma estará no intervalo [0-2046]. O número aleatório pelo qual você está mergulhando está no intervalo [0, RAND_MAX], onde RAND_MAX é de pelo menos 32k e em alguns sistemas é de 2 bilhões. Em outras palavras, há uma chance de 1 em 16 de que o restante não seja apenas
(x + y)
. Portanto, na maior parte das vezes, essa função produz apenas um gradiente de aumento de azul na direção + x + y.No entanto, você está usando apenas os 8 bits mais baixos, porque retorna a
uint8_t
, para ter faixas de gradientes com 256 pixels de largura.Chame isso de "Imagem A"
(x - y) % rand()
Aqui você faz algo semelhante, mas com subtração. Contanto que x seja maior que y, você terá algo semelhante à imagem anterior. Mas onde y é maior, o resultado é um número muito grande porque
x
ey
não está assinado (resultados negativos são agrupados no topo do intervalo do tipo não assinado) e, em seguida, o sinal% rand()
entra em ação e você realmente recebe ruído.Chame isso de "Imagem B"
Cada pixel em sua imagem final é obtido de uma dessas três imagens usando as funções
rand() % 2
e((x * y % 1024) % rand()) % 2
. A primeira delas pode ser lida como uma escolha com 50% de probabilidade (ignorando problemas comrand()
e seus bits de baixa ordem).Aqui está um close de onde
rand() % 2
é verdade (pixels brancos) para que a Imagem A seja selecionada.A segunda função
((x * y % 1024) % rand()) % 2
novamente apresenta o problema em querand()
geralmente é maior do que o que você está dividindo(x * y % 1024)
, que é no máximo 1023. Então(x*y%1024)%2
não produz 0 e 1 com a mesma frequência. Qualquer número ímpar multiplicado por qualquer número par é par. Qualquer número par multiplicado por qualquer número par também é par. Somente um número ímpar multiplicado por um número ímpar é ímpar, e assim por%2
diante, valores que são até três quartos das vezes produzirão 0 três quartos das vezes.Aqui está um close de onde
((x * y % 1024) % rand()) % 2
é verdade, para que a Imagem B possa ser selecionada. É selecionar exatamente onde as duas coordenadas são ímpares.E aqui está um close de onde a Imagem C pode ser selecionada:
Finalmente, combinando as condições, é aqui que a Imagem B é selecionada:
E onde a Imagem C está selecionada:
A combinação resultante pode ser lida como:
Com 50% de probabilidade, use o pixel da Imagem A. O restante do tempo escolhe entre a Imagem B e a Imagem C, B, onde ambas as coordenadas são ímpares, C, onde ambas são pares.
Finalmente, como você faz o mesmo com três cores diferentes, mas com orientações diferentes, os padrões são orientados de maneira diferente em cada cor e produzem as tiras de cruzamento ou o padrão de grade que você está vendo.
fonte
Muitos dos cálculos que você está fazendo no seu código não levam a valores verdadeiramente aleatórios. Essas linhas nítidas que você vê correspondem a lugares onde os valores relativos de suas coordenadas xey são trocados entre si e, quando isso acontece, você usa fórmulas fundamentalmente diferentes. Por exemplo, a computação
(x + y) % rand()
geralmente devolve o valorx + y
, poisrand()
(geralmente) retorna um número muito, muito maior do que ox + y
dado,RAND_MAX
geralmente um número bastante grande. Nesse sentido, você não deve esperar receber ruído branco, já que o algoritmo que você está usando para gerar coisas é tendencioso para gerar ruído branco. Se você deseja ruído branco, basta definir cada pixel comorand()
. Se você deseja um bom padrão como o que você tem acima, mas com um pouco de aleatoriedade aqui e ali, continue usando o código que você escreveu.Além disso, como @ pm100 observou nos comentários, a
rand
função não retorna números verdadeiramente aleatórios e usa uma função pseudo-aleatória para produzir seus valores. A implementação padrão derand
muitos sistemas usa um tipo de gerador de números pseudo-aleatórios chamado gerador congruencial linear que produz números que, em breves rajadas, podem parecer aleatórios, mas que na prática são decididamente não-aleatórios. Por exemplo, aqui está uma animação da Wikipedia mostrando como pontos aleatórios no espaço escolhido com um gerador congruencial linear acabam caindo em um número fixo de hiperplanos:Se você substituir as coordenadas x, ye z pelas coordenadas R, G e B, isso será notavelmente semelhante à saída produzida pelo seu programa. Suspeito que esse provavelmente não seja o problema principal aqui, pois o outro aspecto mencionado acima provavelmente será muito mais pronunciado.
Se você estiver procurando por números aleatórios de alta qualidade, precisará usar uma fonte aleatória de alta qualidade. Em C, você pode considerar ler bytes de
/dev/urandom/
(em um sistema semelhante ao Linux), o que fornece valores aleatórios de maneira bastante uniforme. O C ++ agora possui várias boas primitivas de geração de números aleatórios em suas bibliotecas padrão, se estiverem disponíveis para você.fonte