Masquer des informations dans les chats

24

Vous êtes un agent secret essayant de communiquer avec votre patrie. Bien sûr, les informations doivent être cachées afin que personne ne laisse tomber votre message. Quoi de mieux qu'un chat? Tout le monde aime les images drôles de chats [citation nécessaire] , donc ils ne soupçonneront pas que des informations secrètes s'y cachent!


Inspiré par l'algorithme que le jeu Monaco utilise pour enregistrer les informations de niveau des niveaux partagés, il vous incombe d'écrire un programme qui a codé les informations dans le bit le moins significatif des couleurs d'une image.

Format d'encodage:

  • Les 24 premiers bits déterminent la longueur de la chaîne d'octets codée restante en bits
  • L'image est lue de gauche à droite et de haut en bas, commençant évidemment dans le pixel supérieur gauche
  • Les canaux sont lus du rouge au vert au bleu
  • Le bit le moins significatif de chaque canal est lu
  • Les bits sont enregistrés dans l'ordre Big Endian

Règles:

  • Votre programme prend une seule chaîne d'octets à encoder et un seul nom de fichier d'image pour l'image de base
  • L'image résultante doit être sortie en tant que fichier PNG de couleur vraie
  • Vous pouvez utiliser les E / S sous la forme que vous souhaitez (ARGV, STDIN, STDOUT, écriture / lecture à partir d'un fichier), tant que vous indiquez comment utiliser votre programme
  • Vous devez choisir une image aléatoire d'un chat drôle et y coder votre programme pour montrer que votre programme fonctionne
  • Vous pouvez supposer que vous ne disposez que d'une entrée valide, si la quantité de bits n'est pas suffisante, l'image n'est pas au format couleur véritable, l'image n'existe pas ou des problèmes similaires, vous pouvez faire ce que vous voulez
  • Vous pouvez supposer que l'image fournie ne contient aucun canal alpha
  • La longueur est comptée en octets UTF-8 sans BOM

Vous pouvez utiliser ce script PHP pour tester votre solution, fournir le nom du fichier PNG comme premier argument de ligne de commande:

<?php
if ($argc === 1) die('Provide the filename of the PNG to read from');
$imageSize = @getimagesize($argv[1]);

if ($imageSize === false) die('Not a PNG file');
list($width, $height) = $imageSize;

$image = imagecreatefrompng($argv[1]);
$read = 0;
$bits = '';
for ($y = 0; $y < $height; $y++) {
    for ($x = 0; $x < $width; $x++) {
        $colorAt = imagecolorat($image, $x, $y);
        $red = ($colorAt >> 16) & 0xFF;
        $green = ($colorAt >> 8) & 0xFF;
        $blue = ($colorAt >> 0) & 0xFF;

        $bits .= ($red & 1).($green & 1).($blue & 1);
        $read += 3;
        if ($read == 24) {
            $length = (int) bindec($bits);
            $bits = '';
        }
        else if ($read > 24 && ($read - 24) > $length) {
            $bits = substr($bits, 0, $length);
            break 2;
        }
    }
}
if (strlen($bits) !== $length) die('Not enough bits read to fulfill the length');
$parts = str_split($bits, 8);
foreach ($parts as $part) {
    echo chr(bindec($part));
}
TimWolla
la source
Votre spécification indique "votre programme prend une seule image comme base". Dans Mathematica, une image n'est en fait qu'une expression comme tout le reste, donc techniquement cette spécification me permettrait de faire le chargement de fichier en dehors du code qui effectue le calcul (avec le paramètre d'entrée étant une image réelle au lieu du nom de fichier de l'image) . Si vous ne voulez pas de trucs comme ça, vous voudrez peut-être spécifier que le programme doit prendre le nom de fichier d'une image en entrée.
Martin Ender
4
ME helpimtrappedinacatfactory OW
TheDoctor
De même, les bits non utilisés pour l'encodage doivent-ils rester intacts? Ou pouvons-nous les régler sur ce que nous voulons (car cela n'affectera pas vraiment la qualité de l'image et n'a pas d'importance pour le décodage)?
Martin Ender
1
Puis-je utiliser une bibliothèque non intégrée pour charger et enregistrer le fichier png, par exemple PIL en Python?
Claudiu
1
@TimWolla Du chat? Gardez-le à l'intérieur et surveillez le bac à litière. De la photo? Si vous prenez une photo aux rayons X suffisamment haute résolution, vous pourrez peut-être voir l'état des transistors individuels dans la puce flash. Je suis certain que ce doit être la méthode la plus efficace de transmission d'informations secrètes jamais conçue, bien que le chat puisse avoir d'autres idées.
Sonic Atom

Réponses:

3

Perl et ImageMagick (Linux), 198 190

Edit: Par une coïncidence, plus tôt, j'ai testé sur un ordinateur avec la version Q8 (profondeur 8 bits) d'ImageMagick installée. La version Q16 «standard» nécessite une explicite -depth 8sur la ligne de commande. Sous Linux, le identifyrésultat nécessite également la suppression de la nouvelle ligne. Les deux facteurs conduisent à une augmentation de la taille du code, donc je poste la version Linux (probablement Mac aussi) comme réponse, avec des correctifs appliqués, et aussi avec certains éléments Windows uniquement supprimés (conversion cr-lf, binaire vs texte, etc.). La version portable (légèrement plus longue) est publiée vers la fin.

Avec de nouvelles lignes pour la lisibilité:

$/=$1;
<>=~/\n/;
$_=`identify -format %wx%h $\``;
chop;
open O,"|convert -size $_ -depth 8 rgb: $`.png";
$_=`convert $\` rgb:`;
print O$_&y//\376/cr|unpack('B*',pack(NX,2048*length$').$')&y//\1/cr

Courir:

perl cat.pl

Il lit à partir de STDIN, le nom du fichier image sur la première ligne, le message «secret» suit, terminé par ctrl-D. Le nom du fichier de sortie est d'origine avec en .pngannexe - pas très agréable, cela n'est fait que pour la brièveté.

Voici une image avec des informations très secrètes cachées à l'intérieur:

entrez la description de l'image ici

Et avec quelques commentaires:

# Undef input record separator, because we'll slurp input.

$/=$1;

# Read from STDIN, separate first line. 
# $` (prematch) contains image file name,
# $' (postmatch) - text to encode.

<>=~/\n/;

# Get IM's 'identify' output, width and height of the image. 
# Note: we don't have to separate them, \d+x\d+ string will 
# do just fine.

$_=`identify -format %wx%h $\``;
chop;

# Open output pipe - IM's 'convert' command that takes raw RGB data from 
# STDIN and writes output to PNG file. Interpolated into this command
# line is previous IM's 'identify' result. Convert wants '-size' command
# option in case of raw RGB input - for obvious reason.

open O,"|convert -size $_ -depth 8 rgb: $`.png";

# Get raw RGB data into $_.

$_=`convert $\` rgb:`;

# Last line does all the job. 

# y//\376/cr --- create string same length as $_, fill with 0xFE
# $_&y//\376/cr --- zero least significant bit for all image bytes (1).
# pack(NX,2048*length$') --- multiply by 8 (bytes to bits count) and 
#         shift left by 8 (because we'll pack long integer into 3 bytes) -
#         i.e. multiply by 2048.
# unpack('B*',pack(NX,2048*length$').$') ---- append 'secret text' to 
#       its encoded length and convert to 'binary' (consisting of 1 and 
#       0 characters) string.
# ... &y//\1/cr --- equivalent of tr/01/\0\1/. We don't have to worry 
#       that raw RGB length is larger than encoded text length, because
#       '&' truncates longer string argument (2).
# Then bitwise-'or' (1) and (2) strings.

print O$_&y//\376/cr|unpack('B*',pack(NX,2048*length$').$')&y//\1/cr

Vient ensuite la version portable, fonctionne à la fois sur Windows (à utiliser ctrl-Zpour terminer la saisie) et Linux, le nombre d'octets est de 244.

$_=do{local$/;<>};
/\n/;
$_=`identify -format %wx%h $\``;
chomp;
open I,'-|:raw',"convert $` rgb:";
open O,'|-:raw',"convert -size $_ -depth 8 rgb: $`.png";
$_=do{local$/;<I>};
print O$_&y//\376/cr|unpack('B*',pack(NX,2048*length$').$')&y//\1/cr
user2846289
la source
10

Mathematica, 255 234 206 octets

J'ai vu tellement de 255s en testant cela, je suis déraisonnablement heureux de la taille du code. :) Et puis mon ambition de jouer au golf encore plus loin a pris le dessus sur moi ...

f=(j=ImageData[Import@#2,t="Byte"];k=(d=IntegerDigits)[j,2,8]~Flatten~2;n=1;(k[[n++,-1]]=#)&/@d[Length@#,2,24]~Join~#&[Join@@d[ToCharacterCode@#,2,8]];ArrayReshape[#~FromDigits~2&/@k,Dimensions@j]~Image~t)&

C'est techniquement une fonction et non un "programme", mais là encore, c'est à peu près comment vous écrivez des "programmes" dans Mathematica, si ce concept est même valable là-bas. Appelez ça comme

f["my secret", "fully/qualified/cat.png"]

Il renverra une expression d'image réelle (car c'est le moyen le plus naturel de renvoyer une image dans Mathematica), donc si vous voulez un fichier, vous devez l'exporter:

Export["output-cat.png", f["my secret", "input-cat.png"]]

Voici l'exemple requis:

entrez la description de l'image ici

J'adorerais vous montrer le message décodé ici, mais il ne convient pas ... alors faites-le passer par le décodeur de l'OP. ;)

Btw, je pourrais le faire fonctionner avec des secrets UTF-8 pour seulement 7 octets (changer ToCharacterCode@#en #~ToCharacterCode~"utf8").

Code non golfé:

f[secret_, filename_] := (
  bits = Join @@ IntegerDigits[ToCharacterCode[secret], 2, 8];
  bits = Join[d[Length @ bits, 2, 24], bits];
  data = ImageData[Import@#2, "Byte"];
  dims = Dimensions@data;
  data = Flatten[IntegerDigits[data, 2, 8], 2];
  m = n = 1;
  While[m <= Length @ bits,
    data[[n++, -1]] = bits[[m++]]
  ];
  Image[ArrayReshape[FromDigits[#, 2] & /@ data, dims], "Byte"]
)
Martin Ender
la source
"Je serais ravi de vous montrer le message décodé ici, mais il ne convient pas ... alors faites-le passer par le décodeur de l'OP.;)" - Je l'ai fait et cela me donne "????????? ??? +++++++ ?? +++++++++++++++++++++ ================= ~ === ~ ============ ~ :::: ~~~~~ = [... pour 9773 caractères] "
TessellatingHeckler
1
@TessellatingHeckler, c'est exact. essayez-le dans une police à espacement fixe et sachez qu'il contient de nouvelles lignes de style UNIX (par exemple, essayez dans un terminal ou PowerShell avec une largeur d'au moins 180 caractères, ou si vous l'exécutez en tant que script Web dans votre navigateur, puis voir la source);)
Martin Ender
2
Je vois! Très méta. Aide à ce que je sois également dans la version KiTTY de PuTTY 😸
TessellatingHeckler
5

PHP, 530 octets

<?php function p($i,$j){return str_pad(decbin($i),$j,0,0);}extract(getopt("i:o:t:"));$t=file_get_contents($t);$_=imagecreatefrompng($i);list($w,$h)=getimagesize($i);$b="";for($i=0;$i<strlen($t);)$b.=p(ord($t[$i++]),8);$l=strlen($b);$b=p($l,24).$b;$l+=24;for($i=$x=$y=0;$y<$h;$y++){for(;$x<$w;){$C=imagecolorat($_,$x,$y);$R=($C>>16)&0xff;$G=($C>>8)&0xff;$B=$C&0xff;$i<$l&&$R=$b[$i++]|$R&~1;$i<$l&&$G=$b[$i++]|$G&~1;$i<$l&&$B=$b[$i++]|$B&~1;imagesetpixel($_,$x++,$y,imagecolorallocate($_,$R,$G,$B));if($i>$l){imagepng($_,$o);die;}}}

Courez comme php 25443.php -i<input image> -o<output image> -t<file to hide>.

Et voici un exemple d'image.

http://i.imgur.com/hevnrbm.png

Le code non golfé est masqué dans l'exemple d'image. Testé avec le décodeur OP. Désolé pour l'image de chat pas drôle.

Goûter
la source
1
Veuillez ajouter le code non golfé dans votre réponse.
AL
1
Vous pouvez raccourcir 0xffà 255.
TimWolla
Vous pouvez enregistrer 4 octets si vous assumez des balises courtes: <?function.
nyuszika7h