How do I convert a WriteableBitmap into a BitmapImage object in Silverlight5 ?
So far, here is my attempt in converting a WriteableBitmap into a BitmapImage; however, it doesn't work.
No exception is thrown, the resulting BitmapImage is simply an 'empty' object with no data; both PixelHeight and PixelWidth are zero:
public BitmapImage Convert(WriteableBitmap wb)
{
int[] p = wb.Pixels;
int len = p.Length * 4;
byte[] result = new byte[len];
Buffer.BlockCopy(p, 0, result, 0, len);
MemoryStream ms = new MemoryStream(result);
BitmapImage bi = new BitmapImage();
bi.SetSource(ms);
// bi.PixelHeight and bi.PixelWidth are zero.
return bi; // pretty much an empty image.
}
Cross-post here: http://social.msdn.microsoft.com/Forums/silverlight/en-US/0a0c833b-8e68-4c8d-871a-d0805d84ba16/writeablebitmap-to-bitmapimage?forum=silverlightpivotviewer
To summarize the discussion on your cross-post:
Yes, you have to convert your image to either png or jpg to create a BitmapImage.
The stream the BitmapImage expects is an encoded image file, but the bytes you get from WriteableBitmap is an uncompressed, raw ARGB pixel format.
In order to produce either a png or a jpg you have to use either link against a library that can do this, or use Stegman's one-page png encoder (that doesn't actually compress anything).
Since this was already suggested in the forum you asked but you noticed that the link is broken, I put a public gist on github from the copy I had locally.
Related
I tried to convert a WriteableBitmap to a cv::Mat in a c++/cx Microsoft universial App. But when I try to progress with the created Mat, I get the following error:
This is my Code:
void App1::MainPage::processImage(SoftwareBitmap^ bitmap)
{
WriteableBitmap^ wb = ref new WriteableBitmap(bitmap->PixelWidth, bitmap->PixelHeight);
bitmap->CopyToBuffer(wb->PixelBuffer);
Mat img_image(wb->PixelHeight, wb->PixelWidth, CV_8UC3,(void*)wb->PixelBuffer);
//next step results in error
cvtColor(img_image, img_image, CV_BGR2BGRA);
...
}
So my final question:
How to convert the SoftwareBitmap or the WriteableBitmap to a cv::Mat?
The problem is that PixelBuffer is not a void *, it is an IBuffer^.
To get at the raw data, you can either use the IBufferByteAccess interface if you're comfortable with COM programming, or you can initialize a DataReader with an IBuffer if you'd prefer to stay in WinRT (although this technique will make a copy of the data).
I used the DataReader to solve the problem:
void App1::MainPage::processImage(SoftwareBitmap^ bitmap)
{
WriteableBitmap^ wb = ref new WriteableBitmap(bitmap->PixelWidth, bitmap->PixelHeight);
bitmap->CopyToBuffer(wb->PixelBuffer);
IBuffer^ buffer = wb->PixelBuffer;
auto reader = ::Windows::Storage::Streams::DataReader::FromBuffer(buffer);
BYTE *extracted = new BYTE[buffer->Length];
reader->ReadBytes(Platform::ArrayReference<BYTE>(extracted, buffer->Length));
Mat img_image(wb->PixelHeight, wb->PixelWidth, CV_8UC4, extracted);
cvtColor(img_image, img_image, CV_RGBA2BGRA);
...
}
Thx to Peter Torr for the hint.
I'm using DirectShow to access a video stream, and then using the SampleGrabber filter and interface to get samples from each frame for further image processing. I'm using a callback, so it gets called after each new frame. I've basically just worked from the PlayCap sample application and added a sample filter to the graph.
The problem I'm having is that I'm trying to display the grabbed samples on a different OpenCV window. However, when I try to cast the information in the buffer to an IplImage, I get a garbled mess of pixels. The code for the BufferCB call is below, sans any proper error handling:
STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen)
{
AM_MEDIA_TYPE type;
g_pGrabber->GetConnectedMediaType(&type);
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER *)type.pbFormat;
BITMAPINFO* bmi = (BITMAPINFO *)&pVih->bmiHeader;
BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);
int channels = bmih->biBitCount / 8;
mih->biPlanes = 1;
bmih->biBitCount = 24;
bmih->biCompression = BI_RGB;
IplImage *Image = cvCreateImage(cvSize(bmih->biWidth, bmih->biHeight), IPL_DEPTH_8U, channels);
Image->imageSize = BufferLen;
CopyMemory(Image->imageData, pBuffer, BufferLen);
cvFlip(Image);
//openCV Mat creation
Mat cvMat = Mat(Image, true);
imshow("Display window", cvMat); // Show our image inside it.
waitKey(2);
return S_OK;
}
My question is, am I doing something wrong here that will make the image displayed look like this:
Am I missing header information or something?
The quoted code is a part of the solution. You create here an image object of certain width/height with 8-bit pixel data and unknown channel/component count. Then you copy data from another buffer of unknown format.
The only chance for it to work well is that all unknowns amazingly match without your effort. So you basically need to start with checking what media type is exactly on Sample Grabber's input pin. Then, if it is not what you wanted, you have to update your code respectively. It might also be important what is the downstream connection of the SG, and whether it is connected to video renderer in particular.
I need to put decoded RGBA data (from 32-bit PNG) in cl::Image2D, then (after some processing) write it back to Magick++ image with enqueueReadImage().
However, at the moment I do not see any way to access RGBA data directly in Magick++ image object. Is this possible? If not, what's the best way to get data in RGBA format from Magick++ object?
You can use the Magick::Image::write function
Magick::Image im;
// read image ....
// only for RGBA !!!
size_t im_size = im.columns() * im.rows() * 4;
uint8_t * pixels = new uint8_t[im_size];
im.write(0, 0, im.columns(), im.rows(), "RGBA", ::Magick::CharPixel, pixels);
I need to send an image into a byte array using c++/cli. The image is initially in Iplimage format.
int img_sz1 = img1->width * img1->height * img1->nChannels;
array <Byte>^ hh1 = gcnew array<Byte> (img_sz1);
Marshal::Copy( (IntPtr)img->imageData, hh1, 0, img_sz1 );
and it was working fine.
I added the encoding step to send it as jpeg
CvMat* buf1 = cvEncodeImage(".jpeg", img1, jpeg_params);
img_sz1=buf1->width*buf1->height
Marshal::Copy( (IntPtr)buf1, hh1, 0, img_sz1 );
and now it compiles fine but gives me the error at the marshal:copy line
An unhandled exception of type 'System.AccessViolationException' occurred in
mscorlib.dll. Additional information: Attempted to read or write protected memory.
Any help is very appreciated.
The return of cvEncodeImage is a single-row matrix, containing the encoded image data. What you're copying now is the struct itself, e.g., the width field, the height field, etc. I believe you need to copy from buf1->data instead.
I have a CGImage (core graphics, C/C++). It's grayscale. Well, originally it was B/W, but the CGImage may be RGB. That shouldn't matter. I want to create a CCITT-Group 4 TIFF.
I can create an LZW TIFF (grayscale or color) via creating a destination with the correct dictionary and adding the image in. No problem.
However, there doesn't seem to be an equivalent kCGImagePropertyTIFFCompression value to represent CCITT-4. It should be 4, but that produces uncompressed.
I have a manual CCITT compression routine, so if I can get the binary (1 bit per pixel) data, I'm set. But I can't seem to get 1 BPP data out of a CGImage. I have code that is supposed to put the CGImage into a CGBitmapContext and then give me the data, but it seems to be giving me all black.
I've asked a couple of questions today trying to get at this, but I just figured, lets ask the question I REALLY want answered and see if someone can answer it.
There's GOT to be a way to do this. I've got to be missing something dumb. What is it?
This seems to work and produce not-all-black output. There may be a way to do it that doesn't involve a manual conversion to grayscale first, but at least it works!
static void WriteCCITTTiffWithCGImage_URL_(CGImageRef im, CFURLRef url) {
// produce grayscale image
CGImageRef grayscaleImage;
{
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
CGContextRef bitmapCtx = CGBitmapContextCreate(NULL, CGImageGetWidth(im), CGImageGetHeight(im), 8, 0, colorSpace, kCGImageAlphaNone);
CGContextDrawImage(bitmapCtx, CGRectMake(0,0,CGImageGetWidth(im), CGImageGetHeight(im)), im);
grayscaleImage = CGBitmapContextCreateImage(bitmapCtx);
CFRelease(bitmapCtx);
CFRelease(colorSpace);
}
// generate options for ImageIO. Man this sucks in C.
CFMutableDictionaryRef options = CFDictionaryCreateMutable(kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
{
{
CFMutableDictionaryRef tiffOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
int fourInt = 4;
CFNumberRef fourNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fourInt);
CFDictionarySetValue(tiffOptions, kCGImagePropertyTIFFCompression, fourNumber);
CFRelease(fourNumber);
CFDictionarySetValue(options, kCGImagePropertyTIFFDictionary, tiffOptions);
CFRelease(tiffOptions);
}
{
int oneInt = 1;
CFNumberRef oneNumber = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &oneInt);
CFDictionarySetValue(options, kCGImagePropertyDepth, oneNumber);
CFRelease(oneNumber);
}
}
// write file
CGImageDestinationRef idst = CGImageDestinationCreateWithURL(url, kUTTypeTIFF, 1, NULL);
CGImageDestinationAddImage(idst, grayscaleImage, options);
CGImageDestinationFinalize(idst);
// clean up
CFRelease(idst);
CFRelease(options);
CFRelease(grayscaleImage);
}
Nepheli:tmp ken$ tiffutil -info /tmp/output.tiff
Directory at 0x1200
Image Width: 842 Image Length: 562
Bits/Sample: 1
Sample Format: unsigned integer
Compression Scheme: CCITT Group 4 facsimile encoding
Photometric Interpretation: "min-is-black"
Orientation: row 0 top, col 0 lhs
Samples/Pixel: 1
Number of Strips: 1
Planar Configuration: Not planar
ImageMagick can convert from and to almost any image format. As it is open source you can go and read the source code to find the answer to your question.
You can even use the ImageMagick API in you app if you use C++.
Edit:
If you can get the data from CGImage in any format (and it sounded like you can) you can use ImageMagick to convert it from whatever the format is that you get from CGImage to any other format supported by ImageMagick (your desired TIFF format).
Edit:
Technical Q&A QA1509
Getting the pixel data from a CGImage object states:
On Mac OS X 10.5 or later, a new call has been added that allows you to obtain the actual pixel data from a CGImage object. This call, CGDataProviderCopyData, returns a CFData object that contains the pixel data from the image in question.
Once you have the pixel data you can use ImageMagick to convert it.
NSBitmapImageRep claims to be able to generate a CCITT FAX Group 4 compressed TIFF. So something like this might do the trick (untested):
CFDataRef tiffFaxG4DataForCGImage(CGImageRef cgImage) {
NSBitmapImageRep *imageRep =
[[[NSBitmapImageRep alloc] initWithCGImage:cgImage] autorelease];
NSData *tiffData =
[imageRep TIFFRepresentationUsingCompression:NSTIFFCompressionCCITTFAX4
factor:0.0f];
return (CFDataRef) tiffData;
}
This function should return the data you seek.