Préparation au travail
Comme toujours, ouvrez d’abord une fenêtre de terminal et exécutez la commande.update50
pour vous assurer que votre candidature est déjà à jour. Avant de commencer, suivez ceci cd ~ / workspace
wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip
pour télécharger une archive ZIP de cette tâche. Maintenant, si vous exécutez ls , vous verrez que vous avez un fichier appelé pset4.zip dans votre répertoire ~/workspace . Extrayez-le à l'aide de la commande : Si vous exécutez unzip pset4.zip
à nouveau la commande ls , vous verrez qu'un autre répertoire est apparu. Vous pouvez maintenant supprimer le fichier zip comme indiqué ci-dessous : rm -f pset4.zip
Ouvrons le répertoire pset4 cd pset4
, exécutons ls et assurons-nous que le répertoire contient bmp / jpg / questions.txt
polar ou "Qui a fait ça?"
Si vous avez déjà vu le bureau Windows XP par défaut (https://en.wikipedia.org/wiki/Bliss_(image)) (collines et ciel bleu), alors vous avez vu BMP. Sur les pages Web, vous avez probablement vu des GIF. Avez-vous regardé des photos numériques ? Nous avons donc eu la joie de voir JPEG. Si vous avez déjà pris une capture d'écran sur un Mac, vous avez probablement vu un fichier PNG. Renseignez-vous sur Internet sur les formats BMP, GIF, JPEG, PNG et répondez à ces questions :-
Combien de couleurs chaque format prend-il en charge ?
-
Quel format prend en charge l’animation ?
-
Quelle est la différence entre la compression avec et sans perte ?
-
Lequel de ces formats utilise la compression avec perte ?
-
Que se passe-t-il d'un point de vue technique lorsqu'un fichier est supprimé dans un système de fichiers FAT ?
-
Que peut-on faire pour garantir (avec une forte probabilité) que les fichiers supprimés ne pourront pas être récupérés ?
xxd -c 24 -g 3 -s 54 smiley.bmp
Vous devriez voir ce qui est affiché ci-dessous ; Nous avons à nouveau surligné en rouge toutes les instances de 0000ff. Dans l'image de la colonne la plus à gauche, vous pouvez voir les adresses du fichier, qui sont équivalentes au décalage par rapport au premier octet du fichier. Tous sont donnés dans un système de nombres hexadécimaux. Si nous convertissons l'hexadécimal 00000036 en décimal, nous obtenons 54. Vous regardez donc le 54ème octet de smiley.bmp . Rappelons que dans les fichiers BMP 24 bits, les 14 + 40 = 54 octets sont remplis de métadonnées. Donc si vous voulez voir les métadonnées, exécutez la commande suivante : xxd -c 24 -g 3 smiley.bmp
Si smiley.bmp contient des caractères ASCII , nous les verrons dans la colonne la plus à droite de xxd au lieu de tous ces points. Ainsi, le smiley est un BMP 24 bits (chaque pixel est représenté par 24 ÷ 8 = 3 octets) avec une taille (résolution) de 8x8 pixels. Chaque ligne (ou "Scanline" comme on l'appelle) occupe ainsi (8 pixels) x (3 octets par pixel) = 24 octets. Ce nombre est un multiple de quatre, ce qui est important car le fichier BMP est stocké légèrement différemment si le nombre d'octets dans la ligne n'est pas un multiple de quatre. Ainsi, dans small.bmp, un autre fichier BMP 24 bits dans notre dossier, vous pouvez voir une boîte verte de 3x3 pixels. Si vous l'ouvrez dans une visionneuse d'images, vous verrez qu'elle ressemble à l'image ci-dessous, mais en plus petite taille. Chaque ligne de small.bmp occupe ainsi (3 pixels) × (3 octets par pixel) = 9 octets, ce qui n'est pas un multiple de 4. Pour obtenir une longueur de ligne multiple de 4, elle est complétée par des zéros supplémentaires : entre 0 et 3 octets, nous remplissons chaque ligne au format BMP 24 bits (pouvez-vous deviner pourquoi ?). Pour small.bmp , 3 octets de zéros sont nécessaires, puisque (3 pixels) x (3 octets par pixel) + (3 octets de remplissage) = 12 octets, ce qui est en réalité un multiple de 4. Pour "voir" ce remplissage, faites ce qui suit. xxd -c 12 -g 3 -s 54 small.bmp
Notez que nous utilisons une valeur différente pour -c qu'avec smiley.bmp , donc xxd ne génère cette fois que 4 colonnes (3 pour le carré vert et 1 pour le remplissage). Pour plus de clarté, nous avons mis en surbrillance toutes les instances de 00ff00 en vert. Par contraste, utilisons xxd pour le fichier large.bmp . Il ressemble exactement à small.bmp, seule sa résolution est de 12x12 pixels, soit quatre fois plus grande. Exécutez la commande ci-dessous. Vous devrez peut-être agrandir la fenêtre pour éviter le transfert. xxd -c 36 -g 3 -s 54 large.bmp
Vous verrez quelque chose comme ceci : Attention, il n'y a aucune digression dans ce BMP ! Après tout, (12 pixels) × (3 octets par pixel) = 36 octets, et c'est un multiple de 4. L'éditeur hexadécimal xxd nous a montré les octets dans nos fichiers BMP. Comment pouvons-nous les obtenir par programmation ? Dans copy.c, il existe un programme dont le seul but dans la vie est de créer une copie du BMP, pièce par pièce. Oui, vous pouvez utiliser cp pour cela . Cependant, cp ne pourra pas aider M. Boddy. Espérons que copy.c fasse cela, alors c'est parti : ./copy smiley.bmp copy.bmp
si vous exécutez maintenant ls (avec l'indicateur approprié), vous verrez que smiley.bmp et copy.bmp ont effectivement la même taille. Vérifions à nouveau si cela est réellement vrai ? diff smiley.bmp copy.bmp
Si cette commande n'affiche rien à l'écran, cela signifie que les fichiers sont bien identiques (important : certains programmes, comme Photoshop, incluent des zéros à la fin de certains VMP. Notre version de copie les supprime, alors ne le faites pas vous inquiétez si, en cas de copie d'autres BMP que vous avez téléchargés ou créés pour les tests, la copie sera quelques octets plus petite que l'original). Vous pouvez ouvrir les deux fichiers dans la visionneuse d'images de Ristretto (double-clic) pour le confirmer visuellement. Mais diff fait cette comparaison octet par octet, donc sa vision est plus nette que la vôtre ! Comment cette copie a-t-elle été créée ? Il s'avère que copy.c est lié à bmp.h . Assurons-nous-en : ouvrez bmp.h. Vous y verrez les définitions réelles de ces en-têtes que nous avons déjà mentionnés, adaptées des propres implémentations de Microsoft. De plus, ce fichier définit les types de données BYTE, DWORD, LONG et WORD, qui sont les types de données généralement trouvés dans le monde de la programmation Win32 (c'est-à-dire Windows). Notez qu’il s’agit essentiellement d’alias de primitives que vous connaissez (espérons-le) déjà. Il s'avère que BITMAPFILEHEADER et BITMAPINFOHEADER utilisaient ces types. Ce fichier définit également une structure appelée RGBTRIPLE. Il « encapsule » trois octets : un bleu, un vert et un rouge (c'est l'ordre dans lequel on va chercher les triplets RVB sur le disque). En quoi ces structures sont-elles utiles ? Pour récapituler, un fichier est simplement une séquence d'octets (ou finalement de bits) sur le disque. Cependant, ces octets sont généralement ordonnés de manière à ce que les premiers représentent quelque chose, puis les suivants représentent autre chose, et ainsi de suite. Les « formats » de fichiers existent parce que nous avons des normes, ou des règles, qui définissent la signification des octets. Désormais, nous pouvons simplement lire le fichier du disque vers la RAM sous la forme d'un grand tableau d'octets. Et nous nous souvenons que l'octet en position [i] représente une chose, tandis que l'octet en position [j] est autre chose. Mais pourquoi ne pas donner des noms à certains de ces octets afin que nous puissions les récupérer plus facilement de la mémoire ? C’est exactement pour cela que les structures de bmp.h nous aident. Au lieu de considérer un fichier comme une longue séquence d’octets, nous le voyons décomposé en blocs plus compréhensibles – des séquences de structures. Rappelez-vous que smiley.bmp a une résolution de 8x8 pixels, il occupe donc 14 + 40 + (8 × 8) × 3 = 246 octets sur le disque (vous pouvez vérifier cela en utilisant la commande ls). Voici à quoi cela ressemble sur le disque selon Microsoft : Nous pouvons voir que l'ordre est important lorsqu'il s'agit des membres des structures. L'octet 57 est rgbtBlue (pas, disons, rgbtRed) car rgbtBlue est défini en premier dans RGBTRIPLE. Soit dit en passant, notre utilisation de l'attribut packagé garantit que clang n'essaie pas d'« aligner les mots » des membres (l'adresse du premier octet de chaque membre étant un multiple de 4), afin que nous ne nous retrouvions pas avec des trous dans nos structures qui n'existent pas du tout sur disque. Allons-nous en. Recherchez les URL qui correspondent à BITMAPFILEHEADER et BITMAPINFOHEADER, selon les commentaires dans bmp.h. Attention, grand moment : vous commencez à utiliser MSDN (Microsoft Developer Network) ! Au lieu de faire défiler plus loin copy.c , répondez à quelques questions pour comprendre comment fonctionne le code. Comme toujours, la commande man est votre véritable amie, et maintenant aussi MSDN. Si vous ne connaissez pas les réponses, recherchez-les sur Google et réfléchissez-y. Vous pouvez également vous référer au fichier stdio.h sur https://reference.cs50.net/.
-
Définissez un point d'arrêt dans main (en cliquant à gauche de la règle avec les numéros de ligne principale).
-
Dans un onglet de terminal , accédez à ~/workspace/pset4/bmp et compilez copy.c dans le programme de copie à l'aide de make.
-
Exécutez debug50 copy smiley.bmp copy.bmp , cela ouvrira le panneau du débogueur sur la droite.
-
Parcourez le programme étape par étape en utilisant le panneau de droite. Notez bf et bi . Dans ~/workspace/pset4/questions.txt , répondez aux questions :
-
Qu'est-ce que stdint.h ?
-
Quel est l'intérêt d'utiliser uint8_t , uint32_t , int32_t et uint16_t dans un programme ?
-
Combien d'octets BYTE , DWORD , LONG et WORD contiennent respectivement (en supposant une architecture 32 bits) ?
- Que doivent être (ASCII, décimal ou hexadécimal) les deux premiers octets d'un fichier BMP ? (les octets de tête, qui sont utilisés pour identifier le format de fichier (avec une forte probabilité) sont souvent appelés « nombres magiques »).
-
Quelle est la différence entre bfSize et biSize ?
-
Que signifie un biHeight négatif ?
-
Quel champ dans BITMAPINFOHEADER définit la profondeur de couleur en BMP (c'est-à-dire bits par pixel) ?
-
Pourquoi la fonction fopen peut-elle renvoyer NULL dans copy.c 37 ?
-
Pourquoi le troisième argument à fread dans notre code est-il égal à 1 ?
-
Quelle valeur dans copy.c 70 définit le remplissage si bi.biWidth est 3 ?
-
Que fait fseek ?
-
Qu'est-ce que SEEK_CUR ?
Revenons à M. Boddy. Exercice:
Écrivez un programme appelé whodunit dans un fichier appelé whodunit.c qui montre le dessin de M. Boddy. Hummm, quoi ? Comme pour la copie, le programme doit prendre exactement deux arguments de ligne de commande, et si vous exécutez votre programme comme indiqué ci-dessous, le résultat sera stocké dans verdict.bmp, dans lequel le dessin de M. Boddy ne sera pas bruyant../whodunit clue.bmp verdict.b
Laissez-nous vous suggérer de commencer à résoudre ce mystère en exécutant la commande ci-dessous. cp copy.c whodunit.c
Vous pourriez être étonné du nombre de lignes de code que vous devez écrire pour aider M. Boddy. Il n'y a rien d'inutile caché dans smiley.bmp , alors n'hésitez pas à tester le programme sur ce fichier. C'est petit, et vous pouvez comparer la sortie de votre programme et la sortie de xxd pendant le développement (ou peut-être qu'il y a quelque chose de caché dans smiley.bmp ? En fait, non). Soit dit en passant, ce problème peut être résolu de différentes manières. Une fois que vous aurez identifié le dessin de M. Boddy, il reposera en paix. Étant donné que Whodunit peut être implémenté de plusieurs manières, vous ne pourrez pas vérifier l'exactitude des implémentations avec check50 . Et laissez cela gâcher votre plaisir, mais la solution des assistants n'est pas non plus disponible pour le problème du polar . Enfin, dans le fichier In ~/workspace/pset4/questions.txt , répondez à la question suivante : Whodunit? //ктоэтосделал?
redimensionner
Eh bien, maintenant – le prochain test ! Écrivons un programme appelé resize dans resize.c . Il redimensionnera une image BMP 24 bits non compressée par étapes de n. Votre application doit accepter exactement trois arguments de ligne de commande, le premier (n) étant un nombre entier ne dépassant pas 100, le deuxième étant le nom du fichier qui sera modifié et le troisième étant le nom de la version enregistrée du fichier modifié. déposer.Usage: ./resize n infile outfile
Avec un tel programme, nous pourrions créer large.bmp à partir de small.bmp en redimensionnant ce dernier par 4 (c'est-à-dire en multipliant à la fois la largeur et la hauteur par 4), comme indiqué ci-dessous. ./resize 4 small.bmp large.bmp
Pour plus de simplicité, vous pouvez démarrer la tâche en copiant à nouveau copy.c et en nommant la copie resize.c . Mais d'abord, posez-vous et répondez à ces questions : que signifie changer la taille du BMP (vous pouvez supposer que n fois la taille du fichier entrant ne dépassera pas 232 - 1) . Déterminez les champs de BITMAPFILEHEADER et BITMAPINFOHEADER que vous devez modifier. Déterminez si vous devez ajouter ou supprimer des champs de lignes de balayage . Et oui, soyez reconnaissant que nous ne vous demandions pas de considérer toutes les valeurs possibles de n de 0 à 1 ! (même si, si cela vous intéresse, c'est un problème issu du livre d'un hacker ;)). Cependant, nous supposons que pour n = 1, le programme fonctionnera correctement et le fichier de sortie aura la même taille que le fichier d'entrée d'origine. Voulez-vous vérifier le programme à l’aide de check50 ? Tapez la commande suivante : ~cs50/pset4/resize
Eh bien, si vous souhaitez voir, par exemple, les en-têtes
large.bmp (sous une forme plus conviviale que ne le permet xxd), vous devez exécuter la commande suivante :
~cs50/pset4/peek large.bmp
Encore mieux, si vous souhaitez comparer vos headers avec les en-têtes des fichiers assistants CS50. Vous pouvez exécuter des commandes dans votre répertoire
~/workspace/pset4/bmp (pensez à ce que fait chaque commande). Si vous avez utilisé
malloc , assurez-vous d'utiliser
free pour éviter les fuites de mémoire. Essayez d'utiliser
valgrind pour vérifier les fuites.
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
Comment décider ?
-
Ouvrez le fichier que nous devons agrandir, ainsi que créez et ouvrez un nouveau fichier dans lequel l'image agrandie sera enregistrée ;
-
mettre à jour les informations d’en-tête du fichier de sortie. Puisque notre image est au format BMP et que nous modifions sa taille, nous devons écrire l'en-tête du nouveau fichier avec les nouvelles dimensions. Qu'est-ce qui va changer ? La taille du fichier, ainsi que la taille de l’image – sa largeur et sa hauteur.
-
Nous lisons le fichier sortant, ligne par ligne, pixel par pixel. Pour ce faire, nous nous tournons à nouveau vers notre bibliothèque d'E/S de fichiers et la fonction fread. Il faut un pointeur vers une structure qui contiendra les octets lus, la taille de l'élément unique que nous allons lire, le nombre de ces éléments, et un pointeur vers le fichier à partir duquel nous allons lire.
-
Nous augmentons chaque ligne horizontalement selon l'échelle spécifiée et écrivons le résultat dans le fichier de sortie.
Comment écrivons-nous des fichiers ? Nous avons une fonction fwrite, à laquelle nous transmettons un indicateur sur la structure où se trouvent les données à écrire dans le fichier, la taille des éléments, leur numéro et un pointeur vers le fichier de sortie. Pour organiser la boucle, nous pouvons utiliser la boucle for que nous connaissons déjà .
-
Remplir les trous! Si le nombre de pixels dans une ligne n'est pas un multiple de quatre, il faut ajouter "l'alignement" - zéro octet. Nous aurons besoin d'une formule pour calculer la taille de l'alignement. Pour écrire des octets nuls dans un fichier de sortie, vous pouvez utiliser la fonction fputc, en lui passant le caractère que vous souhaitez écrire et un pointeur vers le fichier de sortie.
Maintenant que nous avons étiré la chaîne horizontalement et ajouté un alignement au fichier de sortie, nous devons déplacer la position actuelle dans le fichier de sortie car nous devons sauter par-dessus l'alignement.
-
Augmentez la taille verticale. C'est plus compliqué, mais nous pouvons utiliser l'exemple de code de copy.c (copy.c ouvre le fichier de sortie, écrit un en-tête dans le fichier de sortie, lit l'image du fichier source ligne par ligne, pixel par pixel, et les écrit au fichier de sortie). Sur cette base, la première chose que vous pouvez faire est d'exécuter la commande suivante : cp copy.c resize.c
Étirer une image verticalement signifie copier chaque ligne plusieurs fois. Il existe plusieurs manières différentes de procéder. Par exemple, en utilisant la réécriture, lorsque nous enregistrons tous les pixels d'une ligne en mémoire et écrivons cette ligne dans le fichier de sortie en boucle autant de fois que nécessaire. Une autre méthode consiste à recopier : après avoir lu une ligne du fichier sortant, l'avoir écrite dans le fichier de sortie et l'avoir alignée, renvoyez la fonction fseek au début de la ligne du fichier sortant et répétez tout plusieurs fois.
-
Ouvrez le fichier avec le contenu de la carte mémoire.
-
Recherchez le début du fichier JPEG. Tous les fichiers de cette carte sont des images au format JPEG.
récupérer
En prévision du document de travail de la semaine 4, j'ai passé ces derniers jours à regarder des photos enregistrées au format JPEG par mon appareil photo numérique sur une carte mémoire CompactFlash (CF) de 1 Go. S'il vous plaît, ne me dites pas plutôt que j'ai passé ces derniers jours sur Facebook. Malheureusement, mes compétences en informatique laissent beaucoup à désirer, et sans le savoir, j'ai accidentellement supprimé toutes les photos ! Heureusement, dans le monde informatique, « supprimé » n’équivaut généralement pas à « tué ». Mon ordinateur insiste sur le fait que la carte mémoire est désormais vide, mais je sais qu'il ment. Devoir : Écrivez un programme dans ~/workspace/pset4/jpg/recover.c qui récupérera ces photos. Hmmm. D'accord, voici encore une précision. Bien que le format JPEG soit plus complexe que le BMP, le format JPEG possède des « signatures », des modèles d'octets qui permettent de le distinguer des autres formats de fichiers. La plupart des fichiers JPEG commencent par les trois octets suivants :0xff 0xd8 0xff
Du premier octet au troisième, de gauche à droite. Le quatrième octet sera très probablement l'une des combinaisons suivantes : 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. En d’autres termes, les quatre premiers bits du quatrième octet d’un fichier JPEG sont 1110. Il y a de fortes chances que si vous trouvez l'un de ces motifs sur le lecteur où les photos ont été stockées (comme ma carte mémoire), ce sera le début du fichier JPEG. Bien sûr, vous pouvez rencontrer ce problème sur quel disque par hasard ; la récupération de données ne peut pas être qualifiée de science exacte.
GO TO FULL VERSION