Tried to implement a bpcs-steganography, and one of the first problem i faced was bit-plane decomposition.
I've created a method (GetBitPlaneRed) (only for Red yet, but it seems the same for other colors) which creates a red-and-white bitmap based on the original bitmap and the index of bit plane (from 1 to 8).
private static int GetBit(byte b, int bitIndex)
{
return (b >> bitIndex) & 0x01;
}
private static Bitmap GetBitPlaneRed(Bitmap bitmap, int bitPlaneIndex)
{
Bitmap newBitmap = new Bitmap(bitmap.Width, bitmap.Height);
for (int i = 0; i < bitmap.Width; i++)
{
for (int j = 0; j < bitmap.Height; j++)
{
Color currColor = bitmap.GetPixel(i, j);
int bit = GetBit(currColor.R, bitPlaneIndex);
Color newColor = Color.FromArgb(255, 255 * bit, 255 * bit);
newBitmap.SetPixel(i, j, newColor);
}
}
return newBitmap;
}
Seems, it works allright for the MSB (most significant bit), but for other bit planes it's not so correct. Here are some result pictures that i've got to comparison with the right ones.
[EDIT] The "right results" are from a scientific article about BPCS steganography written by Eiji Kawaguchi, so i trust that source. Also, it seems that the mistake is in the way i save my bit-plane images, so i've added some peace of code here where i save my bit-plane images.
Added an original image as well.
private static void SaveBitPlanes()
{
string filePath = "monalisa.jpg";
string ext = System.IO.Path.GetExtension(filePath);
Bitmap bitmap = new Bitmap(filePath);
ImageFormat imageFormat = bitmap.RawFormat;
for (int i = 0; i < 8; i++)
{
Bitmap newBitmap = GetBitPlaneRed(bitmap, i);
newBitmap.Save("bitPlaneRed" + i + ext, imageFormat);
}
}
Original image:
My result of MSB plane:
My result of bit plane #3:
My result of bit plane #7:
Right results:
I would appreciate any help or advice.
There are all important stuff I figured out during existance of this question.
All code I wrote in main post is correct.
Difference in images is not connected with jpeg compression or any
other compression. It works for any file format.
The reason why my results were not similar to those results in an
article is that I used a low-quality image as a source. After I tried
some other high quality images, results look nice and pretty
detailed.
Thank everybody for helping me in figuring out all these points!
Related
I simply want to convert a previously loaded BMP-File into a byte[][]. It works pretty well for my own testing images (just some black spots on white background) which are all in the 8 bits per pixel format.
Now I tried the same code for some bitmaps somebody gave me ( also black squares, rectangles on white background) but it's not working as I expected it:
I expected a white pixel to be represented by a value of 255 (and black just by 0 ) in the resulting array, but i found different values there. In one case pixels that are supposed to be white end up with a value of 1 in the array.
Again, all these files are of 8 bit color depth.
Also, I noticed, when I open the Images in paint, save them again as a 256 color bitmap, then it works.
So my questions are:
What is causing this problem ? (Do color palettes maybe play a role ?)
And how can I make it work ?
Here's my amateurish code:
public byte[][] ConvertImageToArray (BitmapSource Image)
{
byte[][] Result = null;
if (Image != null)
{
int Index = 0;
int size = Image.PixelWidth * Image.PixelHeight * Image.Format.BitsPerPixel/8;
byte[] RawImg = new byte[size];
BitmapPalette test = Image.Palette;
int stride = (Image.PixelWidth * Image.Format.BitsPerPixel) / 8;
Image.CopyPixels(RawImg, stride, 0);
Result = new byte[Image.PixelHeight][];
int Width = Image.PixelWidth;
for (int i = 0; i < Result.Length; i++)
{
Result[i] = new byte[Width];
for (int k = 0; k < Result[i].Length; k++)
{
Result[i][k] = RawImg[Index++];
}
}
}
return Result;
}
I don't know how to tag this question, please edit if possible.
The job: Create an application which can auto-crop black borders in images in batch runs. Images vary in quality from 100-300dpi, 1bpp-24bpp and a batch can vary from 10 - 10 000 images.
The plan: Convert image to 1bpp (bitonal, black/white, if it isn't already) and after "cleaning up" white spots/dirt/noise find where the black ends and the white begins, these are the new coords for the image crop, apply them to a clone of the original image. Delete old image, save new one.
The progress: All of the above is done, and works, but...
The problem: When converting to 1bpp I have no control of a "threshold" value. I need this. A lot of dark images get cropped too much.
The tries: I've tried
Bitmap imgBitonal = imgOriginal.Clone(new Rectangle(0, 0, b.Width, b.Height), PixelFormat.Format1bppIndexed)
And also this. Both of which work, but none seem to give me the possibility to manually set a threshold value. I need for the user to be able to set this value, amongst others, and use my "preview" function before running the batch so as to see if the settings are any good.
The cry: I'm at a loss here. I don't now what to do or how to do it. Please help a fellow coder out. Point me in a direction, show me where in the code found in the link a threshold value is found (I haven't found one, or don't know where to look) or just give me some code that works. Any help is appreciated.
Try this, from very fast 1bpp convert:
Duplicate from here Convert 24bpp Bitmap to 1bpp
private static unsafe void Convert(Bitmap src, Bitmap conv)
{
// Lock source and destination in memory for unsafe access
var bmbo = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadOnly,
src.PixelFormat);
var bmdn = conv.LockBits(new Rectangle(0, 0, conv.Width, conv.Height), ImageLockMode.ReadWrite,
conv.PixelFormat);
var srcScan0 = bmbo.Scan0;
var convScan0 = bmdn.Scan0;
var srcStride = bmbo.Stride;
var convStride = bmdn.Stride;
byte* sourcePixels = (byte*)(void*)srcScan0;
byte* destPixels = (byte*)(void*)convScan0;
var srcLineIdx = 0;
var convLineIdx = 0;
var hmax = src.Height-1;
var wmax = src.Width-1;
for (int y = 0; y < hmax; y++)
{
// find indexes for source/destination lines
// use addition, not multiplication?
srcLineIdx += srcStride;
convLineIdx += convStride;
var srcIdx = srcLineIdx;
for (int x = 0; x < wmax; x++)
{
// index for source pixel (32bbp, rgba format)
srcIdx += 4;
//var r = pixel[2];
//var g = pixel[1];
//var b = pixel[0];
// could just check directly?
//if (Color.FromArgb(r,g,b).GetBrightness() > 0.01f)
if (!(sourcePixels[srcIdx] == 0 && sourcePixels[srcIdx + 1] == 0 && sourcePixels[srcIdx + 2] == 0))
{
// destination byte for pixel (1bpp, ie 8pixels per byte)
var idx = convLineIdx + (x >> 3);
// mask out pixel bit in destination byte
destPixels[idx] |= (byte)(0x80 >> (x & 0x7));
}
}
}
src.UnlockBits(bmbo);
conv.UnlockBits(bmdn);
}
I have an image that looks like this:
and I want to find the edges of the dark part so like this (the red lines are what I am looking for):
I have tried a few approaches and none have worked so I am hoping there is an emgu guru out there willing to help me...
Approach 1
Convert the image to grayscale
Remove noise and invert
Remove anything that is not really bright
Get the canny and the polygons
Code for this (I know that I should be disposing of things properly but I am keeping the code short):
var orig = new Image<Bgr, byte>(inFile);
var contours = orig
.Convert<Gray, byte>()
.PyrDown()
.PyrUp()
.Not()
.InRange(new Gray(190), new Gray(255))
.Canny(new Gray(190), new Gray(255))
.FindContours(CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE,
RETR_TYPE.CV_RETR_TREE);
var output = new Image<Gray, byte>(orig.Size);
for (; contours != null; contours = contours.HNext)
{
var poly = contours.ApproxPoly(contours.Perimeter*0.05,
contours.Storage);
output.Draw(poly, new Gray(255), 1);
}
output.Save(outFile);
This is the result:
Approach 2
Convert the image to grayscale
Remove noise and invert
Remove anything that is not really bright
Get the canny and then lines
Code for this:
var orig = new Image<Bgr, byte>(inFile);
var linesegs = orig
.Convert<Gray, byte>()
.PyrDown()
.PyrUp()
.Not()
.InRange(new Gray(190), new Gray(255))
.Canny(new Gray(190), new Gray(255))
.HoughLinesBinary(
1,
Math.PI/45.0,
20,
30,
10
)[0];
var output = new Image<Gray, byte>(orig.Size);
foreach (var l in linesegs)
{
output.Draw(l, new Gray(255), 1);
}
output.Save(outFile);
This is the result:
Notes
I have tried adjusting all the parameters on those two approaches and adding smoothing but I can never get the simple edges that I need because, I suppose, the darker region is not a solid colour.
I have also tried dilating and eroding but the parameters I have to put in for those are so high to get a single colour that I end up including some of the grey stuff on the right and lose accuracy.
Yes, it's possible, and here is how you could do it:
Change the contrast of the image to make the lighter part disappear:
Then, convert it to HSV to perform a threshold operation on the Saturation channel:
And execute erode & dilate operations to get rid of the noises:
At this point you'll have the result you were looking for. For testing purposes, at the end I execute the bounding box technique to show how to detect the beggining and the end of the area of interest:
I didn't have the time to tweak the parameters and make a perfect detection, but I'm sure you can figure it out. This answer provides a roadmap for achieving that!
This is the C++ code I came up with, I trust you are capable of converting it to C#:
#include <cv.h>
#include <highgui.h>
int main(int argc, char* argv[])
{
cv::Mat image = cv::imread(argv[1]);
cv::Mat new_image = cv::Mat::zeros(image.size(), image.type());
/* Change contrast: new_image(i,j) = alpha*image(i,j) + beta */
double alpha = 1.8; // [1.0-3.0]
int beta = 100; // [0-100]
for (int y = 0; y < image.rows; y++)
{
for (int x = 0; x < image.cols; x++)
{
for (int c = 0; c < 3; c++)
{
new_image.at<cv::Vec3b>(y,x)[c] =
cv::saturate_cast<uchar>(alpha * (image.at<cv::Vec3b>(y,x)[c]) + beta);
}
}
}
cv::imshow("contrast", new_image);
/* Convert RGB Mat into HSV color space */
cv::Mat hsv;
cv::cvtColor(new_image, hsv, CV_BGR2HSV);
std::vector<cv::Mat> v;
cv::split(hsv,v);
// Perform threshold on the S channel of hSv
int thres = 15;
cv::threshold(v[1], v[1], thres, 255, cv::THRESH_BINARY_INV);
cv::imshow("saturation", v[1]);
/* Erode & Dilate */
int erosion_size = 6;
cv::Mat element = cv::getStructuringElement(cv::MORPH_CROSS,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(erosion_size, erosion_size) );
cv::erode(v[1], v[1], element);
cv::dilate(v[1], v[1], element);
cv::imshow("binary", v[1]);
/* Bounding box */
// Invert colors
cv::bitwise_not(v[1], v[1]);
// Store the set of points in the image before assembling the bounding box
std::vector<cv::Point> points;
cv::Mat_<uchar>::iterator it = v[1].begin<uchar>();
cv::Mat_<uchar>::iterator end = v[1].end<uchar>();
for (; it != end; ++it)
{
if (*it) points.push_back(it.pos());
}
// Compute minimal bounding box
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));
// Draw bounding box in the original image (debug purposes)
cv::Point2f vertices[4];
box.points(vertices);
for (int i = 0; i < 4; ++i)
{
cv::line(image, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0), 2, CV_AA);
}
cv::imshow("box", image);
cvWaitKey(0);
return 0;
}
Update: I am attempting to pull a little clutter out of this post and sum it up more concisely. Please see the original edit if needed.
I am currently attempting to trace a series of single colored blobs on a Bitmap canvas.
e.g. An example of the bitmap I am attempting to trace would look like the following:
alt text http://www.refuctored.com/polygons.bmp
After successfully tracing the outlines of the 3 blobs on the image, I would have a class that held the color of a blob tied to a point list representing the outline of the blob (not all the pixels inside of the blobs).
The problem I am running into is logic in instances where a neighboring pixel has no surrounding pixels other than the previous pixel.
e.g The top example would trace fine, but the second would fail because the pixel has no where to go since the previous pixels have already been used.
alt text http://www.refuctored.com/error.jpg
I am tracing left-to-right, top-to-bottom, favoring diagonal angles over right angles. I must be able to redraw an exact copy of the region based off the data I extract, so the pixels in the list must be in the right order for the copy to work.
Thus far, my attempt has been riddled with failure, and a couple days of pulling my hair out trying to rewrite the algorithms a little different each time to solve the issue. Thus far I have been unsuccessful. Has anyone else had a similar issue like mine who has a good algorithm to find the edges?
One simple trick to avoiding these cul-de-sacs is to double the size of the image you want to trace using a nearest neighbor scaling algorithm before tracing it. Like that you will never get single strips.
The alternative is to use a marching squares algorithm - but it seems to still have one or two cases where it fails: http://www.sakri.net/blog/2009/05/28/detecting-edge-pixels-with-marching-squares-algorithm/
Have you looked at blob detection algorithms? For example, http://opencv.willowgarage.com/wiki/cvBlobsLib if you can integrate OpenCV into your application. Coupled with thresholding to create binary images for each color (or color range) in your image, you could easily find the blobs that are the same color. Repeat for each color in the image, and you have a list of blobs sorted by color.
If you cannot use OpenCV directly, perhaps the paper referenced by that library ("A linear-time component labeling algorithm using contour tracing technique", F.Chang et al.) would provide a good method for finding blobs.
Rather than using recursion, use a stack.
Pseudo-code:
Add initial pixel to polygon
Add initial pixel to stack
while(stack is not empty) {
pop pixel off the stack
foreach (neighbor n of popped pixel) {
if (n is close enough in color to initial pixel) {
Add n to polygon
Add n to stack
}
}
}
This will use a lot less memory than the same solution using recursion.
Just send your 'Image' to BuildPixelArray function and then call the FindRegions.
After that the 'colors' variable will be holding your colors list and pixel coordinates in every list member.
I've copied the source from one of my projects, there may be some undefined variables or syntax erors.
public class ImageProcessing{
private int[,] pixelArray;
private int imageWidth;
private int imageHeight;
List<MyColor> colors;
public void BuildPixelArray(ref Image myImage)
{
imageHeight = myImage.Height;
imageWidth = myImage.Width;
pixelArray = new int[imageWidth, imageHeight];
Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height);
Bitmap temp = new Bitmap(myImage);
BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int remain = bmpData.Stride - bmpData.Width * 3;
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
for (int j = 15; j < bmpData.Height; j++)
{
for (int i = 0; i < bmpData.Width; i++)
{
pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256;
ptr += 3;
}
ptr += remain;
}
}
temp.UnlockBits(bmpData);
}
public void FindRegions()
{
colors = new List<MyColor>();
for (int i = 0; i < imageWidth; i++)
{
for (int j = 0; j < imageHeight; j++)
{
int tmpColorValue = pixelArray[i, j];
MyColor tmp = new MyColor(tmpColorValue);
if (colors.Contains(tmp))
{
MyColor tmpColor = (from p in colors
where p.colorValue == tmpColorValue
select p).First();
tmpColor.pointList.Add(new MyPoint(i, j));
}
else
{
tmp.pointList.Add(new MyPoint(i, j));
colors.Add(tmp);
}
}
}
}
}
public class MyColor : IEquatable<MyColor>
{
public int colorValue { get; set; }
public List<MyPoint> pointList = new List<MyPoint>();
public MyColor(int _colorValue)
{
colorValue = _colorValue;
}
public bool Equals(MyColor other)
{
if (this.colorValue == other.colorValue)
{
return true;
}
return false;
}
}
public class MyPoint
{
public int xCoord { get; set; }
public int yCoord { get; set; }
public MyPoint(int _xCoord, int _yCoord)
{
xCoord = _xCoord;
yCoord = _yCoord;
}
}
If you're getting a stack overflow I would guess that you're not excluding already-checked pixels. The first check on visiting a square should be whether you've been here before.
Also, I was working on a related problem not too long ago and I came up with a different approach that uses a lot less memory:
A queue:
AddPointToQueue(x, y);
repeat
x, y = HeadItem;
AddMaybe(x - 1, y); x + 1, y; x, y - 1; x, y + 1;
until QueueIsEmpty;
AddMaybe(x, y):
if Visited[x, y] return;
Visited[x, y] = true;
AddPointToQueue(x, y);
The point of this approach is that you end up with your queue basically holding a line wrapped around the mapped area. This limits memory usage better than a stack can.
If relevant it also can be trivially modified to yield the travel distance to any square.
Try using AForge.net. I would go for Filter by colors, Threshold and then you could do some Morphology to decrement the black/White zones to lose contact between the objects. Then you could go for the Blobs.
I have an array of ushort pixel data that I need to save as a jpeg file. From what I've found I can do this by using
Image.Save(path, ImageFormat.Jpeg);
but I don't know how to get the ushort data into the Image object. I've found ways to do this with byte arrays but not ushort.
I've spent far to much time trying to figure this out so now I ask the mighty StackOverflow, How do I this?
Edit:
Sorry, the ushorts are 16 bit grayscale values.
I think you have to actually create a Bitmap, draw the pixels onto it and save it afterwards.
Something like this:
var bitmap = new Bitmap(sizeX, sizeY, Imaging.PixelFormat.Format16bppGrayScale)
for (y = 0; ...)
for (x = 0; ...)
{
bitmap.SetPixel(x, y, color information from ushort array);
}
bitmap.Save("filename.jpg", ImageFormat.Jpeg);
Note that I don't know how to get a 16 bit greyscale color information into the Color struct.
This is a complete working example based on the accepted answer:
public static void SaveJpg(string fileName,int sizeX,int sizeY,ushort [] imData)
{
var bitmap = new Bitmap(sizeX, sizeY, PixelFormat.Format48bppRgb);
int count = 0;
for (int y = 0; y < sizeY; y++)
{
for (int x = 0; x < sizeX; x++)
{
bitmap.SetPixel(x, y, Color.FromArgb(imData[count], imData[count], imData[count]));
count++;
}
}
bitmap.Save(fileName, ImageFormat.Jpeg);
}
I believe you want to use the Bitmap class, which inherits from Image. This MSDN reference may assist.