Preparándose para el trabajo
Como siempre, primero abra una ventana de terminal y ejecute el comando.update50
para asegurarse de que su solicitud ya esté actualizada. Antes de comenzar, siga esto cd ~ / workspace
wget http://cdn.cs50.net/2015/fall/psets/4/pset4/pset4.zip
para descargar un archivo ZIP de esta tarea. Ahora, si ejecuta ls , verá que tiene un archivo llamado pset4.zip en su directorio ~/workspace . Extráelo usando el comando: Si unzip pset4.zip
vuelves a ejecutar el comando ls , verás que ha aparecido otro directorio. Ahora puede eliminar el archivo zip como se muestra a continuación: rm -f pset4.zip
Abramos el directorio pset4 cd pset4
, ejecutemos ls y asegurémonos de que el directorio contenga bmp / jpg / questions.txt
novela policíaca o "¿Quién hizo esto?"
Si alguna vez ha visto el escritorio predeterminado de Windows XP (https://en.wikipedia.org/wiki/Bliss_(image)) (colinas y cielo azul), entonces ha visto BMP. En las páginas web, lo más probable es que hayas visto GIF. ¿Has mirado fotografías digitales? Entonces, tuvimos el placer de ver JPEG. Si alguna vez tomaste una captura de pantalla en una Mac, lo más probable es que hayas visto un PNG. Lea en Internet sobre los formatos BMP, GIF, JPEG, PNG y responda estas preguntas:-
¿Cuantos colores soporta cada formato?
-
¿Qué formato admite animación?
-
¿Cuál es la diferencia entre compresión con y sin pérdidas?
-
¿Cuál de estos formatos utiliza compresión con pérdida?
-
¿Qué sucede desde un punto de vista técnico cuando se elimina un archivo en un sistema de archivos FAT?
-
¿Qué se puede hacer para garantizar (con una alta probabilidad) que los archivos eliminados no se puedan recuperar?
xxd -c 24 -g 3 -s 54 smiley.bmp
Debería ver lo que se muestra a continuación; Hemos resaltado nuevamente en rojo todas las instancias de 0000ff. En la imagen de la columna de la izquierda puedes ver las direcciones del archivo, que equivalen al desplazamiento desde el primer byte del archivo. Todos ellos se dan en el sistema numérico hexadecimal. Si convertimos hexadecimal 00000036 a decimal, obtenemos 54. Entonces estás mirando el byte 54 de smiley.bmp . Recuerde que en los archivos BMP de 24 bits, los primeros 14 + 40 = 54 bytes están llenos de metadatos. Entonces, si desea ver los metadatos, ejecute el siguiente comando: xxd -c 24 -g 3 smiley.bmp
Si smiley.bmp contiene caracteres ASCII , los veremos en la columna de la derecha en xxd en lugar de todos esos puntos. Entonces, Smiley es un BMP de 24 bits (cada píxel está representado por 24 ÷ 8 = 3 bytes) con un tamaño (resolución) de 8x8 píxeles. Cada línea (o "Scanline", como se la llama) ocupa (8 píxeles) x (3 bytes por píxel) = 24 bytes. Este número es múltiplo de cuatro, y esto es importante porque el archivo BMP se almacena de manera ligeramente diferente si el número de bytes en la línea no es múltiplo de cuatro. Entonces, en small.bmp, otro archivo BMP de 24 bits en nuestra carpeta, puede ver un cuadro verde de 3x3 píxeles. Si lo abres en un visor de imágenes, verás que se parece a la imagen que se muestra a continuación, sólo que de menor tamaño. Por lo tanto, cada línea en small.bmp ocupa (3 píxeles) × (3 bytes por píxel) = 9 bytes, que no es un múltiplo de 4. Para obtener una longitud de línea que sea múltiplo de 4, se rellena con ceros adicionales: entre 0 y 3 bytes completamos cada línea en formato BMP de 24 bits (¿puedes adivinar por qué?). Para small.bmp , se necesitan 3 bytes de ceros, ya que (3 píxeles) x (3 bytes por píxel) + (3 bytes de relleno) = 12 bytes, que en realidad es un múltiplo de 4. Para "ver" este relleno, Haz lo siguiente. xxd -c 12 -g 3 -s 54 small.bmp
Tenga en cuenta que usamos un valor diferente para -c que con smiley.bmp , por lo que xxd solo genera 4 columnas esta vez (3 para el cuadrado verde y 1 para el relleno). Para mayor claridad, hemos resaltado todas las instancias de 00ff00 en verde. Por el contrario, usemos xxd para el archivo grande.bmp . Se ve exactamente igual que pequeño.bmp., sólo que su resolución es de 12x12 píxeles, es decir, cuatro veces mayor. Ejecute el siguiente comando. Es posible que tengas que ampliar la ventana para evitar la transferencia. xxd -c 36 -g 3 -s 54 large.bmp
Verá algo como esto: ¡ Tenga en cuenta que no hay digresiones en este BMP! Después de todo, (12 píxeles) × (3 bytes por píxel) = 36 bytes, y esto es un múltiplo de 4. El editor hexadecimal xxd nos mostró los bytes en nuestros archivos BMP. ¿Cómo podemos obtenerlos mediante programación? En copy.c hay un programa cuyo único propósito en la vida es crear una copia del BMP, pieza por pieza. Sí, puedes usar cp para esto . Sin embargo, cp no podrá ayudar al Sr. Boddy. Esperemos que copy.c haga esto, así que allá vamos: ./copy smiley.bmp copy.bmp
si ahora ejecuta ls (con el indicador apropiado), verá que smiley.bmp y copy.bmp son de hecho del mismo tamaño. Comprobemos nuevamente si esto es realmente cierto. diff smiley.bmp copy.bmp
Si este comando no muestra nada en la pantalla, significa que los archivos son efectivamente idénticos (importante: algunos programas, como Photoshop, incluyen ceros finales al final de algunos VMP. Nuestra versión de copia los descarta, así que no Preocúpese si, en caso de copiar otros BMP que haya descargado o creado para realizar pruebas, la copia será unos pocos bytes más pequeña que el original). Puede abrir ambos archivos en el visor de imágenes de Ristretto (doble clic) para confirmarlo visualmente. Pero diff hace esta comparación byte a byte, por lo que su visión es más nítida que la tuya. ¿Cómo se creó esta copia? Resulta que copy.c está relacionado con bmp.h. Asegurémonos: abra bmp.h. Allí verá las definiciones reales de los encabezados que ya hemos mencionado, adaptadas de las propias implementaciones de Microsoft. Además, este archivo define los tipos de datos BYTE, DWORD, LONG y WORD, que son los tipos de datos que normalmente se encuentran en el mundo de la programación Win32 (es decir, Windows). Tenga en cuenta que estos son esencialmente alias de primitivas con las que (con suerte) ya está familiarizado. Resulta que BITMAPFILEHEADER y BITMAPINFOHEADER estaban usando estos tipos. Este archivo también define una estructura llamada RGBTRIPLE. “Encapsula” tres bytes: uno azul, uno verde y uno rojo (este es el orden en el que buscaremos tripletes RGB en el disco). ¿Cómo son útiles estas estructuras? En resumen, un archivo es simplemente una secuencia de bytes (o, en última instancia, bits) en el disco. Sin embargo, estos bytes normalmente están ordenados de modo que los primeros representen algo, luego los siguientes representen algo más, y así sucesivamente. Los "formatos" de archivos existen porque tenemos estándares o reglas que definen qué significan los bytes. Ahora, podemos simplemente leer el archivo del disco a la RAM como una gran matriz de bytes. Y recordemos que el byte en la posición [i] representa una cosa, mientras que el byte en la posición [j] es otra. Pero, ¿por qué no dar nombres a algunos de estos bytes para que podamos recuperarlos más fácilmente de la memoria? Esto es exactamente en lo que nos ayudan las estructuras en bmp.h. En lugar de pensar en un archivo como una larga secuencia de bytes, lo vemos dividido en bloques más comprensibles: secuencias de estructuras. Recuerde que smiley.bmp tiene una resolución de 8x8 píxeles, por lo que ocupa 14 + 40 + (8 × 8) × 3 = 246 bytes en el disco (puede verificar esto usando el comando ls). Así es como se ve en el disco según Microsoft: Podemos ver que el orden importa cuando se trata de miembros de estructuras. El byte 57 es rgbtBlue (no, digamos, rgbtRed) porque rgbtBlue se define primero en RGBTRIPLE. Por cierto, nuestro uso del atributo empaquetado garantiza que clang no intente “alinear palabras” a los miembros (con la dirección del primer byte de cada miembro siendo un múltiplo de 4), de modo que no terminemos con agujeros en nuestras estructuras que no existen en el disco en absoluto. Vamonos. Busque las URL que coincidan con BITMAPFILEHEADER y BITMAPINFOHEADER, según los comentarios en bmp.h. Atención, gran momento: ¡estás empezando a utilizar MSDN (Microsoft Developer Network)! En lugar de desplazarse más por copy.c , responda algunas preguntas para comprender cómo funciona el código. Como siempre, el comando man es tu verdadero amigo, y ahora también MSDN. Si no sabes las respuestas, busca en Google y piénsalo. También puede consultar el archivo stdio.h en https://reference.cs50.net/.
-
Establezca un punto de interrupción en principal (haciendo clic a la izquierda de la regla con los números de línea principal).
-
En una pestaña de terminal , vaya a ~/workspace/pset4/bmp y compile copy.c en el programa de copia usando make.
-
Ejecute debug50 copy smiley.bmp copy.bmp , esto abrirá el panel del depurador a la derecha.
-
Recorre el programa paso a paso utilizando el panel de la derecha. Nota bf y bi . En ~/workspace/pset4/questions.txt , responda las preguntas:
-
¿ Qué es stdint.h ?
-
¿Cuál es el punto de usar uint8_t , uint32_t , int32_t y uint16_t en un programa?
-
¿Cuántos bytes contienen BYTE , DWORD , LONG y WORD respectivamente (suponiendo una arquitectura de 32 bits)?
- ¿Cuáles (ASCII, decimal o hexadecimal) deben ser los primeros dos bytes de un archivo BMP? (Los bytes iniciales, que se utilizan para identificar el formato del archivo (con alta probabilidad) a menudo se denominan "números mágicos").
-
¿Cuál es la diferencia entre bfSize y biSize?
-
¿Qué significa una biHeight negativa?
-
¿Qué campo en BITMAPINFOHEADER define la profundidad de color en BMP (es decir, bits por píxel)?
-
¿Por qué la función fopen puede devolver NULL en copy.c 37?
-
¿Por qué el tercer argumento de fread en nuestro código es igual a 1?
-
¿Qué valor en copy.c 70 define el relleno si bi.biWidth es 3?
-
¿Qué hace fseek?
-
¿Qué es SEEK_CUR?
Volvamos al Sr. Boddy. Ejercicio:
Escribe un programa llamado whodunit en un archivo llamado whodunit.c que muestre el dibujo del Sr. Boddy. Mmmmm, ¿qué? Al igual que copiar, el programa debe tomar exactamente dos argumentos de línea de comando, y si ejecuta su programa como se muestra a continuación, el resultado se almacenará en verdict.bmp, en el que el dibujo del Sr. Boddy no será ruidoso../whodunit clue.bmp verdict.b
Le sugerimos que comience a resolver este misterio ejecutando el siguiente comando. cp copy.c whodunit.c
Es posible que se sorprenda de cuántas líneas de código necesita escribir para ayudar al Sr. Boddy. No hay nada innecesario oculto en smiley.bmp , así que siéntete libre de probar el programa en este archivo. Es pequeño y puede comparar el resultado de su programa y el resultado de xxd durante el desarrollo (¿o tal vez hay algo oculto en smiley.bmp ? En realidad, no). Por cierto, este problema se puede solucionar de diferentes formas. Una vez que identifiques el dibujo del Sr. Boddy, descansará en paz. Dado que whodunit se puede implementar de varias maneras, no podrá verificar la exactitud de las implementaciones con check50 . Y deja que eso arruine tu diversión, pero la solución de los asistentes tampoco está disponible para el problema de la novela policíaca . Finalmente, en el archivo In ~/workspace/pset4/questions.txt , responda la siguiente pregunta: Whodunit? //ктоэтосделал?
cambiar el tamaño
Bueno, ahora, ¡la próxima prueba! Escribamos un programa llamado cambiar tamaño en resize.c . Cambiará el tamaño de una imagen BMP de 24 bits sin comprimir en pasos de n. Su aplicación debe aceptar exactamente tres argumentos de línea de comando, siendo el primero (n) un número entero no mayor a 100, el segundo el nombre del archivo que se modificará y el tercero el nombre de la versión guardada del archivo modificado. archivo.Usage: ./resize n infile outfile
Con un programa de este tipo, podríamos crear un archivo grande.bmp a partir de un archivo pequeño.bmp cambiando el tamaño de este último por 4 (es decir, multiplicando el ancho y el alto por 4), como se muestra a continuación. Para simplificar, puede iniciar la tarea copiando copy.c./resize 4 small.bmp large.bmp
nuevamente y nombrando la copia resize.c . Pero primero, pregúntese y responda estas preguntas: ¿qué significa cambiar el tamaño de BMP (puede asumir que n veces el tamaño del archivo no excederá 232 - 1) ? Determine qué campos en BITMAPFILEHEADER y BITMAPINFOHEADER necesita cambiar. Considere si necesita agregar o eliminar campos de líneas de exploración . Y sí, ¡agradezca que no le pedimos que considere todos los valores posibles de n de 0 a 1! (aunque, si estás interesado, este es un problema sacado de un libro de hackers;)). Sin embargo, asumimos que para n = 1 el programa funcionará correctamente y el archivo de salida tendrá el mismo tamaño que el archivo de entrada original. ¿Quieres comprobar el programa usando check50? Escriba el siguiente comando: ~cs50/pset4/resize
Bueno, si desea ver, por ejemplo, los encabezados
grandes.bmp (en una forma más fácil de usar que la que permite xxd), debe ejecutar el siguiente comando:
~cs50/pset4/peek large.bmp
Aún mejor, si desea comparar sus encabezados con los encabezados de los archivos del asistente CS50. Puede ejecutar comandos dentro de su directorio
~/workspace/pset4/bmp (piense en lo que hace cada comando). Si usó
malloc , asegúrese de usar
free para evitar pérdidas de memoria. Intente usar
valgrind para comprobar si hay fugas.
./resize 4 small.bmp student.bmp
~cs50/pset4/resize 4 small.bmp staff.bmp
~cs50/pset4/peek student.bmp staff.bmp
¿Cómo decidir?
-
Abra el archivo que necesitamos ampliar, y también cree y abra un nuevo archivo en el que se grabará la imagen ampliada;
-
actualizar la información del encabezado del archivo de salida. Como nuestra imagen está en formato BMP y estamos cambiando su tamaño, necesitamos escribir el encabezado del nuevo archivo con las nuevas dimensiones. ¿Qué cambiará? El tamaño del archivo, así como el tamaño de la imagen: su ancho y alto.
-
Leemos el archivo saliente, línea por línea, píxel por píxel. Para hacer esto, volvemos a nuestra biblioteca de E/S de archivos y a la función fread. Toma un puntero a una estructura que contendrá los bytes leídos, el tamaño del elemento único que vamos a leer, el número de dichos elementos y un puntero al archivo desde el cual leeremos.
-
Aumentamos cada línea horizontalmente de acuerdo con la escala especificada y escribimos el resultado en el archivo de salida.
¿Cómo escribimos archivos? Tenemos una función fwrite, a la que le pasamos un indicador de la estructura donde se encuentran los datos a escribir en el archivo, el tamaño del elemento, su número y un puntero al archivo de salida. Para organizar el bucle, podemos utilizar el bucle for con el que ya estamos familiarizados .
-
¡Rellena los huecos! Si el número de píxeles en una línea no es múltiplo de cuatro, debemos agregar "alineación": cero bytes. Necesitaremos una fórmula para calcular el tamaño de alineación. Para escribir bytes nulos en un archivo de salida, puede usar la función fputc, pasándole el carácter que desea escribir y un puntero al archivo de salida.
Ahora que estiramos la cadena horizontalmente y agregamos una alineación al archivo de salida, necesitamos mover la posición actual en el archivo de salida porque necesitamos saltar sobre la alineación.
-
Aumentar el tamaño vertical. Es más complicado, pero podemos usar el código de muestra de copy.c (copy.c abre el archivo de salida, escribe un encabezado en el archivo de salida, lee la imagen del archivo fuente línea por línea, píxel por píxel y los escribe al archivo de salida). En base a esto, lo primero que puedes hacer es ejecutar el siguiente comando: cp copy.c resize.c
Estirar una imagen verticalmente significa copiar cada línea varias veces. Hay varias formas diferentes de hacer esto. Por ejemplo, usando la reescritura, cuando guardamos todos los píxeles de una línea en la memoria y escribimos esta línea en el archivo de salida en un bucle tantas veces como sea necesario. Otro método es volver a copiar: después de leer una línea del archivo saliente, escribirla en el archivo de salida y alinearla, devolver la función fseek al principio de la línea en el archivo saliente y repetir todo varias veces.
-
Abra el archivo con el contenido de la tarjeta de memoria.
-
Busque el comienzo del archivo JPEG. Todos los archivos de esta tarjeta son imágenes en formato JPEG.
recuperar
Anticipándome al artículo problemático de la Semana 4, he pasado los últimos días mirando fotografías guardadas en formato JPEG con mi cámara digital en una tarjeta de memoria CompactFlash (CF) de 1 GB. Por favor, no me digas que en realidad he pasado los últimos días en Facebook. Desafortunadamente, mis habilidades informáticas dejan mucho que desear y, sin saberlo, ¡borré accidentalmente todas las fotos! Afortunadamente, en el mundo de la informática, "eliminado" normalmente no equivale a "eliminado". Mi computadora insiste en que la tarjeta de memoria ahora está vacía, pero sé que miente. Tarea: escriba un programa en ~/workspace/pset4/jpg/recover.c que recupere estas fotos. Mmm. Bien, aquí hay una aclaración más. Aunque el formato JPEG es más complejo que BMP, JPEG tiene "firmas", patrones de bytes que ayudan a distinguirlo de otros formatos de archivo. La mayoría de los archivos JPEG comienzan con los siguientes tres bytes:0xff 0xd8 0xff
del primer byte al tercero, de izquierda a derecha. Lo más probable es que el cuarto byte sea una de las siguientes combinaciones: 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,0xe8, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef. En otras palabras, los primeros cuatro bits del cuarto byte de un archivo JPEG son 1110. Lo más probable es que, si encuentra uno de estos patrones en la unidad donde se almacenaron las fotos (como mi tarjeta de memoria), este sea el comienzo del archivo JPEG. Por supuesto, esto se puede encontrar en qué disco por pura casualidad; la recuperación de datos no se puede llamar una ciencia exacta.
GO TO FULL VERSION