如何在 Scala 中替换缓冲图像中的最低有效位
How to Replace Least Significant Bit In Buffered Image in Scala
我正在尝试替换 Scala 中缓冲图像像素中每个红色、绿色和蓝色值中的两个最低有效位。但是当我尝试更改最低有效位时,更改不成立。当更改两个最高有效位时,我已经得到了这个工作,但它不会使用最低有效位。这是完成大部分繁重工作的方法
def hideDataInImage(dataToHide: String, imageToHideIn: BufferedImage): Option[BufferedImage] = {
// Check that the binary data is short enough to encode in the image. Assumeing that we can hide
// one 6-bit element in one pixel the size of the array must be less than the area of the array
if(dataToHide.length > (imageToHideIn.getHeight() * imageToHideIn.getWidth())) {
return None
}
val imageToHideInCopy = deepCopyBufferedImage(imageToHideIn)
// Turn the string of data into a string of binary numbers
val binaryToHide = stringToBinaryArray(dataToHide)
// Loop through the binary data and hide it in each pixel
for(i <- binaryToHide.indices) {
// Get the x and y data for the pixel
val y = i / imageToHideInCopy.getWidth()
val x = i % imageToHideInCopy.getHeight()
val newColor = hideDataInColor(imageToHideInCopy.getRGB(x, y), binaryToHide(i))
imageToHideInCopy.setRGB(x, y, newColor.getRGB)
}
// Return some image to hide in if success
Some(imageToHideInCopy)
}
其余代码可以在我的Github.
上看到
编辑:这是我的其余代码
def hideDataInColor(pixelValue: Int, binaryDataToHide: String): Color = {
// Get the red green blue and alpha values from the pixel value
// 3 2 1 0
// bitpos 10987654 32109876 54321098 76543210
// ------ +--------+--------+--------+--------+
// bits |AAAAAAAA|RRRRRRRR|GGGGGGGG|BBBBBBBB|
val blueValue = byteToPaddedBinary(pixelValue & 0xff, 8)
val greenValue = byteToPaddedBinary((pixelValue & 0xff00) >> 8, 8)
val redValue = byteToPaddedBinary((pixelValue & 0xff0000) >> 16, 8)
val alphaValue = byteToPaddedBinary((pixelValue & 0xff000000) >>> 24, 8)
// Split the binarydata into three parts
val splitData = binaryDataToHide.split("(?<=\G.{" + binaryDataToHide.length / 3 + "})")
// Get the modified red blue green values
val newRed = hideBitsInBinaryString(redValue, splitData(0))
val newGreen = hideBitsInBinaryString(greenValue, splitData(1))
val newBlue = hideBitsInBinaryString(blueValue, splitData(2))
// Convert the binary number to an integer and return it
new Color(Integer.parseInt(newRed, 2), Integer.parseInt(newGreen, 2),
Integer.parseInt(newBlue, 2), Integer.parseInt(alphaValue, 2))
}
def hideBitsInBinaryString(binaryString: String, bitsToHide: String): String = {
// Create a string builder from the binary string
val binaryStringStringBuilder = new StringBuilder(binaryString)
// Loop through the bits to hide
for(i <- 0 until bitsToHide.length) {
// replace to chars at the end of the string builder
binaryStringStringBuilder.setCharAt(binaryString.length - bitsToHide.length + i, bitsToHide(i))
// binaryStringStringBuilder.setCharAt(i, bitsToHide(i))
}
// Return the binary string builder to string
binaryStringStringBuilder.toString()
}
您的代码中不应有 x
和 y
坐标。内存中像素的布局完全无关紧要,反正你也不关心原始图像。您应该直接使用底层线性 pixels
数组,并按索引寻址像素。
(x, y)
-坐标非常有害,它们至少会导致您的代码中出现一个错误。这里是错误的:
val y = i / imageToHideInCopy.getWidth()
val x = i % imageToHideInCopy.getHeight()
您应该使用 getWidth()
getWidth()
或 getHeight()
getHeight()
,但不要混合使用两者。
更小的演示以了解出了什么问题。假设你的图像是 3x5:
scala> val w = 5
w: Int = 5
scala> val h = 3
h: Int = 3
这就是你正在做的事情:
scala> val coords = (0 until 15).map{ i => (i / w, i % h) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2), (0,0), (0,1),
(1,2), (1,0), (1,1), (1,2), (1,0),
(2,1), (2,2), (2,0), (2,1), (2,2)
)
请注意,例如(0,0)
和 (0,1)
出现了两次。信息被写入这些像素两次。
您应该这样做:
scala> val coords = (0 until 15).map{ i => (i / w, i % w) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2), (0,3), (0,4),
(1,0), (1,1), (1,2), (1,3), (1,4),
(2,0), (2,1), (2,2), (2,3), (2,4)
)
或者这个:
scala> val coords = (0 until 15).map{ i => (i / h, i % h) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2),
(1,0), (1,1), (1,2),
(2,0), (2,1), (2,2),
(3,0), (3,1), (3,2),
(4,0), (4,1), (4,2)
)
如您所见,无论是 row-major 还是 column-major 都完全无关紧要。所以你其实可以扔掉这个容易出错的代码,直接使用pixels
。
那个位操作代码...好吧,那不是位操作代码。字符串和正则表达式不应靠近此代码。只需阅读十个关于位操作和颜色的随机 Whosebug 答案,例如 something like this or this,或查找一些教程,只是为了了解代码应该是什么样子。
我正在尝试替换 Scala 中缓冲图像像素中每个红色、绿色和蓝色值中的两个最低有效位。但是当我尝试更改最低有效位时,更改不成立。当更改两个最高有效位时,我已经得到了这个工作,但它不会使用最低有效位。这是完成大部分繁重工作的方法
def hideDataInImage(dataToHide: String, imageToHideIn: BufferedImage): Option[BufferedImage] = {
// Check that the binary data is short enough to encode in the image. Assumeing that we can hide
// one 6-bit element in one pixel the size of the array must be less than the area of the array
if(dataToHide.length > (imageToHideIn.getHeight() * imageToHideIn.getWidth())) {
return None
}
val imageToHideInCopy = deepCopyBufferedImage(imageToHideIn)
// Turn the string of data into a string of binary numbers
val binaryToHide = stringToBinaryArray(dataToHide)
// Loop through the binary data and hide it in each pixel
for(i <- binaryToHide.indices) {
// Get the x and y data for the pixel
val y = i / imageToHideInCopy.getWidth()
val x = i % imageToHideInCopy.getHeight()
val newColor = hideDataInColor(imageToHideInCopy.getRGB(x, y), binaryToHide(i))
imageToHideInCopy.setRGB(x, y, newColor.getRGB)
}
// Return some image to hide in if success
Some(imageToHideInCopy)
}
其余代码可以在我的Github.
上看到编辑:这是我的其余代码
def hideDataInColor(pixelValue: Int, binaryDataToHide: String): Color = {
// Get the red green blue and alpha values from the pixel value
// 3 2 1 0
// bitpos 10987654 32109876 54321098 76543210
// ------ +--------+--------+--------+--------+
// bits |AAAAAAAA|RRRRRRRR|GGGGGGGG|BBBBBBBB|
val blueValue = byteToPaddedBinary(pixelValue & 0xff, 8)
val greenValue = byteToPaddedBinary((pixelValue & 0xff00) >> 8, 8)
val redValue = byteToPaddedBinary((pixelValue & 0xff0000) >> 16, 8)
val alphaValue = byteToPaddedBinary((pixelValue & 0xff000000) >>> 24, 8)
// Split the binarydata into three parts
val splitData = binaryDataToHide.split("(?<=\G.{" + binaryDataToHide.length / 3 + "})")
// Get the modified red blue green values
val newRed = hideBitsInBinaryString(redValue, splitData(0))
val newGreen = hideBitsInBinaryString(greenValue, splitData(1))
val newBlue = hideBitsInBinaryString(blueValue, splitData(2))
// Convert the binary number to an integer and return it
new Color(Integer.parseInt(newRed, 2), Integer.parseInt(newGreen, 2),
Integer.parseInt(newBlue, 2), Integer.parseInt(alphaValue, 2))
}
def hideBitsInBinaryString(binaryString: String, bitsToHide: String): String = {
// Create a string builder from the binary string
val binaryStringStringBuilder = new StringBuilder(binaryString)
// Loop through the bits to hide
for(i <- 0 until bitsToHide.length) {
// replace to chars at the end of the string builder
binaryStringStringBuilder.setCharAt(binaryString.length - bitsToHide.length + i, bitsToHide(i))
// binaryStringStringBuilder.setCharAt(i, bitsToHide(i))
}
// Return the binary string builder to string
binaryStringStringBuilder.toString()
}
您的代码中不应有 x
和 y
坐标。内存中像素的布局完全无关紧要,反正你也不关心原始图像。您应该直接使用底层线性 pixels
数组,并按索引寻址像素。
(x, y)
-坐标非常有害,它们至少会导致您的代码中出现一个错误。这里是错误的:
val y = i / imageToHideInCopy.getWidth()
val x = i % imageToHideInCopy.getHeight()
您应该使用 getWidth()
getWidth()
或 getHeight()
getHeight()
,但不要混合使用两者。
更小的演示以了解出了什么问题。假设你的图像是 3x5:
scala> val w = 5
w: Int = 5
scala> val h = 3
h: Int = 3
这就是你正在做的事情:
scala> val coords = (0 until 15).map{ i => (i / w, i % h) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2), (0,0), (0,1),
(1,2), (1,0), (1,1), (1,2), (1,0),
(2,1), (2,2), (2,0), (2,1), (2,2)
)
请注意,例如(0,0)
和 (0,1)
出现了两次。信息被写入这些像素两次。
您应该这样做:
scala> val coords = (0 until 15).map{ i => (i / w, i % w) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2), (0,3), (0,4),
(1,0), (1,1), (1,2), (1,3), (1,4),
(2,0), (2,1), (2,2), (2,3), (2,4)
)
或者这个:
scala> val coords = (0 until 15).map{ i => (i / h, i % h) }
coords: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector(
(0,0), (0,1), (0,2),
(1,0), (1,1), (1,2),
(2,0), (2,1), (2,2),
(3,0), (3,1), (3,2),
(4,0), (4,1), (4,2)
)
如您所见,无论是 row-major 还是 column-major 都完全无关紧要。所以你其实可以扔掉这个容易出错的代码,直接使用pixels
。
那个位操作代码...好吧,那不是位操作代码。字符串和正则表达式不应靠近此代码。只需阅读十个关于位操作和颜色的随机 Whosebug 答案,例如 something like this or this,或查找一些教程,只是为了了解代码应该是什么样子。