Preparando-se para o trabalho
Como sempre, primeiro abra uma janela de terminal e execute o comando.update50
para ter certeza de que seu aplicativo já está atualizado. Antes de começar, siga este procedimento cd ~ / workspace
wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip
para baixar um arquivo ZIP desta tarefa. Agora, se você executar ls , verá que possui um arquivo chamado pset4.zip em seu diretório ~/workspace . Extraia-o usando o comando: Se você executar o comando lsunzip pset4.zip
novamente , verá que outro diretório apareceu. Agora você pode excluir o arquivo zip conforme mostrado abaixo: Vamos abrir o diretório pset4 , executar ls e verificar se o diretório contém rm -f pset4.zip
cd pset4
bmp / jpg / questions.txt
whodunit ou “Quem fez isso?”
Se você já viu a área de trabalho padrão do Windows XP (https://en.wikipedia.org/wiki/Bliss_(image)) (colinas e céu azul), então você viu o BMP. Nas páginas da web, você provavelmente já viu GIFs. Você já olhou fotos digitais? Então, tivemos a alegria de ver JPEG. Se você já fez uma captura de tela em um Mac, provavelmente já viu um PNG. Leia na Internet sobre os formatos BMP, GIF, JPEG, PNG e responda a estas perguntas:-
Quantas cores cada formato suporta?
-
Qual formato suporta animação?
-
Qual é a diferença entre compactação com e sem perdas?
-
Qual desses formatos usa compactação com perdas?
-
O que acontece do ponto de vista técnico quando um arquivo é excluído em um sistema de arquivos FAT?
-
O que pode ser feito para garantir (com alta probabilidade) que os arquivos excluídos não possam ser recuperados?
xxd -c 24 -g 3 -s 54 smiley.bmp
Você deverá ver o que é mostrado abaixo; Destacamos novamente em vermelho todas as instâncias de 0000ff. Na imagem da coluna mais à esquerda você pode ver os endereços do arquivo, que equivalem ao deslocamento do primeiro byte do arquivo. Todos eles são fornecidos em sistema de numeração hexadecimal. Se convertermos hexadecimal 00000036 em decimal, obteremos 54. Então você está olhando para o 54º byte de smiley.bmp . Lembre-se de que em arquivos BMP de 24 bits, os primeiros 14 + 40 = 54 bytes são preenchidos com metadados. Então, se você quiser ver os metadados, execute o seguinte comando: xxd -c 24 -g 3 smiley.bmp
If smiley.bmp contém caracteres ASCII , nós os veremos na coluna mais à direita em xxd em vez de todos aqueles pontos. Portanto, smiley é um BMP de 24 bits (cada pixel é representado por 24 ÷ 8 = 3 bytes) com tamanho (resolução) de 8x8 pixels. Cada linha (ou "Scanline" como é chamada) ocupa (8 pixels) x (3 bytes por pixel) = 24 bytes. Esse número é um múltiplo de quatro e é importante porque o arquivo BMP é armazenado de maneira um pouco diferente se o número de bytes na linha não for um múltiplo de quatro. Então, em small.bmp, outro arquivo BMP de 24 bits em nossa pasta, você pode ver uma caixa verde de 3x3 pixels. Se você abri-lo em um visualizador de imagens, verá que ele se parece com a imagem mostrada abaixo, só que menor em tamanho. Cada linha em small.bmp ocupa, portanto, (3 pixels) × (3 bytes por pixel) = 9 bytes, o que não é um múltiplo de 4. Para obter um comprimento de linha que seja múltiplo de 4, ele é preenchido com zeros adicionais: entre 0 e 3 bytes preenchemos cada linha no formato BMP de 24 bits (você consegue adivinhar por que isso acontece?). Para small.bmp , são necessários 3 bytes de zeros, pois (3 pixels) x (3 bytes por pixel) + (3 bytes de preenchimento) = 12 bytes, que na verdade é um múltiplo de 4. Para "ver" esse preenchimento, faça o seguinte. xxd -c 12 -g 3 -s 54 small.bmp
Observe que usamos um valor diferente para -c do que para smiley.bmp , então xxd gera apenas 4 colunas desta vez (3 para o quadrado verde e 1 para preenchimento). Para maior clareza, destacamos todas as ocorrências de 00ff00 em verde. Por outro lado, vamos usar xxd para o arquivo large.bmp . Parece exatamente igual a small.bmp, apenas sua resolução é de 12x12 pixels, ou seja, quatro vezes maior. Execute o comando abaixo. Pode ser necessário expandir a janela para evitar a transferência. xxd -c 36 -g 3 -s 54 large.bmp
Você verá algo assim: Observe que não há digressões neste BMP! Afinal, (12 pixels) × (3 bytes por pixel) = 36 bytes, e isso é um múltiplo de 4. O editor hexadecimal xxd nos mostrou os bytes em nossos arquivos BMP. Como podemos obtê-los programaticamente? Em copy.c existe um programa cujo único propósito na vida é criar uma cópia do BMP, peça por peça. Sim, você pode usar cp para isso . No entanto, cp não poderá ajudar o Sr. Boddy. Esperemos que copy.c faça isso, então vamos lá: Se você executar ls./copy smiley.bmp copy.bmp
agora (com o sinalizador apropriado), verá que smiley.bmp e copy.bmp são de fato do mesmo tamanho. Vamos verificar novamente se isso é realmente verdade? Se este comando não exibir nada na tela, significa que os arquivos são realmente idênticos (importante: alguns programas, como o Photoshop, incluem zeros à direita no final de alguns VMPs. Nossa versão de cópia os descarta, então não faça isso. preocupe-se se no caso de copiar outros BMPs que você baixou ou criou para teste, a cópia será alguns bytes menor que o original). Você pode abrir ambos os arquivos no visualizador de imagens do Ristretto (clique duas vezes) para confirmar visualmente. Mas diff faz essa comparação byte por byte, então a visão dela é mais nítida que a sua! Como esta cópia foi criada? Acontece que copy.c está relacionado a bmp.h . Vamos ter certeza: abra bmp.hdiff smiley.bmp copy.bmp
. Lá você verá as definições reais dos cabeçalhos que já mencionamos, adaptadas das próprias implementações da Microsoft. Além disso, este arquivo define os tipos de dados BYTE, DWORD, LONG e WORD, que são os tipos de dados normalmente encontrados no mundo da programação Win32 (ou seja, Windows). Observe que estes são essencialmente aliases para primitivos com os quais você (espero) já está familiarizado. Acontece que BITMAPFILEHEADER e BITMAPINFOHEADER estavam usando esses tipos. Este arquivo também define uma estrutura chamada RGBTRIPLE. Ele “encapsula” três bytes: um azul, um verde e um vermelho (esta é a ordem em que procuraremos os trigêmeos RGB no disco). Como essas estruturas são úteis? Para recapitular, um arquivo é simplesmente uma sequência de bytes (ou, em última análise, bits) no disco. No entanto, esses bytes são normalmente ordenados de modo que os primeiros representem algo, depois os próximos representem outra coisa e assim por diante. Os "formatos" de arquivo existem porque temos padrões, ou regras, que definem o que bytes significam. Agora podemos simplesmente ler o arquivo do disco para a RAM como uma grande matriz de bytes. E lembramos que o byte na posição [i] representa uma coisa, enquanto o byte na posição [j] é outra coisa. Mas por que não dar nomes a alguns desses bytes para que possamos recuperá-los mais facilmente da memória? É exatamente nisso que as estruturas em bmp.h nos ajudam. Em vez de pensar em um arquivo como uma longa sequência de bytes, nós o vemos dividido em blocos mais compreensíveis – sequências de estruturas. Lembre-se de que smiley.bmp tem resolução de 8x8 pixels, portanto ocupa 14 + 40 + (8 × 8) × 3 = 246 bytes no disco (você pode verificar isso usando o comando ls). Esta é a aparência do disco, de acordo com a Microsoft: Podemos ver que a ordem é importante quando se trata de membros de estruturas. O byte 57 é rgbtBlue (não, digamos, rgbtRed) porque rgbtBlue é definido primeiro em RGBTRIPLE. A propósito, nosso uso do atributo empacotado garante que o clang não tente “alinhar palavras” nos membros (com o endereço do primeiro byte de cada membro sendo um múltiplo de 4), para que não acabemos com buracos no nossas estruturas que não existem no disco. Vamos continuar. Encontre os URLs que correspondem a BITMAPFILEHEADER e BITMAPINFOHEADER, conforme os comentários em bmp.h. Atenção, ótimo momento: você está começando a usar o MSDN (Microsoft Developer Network)! Em vez de rolar ainda mais copy.c , responda algumas perguntas para entender como o código funciona. Como sempre, o comando man é seu verdadeiro amigo, e agora também o MSDN. Se você não sabe as respostas, pesquise no Google e pense a respeito. Você também pode consultar o arquivo stdio.h em https://reference.cs50.net/.
-
Defina um ponto de interrupção em main (clicando à esquerda da régua com os números das linhas principais).
-
Em uma guia do terminal , vá para ~/workspace/pset4/bmp e compile copy.c no programa de cópia usando make.
-
Execute debug50 copy smiley.bmp copy.bmp , isso abrirá o painel do depurador à direita.
-
Percorra o programa passo a passo usando o painel à direita. Observe bf e bi . Em ~/workspace/pset4/questions.txt , responda às perguntas:
-
O que é stdint.h ?
-
Qual é o sentido de usar uint8_t , uint32_t , int32_t e uint16_t em um programa?
-
Quantos bytes BYTE , DWORD , LONG e WORD contêm respectivamente (assumindo arquitetura de 32 bits)?
- Quais devem ser (ASCII, decimal ou hexadecimal) os primeiros dois bytes de um arquivo BMP? (bytes iniciais, que são usados para identificar o formato do arquivo (com alta probabilidade) são frequentemente chamados de "números mágicos").
-
Qual é a diferença entre bfSize e biSize?
-
O que significa um biHeight negativo?
-
Qual campo no BITMAPINFOHEADER define a profundidade de cor em BMP (ou seja, bits por pixel)?
-
Por que a função fopen pode retornar NULL em copy.c 37?
-
Por que o terceiro argumento para fread em nosso código é igual a 1?
-
Qual valor em copy.c 70 define o preenchimento se bi.biWidth for 3?
-
O que o fseek faz?
-
O que é SEEK_CUR?
De volta ao Sr. Boddy. Exercício:
Escreva um programa chamado whodunit em um arquivo chamado whodunit.c que mostre o desenho do Sr. Boddy. Hummm, o quê? Assim como a cópia, o programa deve receber exatamente dois argumentos de linha de comando e, se você executar o programa conforme mostrado abaixo, o resultado será armazenado em verdict.bmp, no qual o desenho do Sr. Boddy não terá ruído../whodunit clue.bmp verdict.b
Sugerimos que você comece a resolver esse mistério executando o comando abaixo. cp copy.c whodunit.c
Você pode se surpreender com quantas linhas de código precisa escrever para ajudar o Sr. Boddy. Não há nada desnecessário escondido em smiley.bmp , então sinta-se à vontade para testar o programa neste arquivo. É pequeno e você pode comparar a saída do seu programa e a saída do xxd durante o desenvolvimento (ou talvez haja algo oculto em smiley.bmp ? Na verdade, não). Aliás, esse problema pode ser resolvido de diferentes maneiras. Depois de identificar o desenho do Sr. Boddy, ele descansará em paz. Como o whodunit pode ser implementado de várias maneiras, você não poderá verificar a exatidão das implementações com check50 . E deixe que isso estrague a sua diversão, mas a solução dos assistentes também não está disponível para o problema do policial . Por fim, no arquivo In ~/workspace/pset4/questions.txt , responda a seguinte pergunta: Whodunit? //ктоэтосделал?
redimensionar
Bem, agora - o próximo teste! Vamos escrever um programa chamado resize em resize.c . Ele redimensionará uma imagem BMP descompactada de 24 bits em etapas de n. Sua aplicação deve aceitar exatamente três argumentos de linha de comando, sendo o primeiro (n) um número inteiro não maior que 100, o segundo sendo o nome do arquivo que será modificado e o terceiro sendo o nome da versão salva do arquivo modificado. arquivo.Usage: ./resize n infile outfile
Com tal programa, poderíamos criar large.bmp a partir de small.bmp redimensionando este último por 4 (ou seja, multiplicando a largura e a altura por 4), conforme mostrado abaixo. Para simplificar, você pode iniciar a tarefa copiando copy.c./resize 4 small.bmp large.bmp
novamente e nomeando a cópia resize.c . Mas primeiro, pergunte-se e responda a estas perguntas: o que significa alterar o tamanho do BMP (você pode assumir que n vezes o tamanho do arquivo não excederá 232 - 1) . Determine quais campos em BITMAPFILEHEADER e BITMAPINFOHEADER você precisa alterar. Considere se você precisa adicionar ou remover campos de scanlines . E, sim, fique grato por não estarmos pedindo para você considerar todos os valores possíveis de n de 0 a 1! (embora, se você estiver interessado, este seja um problema do livro de um hacker;)). No entanto, assumimos que para n = 1 o programa funcionará corretamente e o arquivo de saída do arquivo de saída terá o mesmo tamanho do arquivo de entrada original. Quer verificar o programa usando check50? Digite o seguinte comando: ~cs50/pset4/resize
Bem, se você quiser ver, por exemplo, os cabeçalhos
large.bmp (em uma forma mais amigável do que o xxd permite), você precisa executar o seguinte comando:
~cs50/pset4/peek large.bmp
Melhor ainda, se você quiser comparar seus cabeçalhos com os cabeçalhos dos arquivos do assistente CS50. Você pode executar comandos dentro do diretório
~/workspace/pset4/bmp (pense no que cada comando faz). Se você usou
malloc , certifique-se de usar
free para evitar vazamentos de memória. Tente usar
o valgrind para verificar se há vazamentos.
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
Como decidir?
-
Abra o arquivo que precisamos ampliar, e também crie e abra um novo arquivo no qual será gravada a imagem ampliada;
-
atualizar informações de cabeçalho para o arquivo de saída. Como nossa imagem está no formato BMP e estamos alterando seu tamanho, precisamos escrever o cabeçalho do novo arquivo com as novas dimensões. O que vai mudar? O tamanho do arquivo, bem como o tamanho da imagem - largura e altura.
-
Lemos o arquivo de saída, linha por linha, pixel por pixel. Para fazer isso, recorremos novamente à nossa biblioteca de E/S de arquivos e à função fread. É necessário um ponteiro para uma estrutura que conterá os bytes lidos, o tamanho do único elemento que iremos ler, o número de tais elementos e um ponteiro para o arquivo do qual iremos ler.
-
Aumentamos cada linha horizontalmente de acordo com a escala especificada e escrevemos o resultado no arquivo de saída.
Como escrevemos arquivos? Temos uma função fwrite, para a qual passamos um indicador para a estrutura onde estão os dados a serem gravados no arquivo, o tamanho do elemento, seu número e um ponteiro para o arquivo de saída. Para organizar o loop, podemos usar o loop for com o qual já estamos familiarizados .
-
Preencha nos espaços! Se o número de pixels em uma linha não for múltiplo de quatro, devemos adicionar "alinhamento" - zero bytes. Precisaremos de uma fórmula para calcular o tamanho do alinhamento. Para escrever bytes nulos em um arquivo de saída, você pode usar a função fputc, passando o caractere que deseja escrever e um ponteiro para o arquivo de saída.
Agora que esticamos a string horizontalmente e adicionamos um alinhamento ao arquivo de saída, precisamos mover a posição atual no arquivo de saída porque precisamos pular o alinhamento.
-
Aumente o tamanho vertical. É mais complicado, mas podemos usar o código de exemplo de copy.c (copy.c abre o arquivo de saída, grava um cabeçalho no arquivo de saída, lê a imagem do arquivo de origem linha por linha, pixel por pixel, e os grava para o arquivo de saída). Com base nisso, a primeira coisa que você pode fazer é executar o seguinte comando: cp copy.c resize.c
Esticar uma imagem verticalmente significa copiar cada linha várias vezes. Existem várias maneiras diferentes de fazer isso. Por exemplo, usando reescrita, quando salvamos todos os pixels de uma linha na memória e escrevemos essa linha no arquivo de saída em um loop quantas vezes forem necessárias. Outro método é copiar novamente: após ler uma linha do arquivo de saída, gravá-la no arquivo de saída e alinhá-la, retorne a função fseek de volta ao início da linha no arquivo de saída e repita tudo novamente várias vezes.
-
Abra o arquivo com o conteúdo do cartão de memória.
-
Encontre o início do arquivo JPEG. Todos os arquivos neste cartão são imagens em formato JPEG.
recuperar
Antecipando o problema da Semana 4, passei os últimos dias olhando fotos salvas em formato JPEG pela minha câmera digital em um cartão de memória CompactFlash (CF) de 1 GB. Por favor, não me diga que passei os últimos dias no Facebook. Infelizmente, meus conhecimentos de informática deixam muito a desejar e, sem saber, apaguei acidentalmente todas as fotos! Felizmente, no mundo da informática, “excluído” geralmente não significa “morto”. Meu computador insiste que o cartão de memória está vazio, mas sei que está mentindo. Tarefa: Escreva um programa em ~/workspace/pset4/jpg/recover.c que recupere essas fotos. Hmmm. Ok, aqui vai mais um esclarecimento. Embora o formato JPEG seja mais complexo que o BMP, o JPEG possui "assinaturas", padrões de bytes que ajudam a distingui-lo de outros formatos de arquivo. A maioria dos arquivos JPEG começa com os três bytes a seguir:0xff 0xd8 0xff
Do primeiro ao terceiro byte, da esquerda para a direita. O quarto byte provavelmente será uma das seguintes combinações: 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. Em outras palavras, os primeiros quatro bits do quarto byte de um arquivo JPEG são 1110. Provavelmente, se você encontrar um desses padrões na unidade onde as fotos foram armazenadas (como meu cartão de memória), este será o início do arquivo JPEG. Claro, você pode encontrar isso em qual disco por puro acaso; a recuperação de dados não pode ser chamada de ciência exata.
GO TO FULL VERSION