Getting PixelData from CGImageRef returns wrong value - c++

Im trying to get the Pixel-Values from a CGImageRef, but somehow it always returns the wrong values.
My Test-Image is a complete red picture, just so i can test it easier. Its this: https://i.imgur.com/ZfLtzsl.png
Im getting the CGImageRef with the following Code:
CGImageRef UICreateScreenImage();
If i add Code to save the Picture to the Album, it saves correctly a complete red screen. So the ImageRef should be correct.
IOSurfaceRef ioSurfaceRef = (__bridge IOSurfaceRef)([UIWindow performSelector:#selector(createScreenIOSurface)]);
CGImageRef cgImageRef = UICreateCGImageFromIOSurface(ioSurfaceRef);
UIImage *uiImage = [UIImage imageWithCGImage:cgImageRef];
UIImageWriteToSavedPhotosAlbum(uiImage, nil, nil, nil);
So, i found code to get the Pixel-Buffer from a ImageRef. Im using the following Code:
size_t width = CGImageGetWidth(cgImageRef);
size_t height = CGImageGetHeight(cgImageRef);
size_t bpr = CGImageGetBytesPerRow(cgImageRef);
size_t bpp = CGImageGetBitsPerPixel(cgImageRef);
size_t bpc = CGImageGetBitsPerComponent(cgImageRef);
size_t bytes_per_pixel = bpp / bpc;
CGDataProviderRef provider = CGImageGetDataProvider(cgImageRef);
CFDataRef rawData = CGDataProviderCopyData(provider);
UInt8 * bytes = (UInt8 *) CFDataGetBytePtr(rawData);
This gives me the width, the height, the buffer
Now, i simply loop through it and print (as test) the Pixels at [100, 100] and [100, 101], both should be red (FF0000FF)
struct SPixel {
int r = 0;
int g = 0;
int b = 0;
int a = 0;
};
for(size_t y = 0; y < height; y++) {
for(size_t x = 0; x < width; x++) {
const uint8_t* pixel = &bytes[y * bpr + x * bytes_per_pixel];
SPixel Pixel;
Pixel.a = pixel[0];
Pixel.r = pixel[1];
Pixel.g = pixel[2];
Pixel.b = pixel[3];
if(y == 100 && x == 100){
Log::Color("red", "Pixel[100, 100]: %d, %d, %d, %d", Pixel.r, Pixel.g, Pixel.b, Pixel.a);
}
if(y == 100 && x == 101){
Log::Color("red", "Pixel[100, 101]: %d, %d, %d, %d", Pixel.r, Pixel.g, Pixel.b, Pixel.a);
}
}
}
And now, if i let that code run, and open my complete red screenshot on fullscreen, the Pixel that get printed are the following:
Pixel[100, 100]: 147, 56, 26, 255
Pixel[100, 101]: 20, 255, 255, 144
They are complete off from Red, and are not even then same, even though they are just one pixel from each other.
What am doing wrong?

Related

Setting pixel color of 8-bit grayscale image using pointer

I have this code:
QImage grayImage = image.convertToFormat(QImage::Format_Grayscale8);
int size = grayImage.width() * grayImage.height();
QRgb *data = new QRgb[size];
memmove(data, grayImage.constBits(), size * sizeof(QRgb));
QRgb *ptr = data;
QRgb *end = ptr + size;
for (; ptr < end; ++ptr) {
int gray = qGray(*ptr);
}
delete[] data;
It is based on this: https://stackoverflow.com/a/40740985/8257882
How can I set the color of a pixel using that pointer?
In addition, using qGray() and loading a "bigger" image seem to crash this.
This works:
int width = image.width();
int height = image.height();
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
image.setPixel(x, y, qRgba(0, 0, 0, 255));
}
}
But it is slow when compared to explicitly manipulating the image data.
Edit
Ok, I have this code now:
for (int y = 0; y < height; ++y) {
uchar *line = grayImage.scanLine(y);
for (int x = 0; x < width; ++x) {
int gray = qGray(line[x]);
*(line + x) = uchar(gray);
qInfo() << gray;
}
}
And it seems to work. However, when I use an image that has only black and white colors and print the gray value, black color gives me 0 and white gives 39. How can I get the gray value in a range of 0-255?
First of all you are copying too much data in this line:
memmove(data, grayImage.constBits(), size * sizeof(QRgb));
The size ob Qrgb is 4 bytes, but according to the documentation, the size of a Format_Grayscale8 pixel is only 8 bits or 1 byte. If you remove sizeof(QRgb) you should be copying the correct amount of bytes, assuming all the lines in the bitmap are consecutive (which, according to the documentation, they are not -- they are aligned to at minimum 32-bits, so you would have to account for that in size). The array data should not be of type Qrgb[size] but ucahr[size]. You can then modify data as you like. Finally, you will probably have to create a new QImage with one of the constructors that accept image bits as uchar and assign the new image to the old image:
auto newImage = QImage( data, image.width(), image.height(), QImage::Format_Grayscale8, ...);
grayImage = std::move( newImage );
But instead of copying image data, you could probably just modify grayImage directly by accessing its data through bits(), or even better, through scanLine(), maybe something like this:
int line, column;
auto pLine = grayImage.scanLine(line);
*(pLine + column) = uchar(grayValue);
EDIT:
According to scanLine documentation, the image is at least 32-bit aligned. So if your 8-bit grayScale image is 3 pixels wide, a new scan line will start every 4 bytes. If you have a 3x3 image, the total size of the memory required to hold the image pixels will be 12. The following code shows the required memory size:
int main() {
auto image = QImage(3, 3, QImage::Format_Grayscale8);
std::cout << image.bytesPerLine() * image.height() << "\n";
return 0;
}
The fill method (setting all gray values to 0xC0) could be implemented like this:
auto image = QImage(3, 3, QImage::Format_Grayscale8);
uchar gray = 0xc0;
for ( int i = 0; i < image.height(); ++i ) {
auto pLine = image.scanLine( i );
for ( int j = 0; j < image.width(); ++j )
*pLine++ = gray;
}

Why QDBMP fail to write 128*128 images?

I am developing a c++ application that reads some bitmap and work with them and then save them as bitmap . I use QDBMP library for working with bitmap file and every thing is good for 512*512 bitmap images . but when working with 128*128 bitmap files it just write some striped line in output . here is my code for reading and writing bitmap files :
int readBitmapImage(const char *file_name,UCHAR* r, UCHAR* g, UCHAR* b)
{
BMP* bmp;
UINT width, height;
bmp = BMP_ReadFile(file_name);
BMP_GetDepth(bmp);
BMP_CHECK_ERROR(stderr, -1);
width = BMP_GetWidth(bmp); height = BMP_GetHeight(bmp);
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
BMP_GetPixelRGB(bmp, x, y, &r[x*width+y], &g[x*width + y], &b[x*width + y]);
}
}
BMP_CHECK_ERROR(stderr, -2);
return 0;
}
void writeImageData(const char *file_name, UCHAR* r, UCHAR* g, UCHAR* b,int width,int height,int bitDepth)
{
BMP* bmp=BMP_Create(width,height,bitDepth);
width = BMP_GetWidth(bmp); height = BMP_GetHeight(bmp);
for (int x = 0; x < width; ++x)
{
for (int y = 0; y < height; ++y)
{
BMP_SetPixelRGB(bmp, x, y, r[x*width + y], g[x*width + y], b[x*width + y]);
}
}
BMP_WriteFile(bmp, file_name);
}
Tank's for your help
UPDATE1
The source image is :
The result of save source image is :
UPDATE2
The value of bitDepth is 24 and code block for alocate memory is :
UCHAR* WimageDataR = (UCHAR*)calloc(128* 128, sizeof(UCHAR));
UCHAR* WimageDataG = (UCHAR*)calloc(128 * 128, sizeof(UCHAR));
UCHAR* WimageDataB = (UCHAR*)calloc(128 * 128, sizeof(UCHAR));
After while i finally found out what is wrong . in BMP_ReadFile() function of QDBMP when the image has size of 128*128 , the header parameter ImageDataSize will not read from the file and has 0 size . so i add this block of code to it to prevent this problem and every thing is just fine.
if (bmp->Header.ImageDataSize == 0)
{
bmp->Header.ImageDataSize = bmp->Header.FileSize - bmp->Header.DataOffset;
}

How to get image data into arrary by ImageMagicK?

I don't know where to start from.
I want to read an image with ImageMagick in an array.
At this point, there is no error:
My_image.read("c:\\a.jpg");
I want to put in the array what I have already read of the image data.
And I want to write to a file using the ImageMagick library.
Here is my code:
...
master.read("c:\\a.jpg");
Image my_image("640x480", "white");
my_image.modifyImage();
Pixels my_pixel_cache(my_image);
PixelPacket* pixels;
int start_x = 0, start_y = 0, size_x = 640, size_y = 480;
*pixels = Color("black");
*(pixels+200) = Color("green");
my_pixel_cache.sync();
...
But I can't get the array of a.jpg. How to get a.jpg image data to an array to modify?
You have to initialize your PixelPacket:
PixelPacket *pixels = my_image.getPixels(0, 0, 640, 480);
then you can modify your image pixel by pixel with a nested loop:
int w = 640;
for (int y = 0; y != h; ++y)
for (int x = 0; x != w; ++x)
{
pixels[w * y + x].red = 255; // if MAGICKCORE_QUANTUM_DEPTH=8
pixels[w * y + x].green = 0;
pixels[w * y + x].blue = 0;
}
Magick::PixelPacket is a struct which contains red, green and blue members (plus another one for the 4th channel). Finally, to write changes to disk:
my_image.syncPixels();
my_image.write("c:\\temp\\output.jpg");

Get RGB values from AVPicture and change to grey-scale in FFMPEG

The main motive of my code is to change the RGB values from the AVPicture in FFMPEG.
I have been able to get the image data "data[0]" by following the article : http://blog.tomaka17.com/2012/03/libavcodeclibavformat-tutorial/
I would like to know that how can I access the 3 bytes of pic.data[0] which is in RGB format. I have been trying to access the pic.data[i][j] via for-loop in 2D matrix fashion but jth element>3.
Any guidance in this regard will be helpful.
Code is here :
AVPicture pic;
avpicture_alloc(&pic, PIX_FMT_RGB24, mpAVFrameInput->width,mpAVFrameInput->height);
auto ctxt = sws_getContext(mpAVFrameInput->width,mpAVFrameInput->height,static_cast<PixelFormat>(mpAVFrameInput->format),
mpAVFrameInput->width, mpAVFrameInput->height, PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr);
if (ctxt == nullptr)
throw std::runtime_error("Error while calling sws_getContext");
sws_scale(ctxt, mpAVFrameInput->data, mpAVFrameInput->linesize, 0, mpAVFrameInput->height, pic.data,
pic.linesize);
for (int i = 0; i < (mpAVFrameInput->height-1); i++) {
for (int j = 0; j < (mpAVFrameInput->width-1); j++) {
printf("\n value: %d",pic.data[0][j]);
}
}
Pseudo code which is in my mind is :
For each pixel in image {
Red = pic.data[i][j].pixel.RED;
Green = pic.data[i][j].pixel.GREEN;
Blue = pic.data[i][j].pixel.BLUE;
GRAY = (Red+Green+Blue)/3;
Red = GRAY;
Green = GRAY;
Blue = GRAY;
Save Frame;}
I am quite new to FFMPEG therefore any guidance and help will be highly appreciable.
Many Thanks
First extract the row data row-by-row of each frame; iterate the loop keeping in view the frame's height.
Here's the sample:
int FrameHeight = FrameInput->height;
int FrameWidth = FrameInput->width;
for(int Counter=0; Counter<FrameHeight; Counter++)
{
int RowSize = FrameWidth*sizeof(uint8_t)*3;
uint8_t* RowData = (uint8_t*) malloc(RowSize);
memset(RowData, 0, RowSize);
memcpy(RowData, AVFrameInput->data[0]+Counter*AVFrameInput->linesize[0], RowSize);
for(int k=0;k<AVFrameInput->linesize[0];++k)
{
if(RowData[k]> 200)
{
RowData[k] = RowData[k]/3;
}
else
{
if(RowData[k] > 150)
{
RowData[k] = RowData[k]/3;
}
else
{
RowData[k] = RowData[k]/3;
}
}
}
memcpy(AVFrameInput->data[0]+Counter*AVFrameInput->linesize[0], RowData, RowSize);
}

OgreBullet incorrect HeightmapCollisionShape shape scale?

I am trying to load a HeightmapTerrainShape in OgreBullet by (mostly) using the demo code, but my terrain mesh is offset from the HeightmapTerrainShape. I have no clue why this is happening. This is my code:
void TerrainLoader::setTerrainPhysics(Ogre::Image *imgPtr)
{
unsigned page_size = terrainGroup->getTerrainSize();
Ogre::Vector3 terrainScale(4096 / (page_size-1), 600, 4096 / (page_size-1));
float *heights = new float[page_size*page_size];
for(unsigned y = 0; y < page_size; ++y)
{
for(unsigned x = 0; x < page_size; ++x)
{
Ogre::ColourValue color = imgPtr->getColourAt(x, y, 0);
heights[x + y * page_size] = color.r;
}
}
OgreBulletCollisions::HeightmapCollisionShape *terrainShape = new OgreBulletCollisions::HeightmapCollisionShape(
page_size,
page_size,
terrainScale,
heights,
true
);
OgreBulletDynamics::RigidBody *terrainBody = new OgreBulletDynamics::RigidBody(
"Terrain",
OgreInit::level->physicsManager->getWorld()
);
imgPtr = NULL;
Ogre::Vector3 terrainShiftPos(terrainScale.x/(page_size-1), 0, terrainScale.z/(page_size-1));
terrainShiftPos.y = terrainScale.y / 2 * terrainScale.y;
Ogre::SceneNode *pTerrainNode = OgreInit::sceneManager->getRootSceneNode()->createChildSceneNode();
terrainBody->setStaticShape(pTerrainNode, terrainShape, 0.0f, 0.8f, terrainShiftPos);
//terrainBody->setPosition(terrainBody->getWorldPosition()-Ogre::Vector3(0.005, 0, 0.005));
OgreInit::level->physicsManager->addBody(terrainBody);
OgreInit::level->physicsManager->addShape(terrainShape);
}
This is what it looks like with the debug drawer turned on:
My world is 4096*600*4096 in size, and each chunk is 64*600*64
heights[x + y * page_size] = color.r;
This Line gives you negative values. If you combine negative terrain height values with ogre bullet terrain, you get a wrong bounding box conversation.
You need to use the intervall 0-1 for height values.
Had the same problem with perlin noise filter that gives you values from -1 to 1.