Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Rafacharkman

Mudando Paleta de cores de uma imagem com GD

Recommended Posts

Gostaria de saber se é possível fazer o seguinte com a biblioteca GD:

 

Tenho uma imagem e quero mudar as cores dela baseadas em uma biblioteca de paletas (arquivo .pal) que tenho. É possível trocar as paletas da imagem por essa nova biblioteca de paletas que tenho utilizando a biblioteca GD? Se possível, poderia me ajudar com algumas dicas do que eu deveria fazer para realizar essa troca de paletas?

 

Obrigado desde Já!

Compartilhar este post


Link para o post
Compartilhar em outros sites

O mais próximo que encontrei se refere a criar um recurso de uma imagem e aplicar imagecolortopalette(), porém nada referente a utilizar um arquivo .PAL.

 

De repente, se você conseguir ler esse arquivo com algum editor de texto pode conseguir ajustar os argumentos dessa função.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não costumo mexer muito com a GD mas pelo que pude compreender você vai ler os 1024 bytes do arquivo PAL e montar um array de cores alocadas via imagecolorallocate(). Essa tarefa está mais legível no post #3 do link informado.

 

Depois fará um loop pelos pixels da imagem e aplicará a cor correspondente àquela coordenada X;Y na paleta lida. Essa tarefa está mais legível na segunda metade do código do post #9.

 

Tente fazer, mas se não conseguir anexe o arquivo PAL em questão para que tenhamos com o que trabalhar.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Antes de tudo, obrigado por me ajudar Bruno Augusto.

 

Bom... eu consegui entender até a leitura do arquivo .pal, mas depois para a criação da imagem e a troca das cores me enrolei aqui...

O arquivo é esse daqui:

http://www.mediafire.com/?r3ln2c6j393sya9

 

No caso dessa .pal com a imagem, quando aplicar a .pal é para mudar a cor do cabelo do desenho.

 

Fiquei com uma outra duvida aqui também, para abrir o arquivo .pal, seria desta maneira aqui:

$pal = fopen("./image/palete.pal", "rb");
// Loading palette
$pal_data = fread( $pal, 1024 );

 

não entendi também por que ele ta armazenando todas as informações na vareavel '$this'(eu acho), é para ficar mais fácil de acessar?

Compartilhar este post


Link para o post
Compartilhar em outros sites

O resultado esperado seria isso: jEKT8el.png

Se for, então deu certo. :grin:

Mas prefiro que você aprenda a fazer.

Primeiro sobre sua pergunta a respeito do $this, no tópico em que ele aparece existe "Orientação a Objetos" (entre aspas porque o código só foi disposto numa classe <_<).

Enfim...

Você já leu o arquivo, agora você vai criar um recurso de imagem a partir do original. Para iso você tem várias funções, cada qual para um tipo de arquivo.

Infelizmente parece não existir suporte nativo ao formato BMP. Você pode encontrar alguma implementação de terceiros ou converter os arquivos para PNG. Eu até encontrei uma implementação, mas estava muito confusa de se entender então me utilizei da segunda opção.

Então, você criará um array com as 256 cores alocadas dessa paleta.

Depois você itera pelos pixels da imagem e seta a nova cor localizando a cor do pixel atual (na iteração) no array de cores da paleta anteriormente criado.

Por fim, faz o output com a função mais adequada. Como utilizei imagecreatefrompng() para criar o recurso, usei imagepng() para finalizar.

Compartilhar este post


Link para o post
Compartilhar em outros sites

é isso mesmo que tem que ocorrer ^^

Bom, por mim tudo bem, eu também fico até mais feliz por que pelo menos agora eu sei que funciona. kkkk :)

 

sobre a pergunta do $this, é que eu nunca usei isso, por isso a duvida...

Então agente vai utilizar 'Orientação a Objetos' ou não vai ser preciso?

 

voltando...

 

Eu vou ir por partes aqui passando para você para mim entender melhor cada coisa tudo bem? :pinch:

Vamos desde o inicio entom...

 

Eu estou configurando aqui a leitura da .pal e a array para armazenar as cores, só que não tenho certeza se está certo, por que eu queria testar para ver se estavam armazenando os valores das cores, mas só que só me mostram o numero da cor. é normal isso mesmo ou era para mostrar os valores da paletta em hexa ou algo do tipo?

Ele também está dando erro na linha 15 por causa de não haver nenhum img relacionada a ela.

 

Abrindo e alocando as Palettes:

$pal="./image/palette.pal";
$palfopen = fopen($pal, "rb");
// Abrindo Paletta
$pal_data = fread( $palfopen, filesize($pal));

// Alocando os valores da Paletta
$palette  = array();
for ( $i=0, $j=0; $i<256; $i++, $j+=4 )
{
	$palette[ $i ] = imagecolorallocate(
		ord( $pal_data[$j+0] ),
		ord( $pal_data[$j+1] ),
		ord( $pal_data[$j+2] )
	 );
}

 

Não entendi direito por que desses erros...

 

 

Tem uma coisa também que eu gostaria de pedir para você ensinar(se não for pedir muito), mas só mais para frente, que seria deixar esse fundo da imagem transparente, mas por enquanto quero entender como funciona a troca de Palettes primeiro. =]

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não, não vamos usar Orientação a Objetos.

 

Nesse código tem um pequeno erro também no fórum original da dúvida. imagecolorallocate(), não sei por qual motivo, precisa de um recurso de imagem para alocar novas cores.

 

Quem criou o código se esqueceu disso e colocou um argumento a menos na função, ou seja, antes desse laço você vai precisar criar o recurso no qual aplicar a nova paleta.

 

Uma coisa que para esse caso não faz muita diferença é usar filesize() na leitura do arquivo, uma vez que uma paleta de cores tem tamanho fixo de 1024 bytes (segundo o autor original).

Compartilhar este post


Link para o post
Compartilhar em outros sites

então o melhor seria fazer assim:

$pal="./image/palette.pal";
$palfopen = fopen($pal, "rb");
// Abrindo Paletta
$pal_data = fread( $palfopen, 1024);

// Armazenando os valores da Paletta
$palette  = array();
$image_pal = array();
for ( $i=0, $j=0; $i<256; $i++, $j+=4 )
{
	$image_pal[$i]=imagecreatetruecolor(1, 1);
	
	$palette[$i] = imagecolorallocate(
		$image_pal[$i],
		ord( $pal_data[$j+0] ),
		ord( $pal_data[$j+1] ),
		ord( $pal_data[$j+2] )
	 );
}

 

Como que você fez para abrir a imagem? Não consegui entender direito o que eu tenho que fazer ali no 'itera pelos pixels da imagem'.

Abre ela com o 'fread' e depois só extrai que nem na paletta?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não, não. Você não vai criar 1024 recursos da mesma imagem senão seu PC é bem capaz de explodir :yay:

 

Você cria um único recurso, fora do loop e usa aquela variável em imagecolorallocate().

 

Quanto a iterar pelos pixels da imagem você vai fazer DEPOIS de montar a paleta.

 

Se você abrir uma imagem com fread() você vai, como a própria função diz, apenas ler a imagem. Apesar de ser teoricamente possível fazer o que a GD faz utilizando os ponteiros de uma stream, deve ser uma tarefa monstruosamente gigantesca deslocar byte por byte, pixel a pixel.

 

Veja, se você abrir essa paleta como texto plano não vai entender nada do que há nela porque ela não foi feita para ser lida por um bloco de notas, por exemplo. Como muita coisa da programação, existe um algorítimo ou uma lógica a ser seguida.

 

Para uma paleta de cores, você tem que são 1024 bytes e cada byte resulta numa string que depois de ter seu caractere ASCII convertido em inteiro por ord() resulta numa cor.

 

Fazendo tudo certinho, se você der um [inline]print_r()[/inline] na variável [inline]$palette[/inline] você verá 1023 números inteiros, cada uma cor, cores essas que serão utilizadas na segunda metade do código, por imagesetpixel().

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então, acho que agora está certo.
Eu estava achando que como cada paletta seria guardada em uma variável, ela precisava ser armazenada cada uma em uma imagem ou algo do tipo, depois que eu vi que ele só usa a imagem criada.

//----Paletta----
$pal="./image/palette.pal";
$palfopen = fopen($pal, "rb");
// Abrindo Paletta
$pal_data = fread( $palfopen, 1024);

// Armazenando os valores da Paletta
$palette  = array();
$image_pal = imagecreatetruecolor(1, 1);

for ( $i=0, $j=0; $i<256; $i++, $j+=4 )
{	
	$palette[$i] = imagecolorallocate(
		$image_pal,
		ord( $pal_data[$j+0] ),
		ord( $pal_data[$j+1] ),
		ord( $pal_data[$j+2] )
	 );
}



vai precisar também separar as palettas de cores da imagem né? por que na hora de trocar as palettas, eu vou precisar saber qual paleta que é para eu trocar pela outra, ou nem precisa fazer isso?

xxxxxxxxxxxxxx Ponto de Mesclagem xxxxxxxxxxxxxx

OBS:
Desculpe o Double Post, pensei que ele juntava tudo pra evitar Spam por que sumiu aqui o botão de edit do post que eu fiz '-'


Compartilhar este post


Link para o post
Compartilhar em outros sites

Certinho, agora depois desse código, você itera pelos pixels da imagem.

 

Mas há um porém. Você criou um recurso de uma imagem de 1x1. Para o propósito de imagecolorallocate(), tudo bem, mas isso te força a, depois desse primeiro loop, destruir o primeiro recurso para liberarar a memória e criar um novo da imagem original.

 

Ou você pode fazer como eu fiz e ao invés de criar esse recursos 1x1, criar um a partir da própria imagem que será manipulada. Lembre-se que não há suporte nativo ao formato BMP, e que você precisaria converter manualmente com qualquer editor de imagem para outro formato suportado.

 

Continuando...

 

Até esse tópico eu sempre achei que a única forma de fazer essa iteração era com dois for-loops aninhados, sendo o primeiro até a largura e o segundo até a altura da imagem (ou vice-versa).

 

Mas no tópico referência o autor usou uma abordagem diferente fantástica que permite separar matematicamente o valor do eixo X do Y e, com isso, melhorar a performance do script.

 

Seja qual for a forma que você vá fazer, a(s) variável(eis) de(as) iteração(ões) você usará como argumentos de imagesetpixel().

Compartilhar este post


Link para o post
Compartilhar em outros sites

Para iterar pelos Pixels da imagem eu devo fazer algo assim então:

 

//----Imagem----
$imagex=imagesx($image);  //$w
$imagey=imagesy($image);  //$h

for ($h = 0; $h < $imagey; $h++) {
    for ($w = 0; $w < $imagex; $w++) {

        // Obtem a cor original do Pixel
        $cor1 = imagecolorat($image, $w, $h);
        $r1 = ($cor1 >> 16) & 0xFF;
        $g1 = ($cor1 >>  8) & 0xFF;
        $b1 = ($cor1 >>  0) & 0xFF;

    }
}

 

 

Só não entendi direito como eu devo fazer para trocar os Pixels da imagem =/

Compartilhar este post


Link para o post
Compartilhar em outros sites

Então você não meu posts inteiros. :assobiando:

 

A resposta pra sua pergunta é literalmente a última palavra do meu post anterior. . :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

mas como eu vou usar o imagesetpixel() para trocar a cor se o imagecolorat() só pega a cor do pixel celecionado, e não o valor da palette que está sendo usada naquela região? por que pelo que eu entendi o imagecolorat() só pega a cor do pixel selecionado, e não o numero da paleta que está sendo usada naquele pixel, então eu não sei o que que eu vou trocar para dar a cor exata da paletta.

 

Como que você vez essa troca? eu não consegui entender ainda @.@

Tem como você colocar um exemplo para mim?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu consegui aqui, foi isso mesmo que você fez?

 

//----Paletta----
$pal="./image/palette.pal";
$palfopen = fopen($pal, "rb");
// Abrindo Paletta
$pal_data = fread( $palfopen, 1024);

// Armazenando os valores da Paletta
$palette  = array();
//Carregando a Imagem (PNG)
$image=imagecreatefrompng('./image/head2.png');

for ( $i=0, $j=0; $i<256; $i++, $j++ ){	
	$palette[$i] = imagecolorallocate(
		$image,
		ord( $pal_data[$j++] ),
		ord( $pal_data[$j++] ),
		ord( $pal_data[$j++] )
	 ); 
}

//----Paletta da Imagem----
$palimg="./image/palette_image.pal";
$palimgfopen = fopen($palimg, "rb");
// Abrindo Paletta
$palimg_data = fread( $palimgfopen, 1024);

// Armazenando os valores da Paletta
$paletteimg  = array();

for ( $i=0, $j=0; $i<256; $i++, $j++ ){	
	$paletteimg[$i] = imagecolorallocate(
		$image,
		ord( $palimg_data[$j++] ),
		ord( $palimg_data[$j++] ),
		ord( $palimg_data[$j++] )
	 ); 
}


//----Imagem----
$imagex=imagesx($image);  //$w
$imagey=imagesy($image);  //$h

for ($h = 0; $h < $imagey; $h++) {
    for ($w = 0; $w < $imagex; $w++) {

        // Obtem a cor original do Pixel
        $image_color = imagecolorat($image, $w, $h);
        $r1 = ($image_color >> 16) & 0xFF;
        $g1 = ($image_color >>  8) & 0xFF;
        $b1 = ($image_color >>  0) & 0xFF;
		
		
		
		for ( $i=0, $j=0; $i<256; $i++, $j++ ){
			if($paletteimg[$i]==$image_color){$index_img=$i;}
		}
		
		imagesetpixel($image, $w, $h, $palette[$index_img]);
    }
}



header('Content-Type: image/png');

imagepng($image);

 

 

Agora eu só queria saber como que eu faço para deixar aquele fundo transparente, teria como você me explicar?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sua implementação ficou ligeiramente diferente da minha, porém não renderizou a mesma coisa.

 

O seu código renderizou pra mim um quadradinho marrom, mais nada.

 

A menos que haja algo mais que você não tenha explicado, o código da seção Paletta da Imagem não só é desnecessário como está atrapalhando o seu uso de imagesetpixel(). As informações geradas no primeiro loop já são os dados das cores que você vai usar.

 

Tanto é verdade que se você remover aquele terceiro for-loop (que ainda estou tentando entender o motivo dele existir ali) e trocar $index_img por $image_color já funciona, ficando prontinho para transformar o tom de azul piscina do fundo em pixels transparentes.

 

Quer que eu poste como eu fiz ou quer tentar de novo?

Compartilhar este post


Link para o post
Compartilhar em outros sites

não renderizou por que falta a paletta da Imagem que será utilizada para ser renderizada, por isso ficou todo preto o desenho.

a paletta da imagem é essa daqui: http://www.mediafire.com/?ysltslse97f05i5

 

como eu falei para você acima, eu não consegui entender como você fez pra trocar as palettas, então o que eu fiz foi o seguinde:

 

Criei dois bancos de palettas, um da paletta a ser adicionada e uma da paletta da imagem, por isso aqueles 2 códigos.

 

e aquele 3° loop abaixo, é para eu saber qual é o numero da paletta que está sendo usado naquele pixel, para que então eu possa trocar pelo mesmo numero da paletta da nova cor que eu quero usar. Por que eu não consegui entender como você fez para trocar a cor do pixel, sem saber o numero da Paletta(ou sabia... xD) da cor da Imagem que estava sendo usada naquele Pixel.

 

Agora eu não entendi esse imagecolorat(), Por que trocar o $index_img pelo $image_color sendo que o $image_color é a cor do Pixel da imagem, e não o numero da Paletta da Imagem da cor que está sendo utilizada naquele Pixel?

 

Pode postar, dai nos comparamos os mesmos =]

Compartilhar este post


Link para o post
Compartilhar em outros sites

Quando você troca a paleta de uma imagem sua intenção é de recolorir a mesma. Cada paleta é um conjunto de cores a ser aplicada de cada vez.

 

O primeiro arquivo que você enviou continha a imagem do garoto ruivo num fundo cinza esverdeado. Aplicar aquela paleta fazia ele ficar loiro sob um fundo azul piscina.

 

A segunda paleta que você acabou de anexar, tanto quanto eu pude perceber do pixel art em questão, não muda nada na imagem, o Alex (nome padrão dos meus tempos de RPG Maker :grin:) continua sendo ruivo.

 

Sendo assim, essa segunda paleta é desnecessária, bastaria aplicar a remoção do background.

 

E aquele loop está errado porque pra você saber qual será a nova cor daquele pixel basta você pegar a cor dele, via imagecolorat() como fez, e usar aquele índice como offset na paleta anteriormente lida.

 

Lembra que eu disse que não fazia sentido imagecolorat() precisar de um recurso de imagem? Pois então, olha a necessidade aí. Sem ele ou então se usasse uma imagem de tamanho menor ou maior haveriam mais ou menos pixels e esse loop começaria a disparar erros de índices indefinidos.

 

Agora, se com essa segunda leitura você quiser criar uma paleta a partir das cores originais para depois modificar e ter como automatizar um procedimento de reversão, é outra história, já que a lógica muda um pouco.

 

Dá uma olhada como eu fiz e veja onde e porquê você está errando:

 

<?php

// Read palette data

$paletteHandle = fopen( 'palette.pal', 'rb' );

$paletteData = fread( $paletteHandle, 1024 );

fclose( $paletteHandle );

// Create image resource

$resource 	= imagecreatefrompng( 'head2.png' );

// Allocate palette colors

$palette  = array();

for( $i = 0, $j = 0; $i < 256; $i += 1, $j += 4 ) {

    $palette[ $i ] = imagecolorallocate(

        $resource,

        ord( $paletteData[ $j + 0 ] ),
        ord( $paletteData[ $j + 1 ] ),
        ord( $paletteData[ $j + 2 ] )
    );
}

// Define a transparent pixel (the first pixel, at 0;0)

$transparent = imagecolortransparent( $resource, $palette[ 0 ] );

$width  = imagesx( $resource );
$height = imagesy( $resource );
$area = ( $width * $height );

for( $i = 0; $i < $area; $i += 1 ) {

    $x = $i % $width;
    $y = $i / $width | 0;

    $colorAt = imagecolorat( $resource, $x, $y );

    imagesetpixel(

        $resource, $x, $y,

        ( $colorAt == 0 ? $transparent : $palette[ $colorAt ] )
    );
}

header( 'Content-type: image/png' );

imagepng( $resource );

 

Nesse código já está incluso a remoção do background. A lógica é simples: Pegar a cor do primeiro pixel que 99% das vezes já é a cor do background e alocar ela como transparente via imagecolortransparent().

 

Na hora de setar a nova cor do pixel você verifica se a cor obtida para aquele ponto é zero. Se for, aplica a transparência, se não for, aplica a cor da paleta.

 

Não estou bem bem certo o porquê de a condição ter de ser zero, mas acredito não ser importante.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Aquela Paletta que te mandei tem referência a alocação de cores da Paletta da Imagem, e não da alocação da Paletta que quero adicionar a imagem.

Da uma olhada ai =]

http://www.mediafire.com/?yfbp34yn6f6wrd3

 

 

E aqui, eu tentei utilizar o seu código e ele ta renderizando para mim essa imagem:imagemdzg.png

 

Provavelmente o Valor 0(zero) é por que tem alguns números de palettas que estão como nulo(em branco), mas não entendi o por que também desse código ai...

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.