ColorLevels::LAB ColorLevels::LABColorToLAB( const Magick::ColorRGB &magickLAB) { return (LAB{ magickLAB.red() * 100, (magickLAB.green() * 256) - 100, (magickLAB.blue() * 256) - 100 }); }
/** \brief Checks if the given Pixel is empty (not used). It is empty, if least significant bits are all 0. * \param pixel const Pixel& the pixel that should be checked * \return bool true if pixel is not used until now, else false */ bool SteganoHide::isPixelEmpty(const Pixel &pixel) { Magick::ColorRGB pixelColor = this->steganoImage.pixelColor(pixel.x, pixel.y); const unsigned char redLeastSignificantBit = convert16BitTo8BitRGB(pixelColor.redQuantum()) % 10; const unsigned char greenLeastSignificantBit = convert16BitTo8BitRGB(pixelColor.greenQuantum()) % 10; const unsigned char blueLeastSignificantBit = convert16BitTo8BitRGB(pixelColor.blueQuantum()) % 10; // std::cout << (int)redLeastSignificantBit << ":" << (int)greenLeastSignificantBit << ":" << (int)blueLeastSignificantBit << std::endl; return (redLeastSignificantBit == 5 && greenLeastSignificantBit == 5 && blueLeastSignificantBit == 5); }
Image<Color> imread(const std::string& file) { Magick::Image image(file); const size_t w = image.columns(); const size_t h = image.rows(); Magick::PixelPacket* pixels = image.getPixels(0, 0, w, h); Image<Color> rtn(h, w); for (size_t i = 0; i < w * h; ++i) { Magick::ColorRGB p = Magick::ColorRGB(pixels[i]); rtn(i) = Color(double(p.red()), double(p.green()), double(p.blue())); } return rtn; }
/** \brief Draw a finish pixel at the specified position. The finishpixel has at the least-significant bit of their rgb-value r: 3, g: 0, b: 0 * * \param &pixel const Pixel a pixel-struct describing the pixel-position where the finish-pixel should be set. */ void SteganoHide::drawFinishPixel(const Pixel &pixel) { Magick::ColorRGB pixelColor = this->steganoImage.pixelColor(pixel.x, pixel.y); RGB pixelRGB(convert16BitTo8BitRGB(pixelColor.redQuantum()), convert16BitTo8BitRGB(pixelColor.greenQuantum()), convert16BitTo8BitRGB(pixelColor.blueQuantum())); pixelRGB.red = (pixelRGB.red - (pixelRGB.red % 10)) + 3; pixelRGB.green = pixelRGB.green - (pixelRGB.green % 10); pixelRGB.blue = pixelRGB.blue - (pixelRGB.blue % 10); this->steganoImage.pixelColor(pixel.x, pixel.y, Magick::ColorRGB(pixelRGB.toString())); }
static void compare_colors(const Magick::ColorRGB & expected, const Magick::ColorRGB & observed, bool expected_has_alpha, bool observed_has_alpha ) { ASSERT_NEAR(expected.red(), observed.red(), 1.1f/(0x1<<8)); ASSERT_NEAR(expected.green(), observed.green(), 1.1f/(0x1<<8)); ASSERT_NEAR(expected.blue(), observed.blue(), 1.1f/(0x1<<8)); if (expected_has_alpha) { ASSERT_NEAR(expected.alpha(), observed.alpha(), 1.1f / (0x1 << 8)); } else if (observed_has_alpha) { ASSERT_NEAR(1.0f, observed.alpha(), 1.1f /(0x1 << 8)); } }
//Konverterer Magick::Image til QImage QImage* image_wrapper::to_QImage(Magick::Image& image) { qimg_ptr_helper = new QImage(image.columns(), image.rows(), QImage::Format_RGB32); const Magick::PixelPacket *pixels; Magick::ColorRGB rgb; for (int y = 0; y < qimg_ptr_helper->height(); y++) { pixels = image.getConstPixels(0, y, qimg_ptr_helper->width(), 1); for (int x = 0; x < qimg_ptr_helper->width(); x++) { rgb = (*(pixels + x)); qimg_ptr_helper->setPixel(x, y, QColor((int) (255 * rgb.red()) , (int) (255 * rgb.green()) , (int) (255 * rgb.blue())).rgb()); } } return qimg_ptr_helper; }
yuv_image magick_wrapper::xyuv_to_yuv_image_444(const xyuv::conversion_matrix &conversion_matrix) const { // Analyse the conversion matrix to discover if any plane is not present. bool has[3] = {false, false, false}; for (uint32_t i = 0; i < 9; i++) { has[i / 3] = has[i / 3] || (conversion_matrix.rgb_to_yuv[i] != static_cast<pixel_quantum>(0)); } bool has_a = image.matte(); yuv_image image_out = create_yuv_image_444( static_cast<uint32_t>(image.columns()), static_cast<uint32_t>(image.rows()), has[channel::Y], has[channel::U], has[channel::V], has_a); // Create pixels with default values. // Note that this hides the lack of planes. rgb_color rgb; yuv_color yuv; const Magick::PixelPacket *pixels = image.getConstPixels(0, 0, image.columns(), image.rows()); for (uint32_t y = 0; y < image_out.image_h; y++) { for (uint32_t x = 0; x < image_out.image_w; x++) { // Fetch color from ImageMagick const Magick::ColorRGB magick_rgb = Magick::Color(pixels[y * image.columns() + x]); rgb.r = static_cast<float>(magick_rgb.red()); rgb.g = static_cast<float>(magick_rgb.green()); rgb.b = static_cast<float>(magick_rgb.blue()); // Imagemagick stores alpha as 0.0 = opaque rgb.a = static_cast<float>(1.0-magick_rgb.alpha()); // Convert to yuv. to_yuv(&yuv, rgb, conversion_matrix); // Assign pixel set_yuv_color(image_out, yuv, x, y); } } // No syncing needed. return image_out; }
/** \brief Normalize the loaded image.(set all least significant bits of all RGB-Values to 0). * This change takes effect only while editing the picture. In the exported image, all pixel have their old value (except the edited ones). * This is needed to differentiate between edited and not edited pixel. */ void SteganoHide::normalizeImage() { for(unsigned int xValue = 0; xValue < this->xResolution; xValue++) { for(unsigned int yValue = 0; yValue < this->yResolution; yValue++) { Magick::ColorRGB curPixelColor = this->steganoImage.pixelColor(xValue, yValue); RGB curPixelRGB(convert16BitTo8BitRGB(curPixelColor.redQuantum()), convert16BitTo8BitRGB(curPixelColor.greenQuantum()), convert16BitTo8BitRGB(curPixelColor.blueQuantum())); RGB curPixelRoundedRGB(curPixelRGB.red - (curPixelRGB.red % 10) + 5, curPixelRGB.green - (curPixelRGB.green % 10) + 5, curPixelRGB.blue - (curPixelRGB.blue % 10) + 5); this->steganoImage.pixelColor(xValue, yValue, Magick::ColorRGB(curPixelRoundedRGB.toString())); } this->doneBytes += this->yResolution; } }
//Konverterer et QImage til Magick::Image void image_wrapper::to_Image(QImage& qimage) { img_ptr_current = new Magick::Image(Magick::Geometry(qimage.width(), qimage.height()), Magick::ColorRGB(0.5, 0.2, 0.3)); double scale = 1 / 256.0; img_ptr_current->modifyImage(); Magick::PixelPacket *pixels; Magick::ColorRGB mgc; for (int y = 0; y < qimage.height(); y++) { pixels = img_ptr_current->setPixels(0, y, img_ptr_current->columns(), 1); for (int x = 0; x < qimage.width(); x++) { QColor pix = qimage.pixel(x, y); mgc.red(scale *pix.red()); mgc.green(scale *pix.green()); mgc.blue(scale *pix.blue()); *pixels++ = mgc; } img_ptr_current->syncPixels(); } }
// TODO: handle wrong input bool SteganoHide::hideNumberInMagickColorRGB(const unsigned short &smallNumber, Magick::ColorRGB &color) { RGB pixelRGB(convert16BitTo8BitRGB(color.redQuantum()), convert16BitTo8BitRGB(color.greenQuantum()), convert16BitTo8BitRGB(color.blueQuantum())); RGB roundedRGB; roundedRGB.red = pixelRGB.red - (pixelRGB.red % 10); roundedRGB.green = pixelRGB.green - (pixelRGB.green % 10); roundedRGB.blue = pixelRGB.blue - (pixelRGB.blue % 10); unsigned char mostSignificantBit = std::floor(smallNumber / 100); unsigned char middleSigificantBit = std::floor(smallNumber % 100 / 10); unsigned char leastSignificantBit = smallNumber % 10; /* if(mostSignificantBit > 5 || middleSigificantBit > 5 || leastSignificantBit > 5) { throw hideNumber2Big; }*/ // there was an overflow: eg. roundedRGB.blue = 250 and leastSignificantLetterBit = 8 => 258 => 2 // This way to check overflow is possible, because *SignificantLetterBit >= 0 if(roundedRGB.green + middleSigificantBit > 255) { return false; } if(roundedRGB.blue + leastSignificantBit > 255) { return false; } // std::cout << (int)roundedRGB.blue << "+" << (int)leastSignificantBit << "="; roundedRGB.red += mostSignificantBit; roundedRGB.green += middleSigificantBit; roundedRGB.blue += leastSignificantBit; //std::cout << (int)roundedRGB.blue << std::endl; color = Magick::ColorRGB(roundedRGB.toString()); return true; }
// TODO: shorten this method and outsource some inner methods and add header comment std::stringstream SteganoExpose::unhidePhrase(const std::string &password) { if(!this->steganoImage.isValid()) { throw SteganoException::UnhideFileNotSpecified(); } this->exposeFinished = false; Chunk hiddenChunk("vaPh"); std::cout << this->steganoImage.baseFilename() << std::endl; PrivateChunk privChunk(this->steganoImage.baseFilename(), "./omg.png"); char *chunkData = new char[privChunk.getChunkSize(hiddenChunk)]; privChunk.readChunk(hiddenChunk, chunkData); commentReaderStream.str(chunkData); calculateNextShift(); Pixel curPixel(0, 0); std::stringstream returnPhrase; for(unsigned int runCounter = 0; ; runCounter++) { curPixel.x = 0; curPixel.y = 0; int_least64_t steps = quadraticSondation(runCounter, this->xResolution * this->yResolution); // std::cout << "Push by" << steps << std::endl; pushPixelBy(curPixel, steps); // std::cout << curPixel.x << ":" << curPixel.y << std::endl; if(runCounter == shiftIndex) { pushPixelBy(curPixel, shiftAmount); calculateNextShift(); } // std::cout << curPixel.x << "::" << curPixel.y << std::endl; Magick::ColorRGB curPixelColor = this->steganoImage.pixelColor(curPixel.x, curPixel.y); unsigned char redLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.redQuantum()) % 10; unsigned char greenLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.greenQuantum()) % 10; unsigned char blueLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.blueQuantum()) % 10; // there was an overflow at the pixel color. we need to handle it special. unsigned char decryptedChar = 0; if(redLeastSignificantBit == 4) { decryptedChar += greenLeastSignificantBit * 10; decryptedChar += blueLeastSignificantBit; incrementPixel(curPixel); curPixelColor = this->steganoImage.pixelColor(curPixel.x, curPixel.y); redLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.redQuantum()) % 10; greenLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.greenQuantum()) % 10; blueLeastSignificantBit = convert16BitTo8BitRGB(curPixelColor.blueQuantum()) % 10; decryptedChar += redLeastSignificantBit * 100; decryptedChar += greenLeastSignificantBit * 10; decryptedChar += blueLeastSignificantBit; } else { decryptedChar = redLeastSignificantBit * 100; decryptedChar += greenLeastSignificantBit * 10; decryptedChar += blueLeastSignificantBit; } if(isFinishPixel(curPixel)) { break; } returnPhrase << decryptedChar; } this->exposeFinished = true; return returnPhrase; }