getting image color information from both RGB32 and indexed type images - image

I am trying to access the image colors in a QImage.
The method that I found most in docs is based on the scanline function...
I tried and it worked... on RGB32 images. I had surprising - and unpleasant results when using the exact method to get color data for 8 bit indexed or monochrome images.
This was my code:
// note RGBTriple is a struct containing unsigned R, G, B
// rgbImage.pixels is a RGBTriple* array
RGBTriple* pTriple = rgbImage.pixels;
for (int y = 0; y < source.height(); y++)
{
const unsigned char* pScanLine = source.scanLine(y);
for (int x = 0; x < source.width(); x++)
{
QRgb* color = (QRgb*)pScanLine;
pTriple->R = qRed(*color);
pTriple->G = qGreen(*color);
pTriple->B = qBlue(*color);
++pTriple;
pScanLine += 4;
}
}
Running the same code with images 8bit indexed or monochrome, I got errors in creating getting colors. The documentation says that scanline is aligned to multiples of 32b - but since that is a multiple of 8 and 2 I didn't think it would be a problem.
Once I found out that I am not getting correct results for all types of input images, I changed it to
RGBTriple* pTriple = rgbImage.pixels;
for (int y = 0; y < source.height(); y++)
{
for (int x = 0; x < source.width(); x++)
{
pTriple->R = qRed(source.pixel(x, y));
pTriple->G = qGreen(source.pixel(x, y));
pTriple->B = qBlue(source.pixel(x, y));
++pTriple;
}
}
Works perfectly... I wonder if it is slower or will have other unexpected behavior ? After all, I am using the pixel() function - even on indexed images - to get color information, which actually should be stored differently... that seems like it should fail...
Is there a way to make the first version, using scanline, work for other image types ?
Why does it seem like using scanline to get the data is the preferred method ?

I tried and it worked... on RGB32 images. I had surprising - and
unpleasant results when using the exact method to get color data for 8
bit indexed or monochrome images.
You should not be surprised because the indexed and monochrome images are different formats. The first code snippet you posted is based on the knowledge on how RGB32 (and RGB32 only) is layed out in memory.
Think about it. In a monochrome image R=G=B. So only one channel need to be saved in memory.
If your goal is to obtain an rgb image inside rgbImage.pixels use QImage::convertToFormat() :
QImage source;
QImage dest = source.convertToFormat( QImage::Format_RGB888 );
memcpy(rgbImage.pixels, dest.bits(),dest.byteCount () );

Related

How to flatten an image using OpenCV correctly for image processing and then convert it to Mat again?

I have an image, read using "cv::imread". I have to flatten it so that I could use CUDA & GPU for my image processing algorithms acceleration.
My problem: When I read my image, I can show it correctly using imshow, however when I flatten it and convert it to a Mat object to be used with imshow, only part of my image is displayed. The size of the output image is also wrong, meaning that some data is really lost. What's the problem with my for loop?
// The problematic part of my code
// The Camera Man gray test image
const char* img_gray_name = "../../Test_Images/cameraman.tiff";
const char* img_blur_name = "../cameraman-blur.tiff";
const char* image_general_name = "cameraman_blur";
cv::Mat img = cv::imread(img_gray_name);
unsigned long int img_gray_size = img.rows * img.cols * sizeof(uchar);
uchar *h_img_in;// input image, converted to a flat array to be
// processed by GPU
h_img_in = (uchar *)malloc(img_gray_size);
//*************** The bug should be here! ***************//
for (int i = 0; i < img.rows; ++i) {
for (int j = 0; j < img.cols; ++j) {
h_img_in[i*img.cols+j] = img.at<uchar>(i, j);
}
}
Mat img_test;
img_test = Mat(cv::Size(img.cols, img.rows), CV_8U, h_img_in);
imwrite(img_blur_name, img_test);
// create image window named "camera man"
cv::namedWindow(image_general_name);
// show the image on window
cv::imshow(image_general_name, img_test);
P.S.: I also tested with a new 2D array instead of 1D h_img_in, result is the same; This means that something goes wrong with my usage of "img.at(i, j)".

Upscaling images on Retina devices

I know images upscale by default on retina devices, but the default scaling makes the images blurry.
I was wondering if there was a way to scale it in nearest-neighbor mode, where there are no transparent pixels created, but rather each pixel multiplied by 4, so it looks like it would on a non retina device.
Example of what I'm talking about can be seen in the image below.
example http://cclloyd.com/downloads/sdfsdf.png
CoreGraphics will not do a 2x scale like that, you need to write a bit of explicit pixel mapping logic to do something like this. The following is some code I used to do this operation, you would of course need to fill in the details as this operates on an input buffer of pixels and writes to an output buffer of pixels that is 2x larger.
// Use special case "DOUBLE" logic that will simply duplicate the exact
// RGB value from the indicated pixel into the 2x sized output buffer.
int numOutputPixels = resizedFrameBuffer.width * resizedFrameBuffer.height;
uint32_t *inPixels32 = (uint32_t*)cgFrameBuffer.pixels;
uint32_t *outPixels32 = (uint32_t*)resizedFrameBuffer.pixels;
int outRow = 0;
int outColumn = 0;
for (int i=0; i < numOutputPixels; i++) {
if ((i > 0) && ((i % resizedFrameBuffer.width) == 0)) {
outRow += 1;
outColumn = 0;
}
// Divide by 2 to get the column/row in the input framebuffer
int inColumn = outColumn / 2;
int inRow = outRow / 2;
// Get the pixel for the row and column this output pixel corresponds to
int inOffset = (inRow * cgFrameBuffer.width) + inColumn;
uint32_t pixel = inPixels32[inOffset];
outPixels32[i] = pixel;
//fprintf(stdout, "Wrote 0x%.10X for 2x row/col %d %d (%d), read from row/col %d %d (%d)\n", pixel, outRow, outColumn, i, inRow, inColumn, inOffset);
outColumn += 1;
}
This code of course depends on you creating a buffer of pixels and then wrapping it back up into CFImageRef. But, you can find all the code to do that kind of thing easily.

Retrieve color information from images

I need to determine the amount/quality of color in an image in order to compare it with other images and recommend a user (owner of the image) maybe he needs to print it in black and white and not in color.
So far I'm analyzing the image and extracting some data of it:
The number of different colors I find in the image
The percentage of color in the whole page (color pixels / total pixels)
For further analysis I may need other characteristic of these images. Do you know what else is important (or I'm missing here) in image analysis?
After some time I found a missing characteristic (very important) which helped me a lot with the analysis of the images. I don't know if there is a name for that but I called it the average color of the image:
When I was looping over all the pixels of the image and counting each color I also retrieved the information of the RGB values and summarized all the Reds, Greens and Blues of all the pixels. Just to come up with this average color which, again, saved my life when I wanted to compare some kind of images.
The code is something like this:
File f = new File("image.jpg");
BufferedImage im = ImageIO.read(f);
int tot = 0;
int red = 0;
int blue= 0;
int green = 0;
int w = im.getWidth();
int h = im.getHeight();
// Going over all the pixels
for (int i=0;i<w;i++){
for (int j=0;j<h;j++){
int pix = im.getRGB(i, j); //
if (!sameARGB(pix)) { // Compares the RGB values
tot+=1;
red+=pix.getRed();
green+=pix.getGreen();
blue+=pix.getBlue();
}
}
}
And you should get the results like this:
// Percentage of color on the image
double per = (double)tot/(h*w);
// Average color <-------------
Color c = new Color((double)red/tot,(double)green/tot,(double)blue/tot);

Reading an Image (standard format png,jpeg etc) and writing the Image Data to a binary file using Objective C

I am pretty new to Objective C and working with Cocoa Framework. I want to read an image and then extract the image data (just pixel data and not the header) and then write the data to a binary file. I am kind of stuck with this, I was going through the methods of NSImage but I couldn't find a suitable one. Can anyone suggest me some other ways of doing this?
Cocoa-wise, the easiest approach is to use the NSBitmapImageRep class. Once initialized with a NSData object, for example, you can access the color value at any coordinate as a NSColor object using the -setColor:atX:y: and -colorAtX:y: methods. Note that if you call these methods in tight loops, you may suffer a performance hit from objc_msg_send. You could consider accessing the raw bitmap data as C array via the -bitmapData method. When dealing with a RGB image, for example, the color values for each channel are stored at offsets of 3.
For example:
color values: [R,G,B][R,G,B][R,G,B]
indices: [0,1,2, 3,4,5, 6,7,8]
To loop through each pixel in the image and extract the RGB components:
unsigned char *bitmapData = [bitmapRep bitmapData];
if ([bitmapRep samplesPerPixel] == 3) {
for (i = 0; i < [image size].width * [image size].height; i++) {
int base = (i * 3);
// these range from 0-255
unsigned char red = bitmapData[base + 0];
unsigned char green = bitmapData[base + 1];
unsigned char blue = bitmapData[base + 2];
}
}

QT QImage pixel manipulation

I am building a QT GUI application and use QImage for opening images.
My problem is that I can't figure out how to use QImage's bit() and scanline()
methods to get access at per pixel level.
I've seen this post Qt QImage pixel manipulation problems
but this is only for the first pixel of each row. Is this correct or I got it all wrong?
thanks in advance
The scanlines correspond to the the height of image, the columns correspond to the width of the image.
According to the docs, the prototype looks like uchar* QImage::scanline(int i), or a similar const version.
But, as a commenter pointed out, because the data is dependent on the machine architecture and image, you should NOT use the uchar * directly. Instead, use something like the following:
QRgb *rowData = (QRgb*)img.scanLine(row);
QRgb pixelData = rowData[col];
int red = qRed(pixelData);
It may not be immediately obvious from Kaleb's post, but the following works for setting a pixel on a Format_RGB32 image.
// Get the line we want
QRgb *line = (QRgb *)image->scanLine(row_index);
// Go to the pixel we want
line += col_index;
// Actually set the pixel
*line = qRgb(qRed(color), qGreen(color), qBlue(color));
The answer did not work for me. It looks like, the data is not 32bit aligned on my system.
To get the correct data, on my system i had to do this:
for(uint32_t Y = 0; Y < mHeight; ++Y)
{
uint8_t* pPixel = Image.scanLine(Y);
for(uint32_t X = 0; X < mWidth; ++X)
{
const int Blue = *pPixel++;
const int Green = *pPixel++;
const int Red = *pPixel++;
uint8_t GrayscalePixel = (0.21f * Red) + (0.72f * Green) + (0.07 * Blue);
}
}

Resources