How can I get the dominant color of a QImage or QIcon in Qt5 ?
Does it need generating a histogram manually ?
And if yes , how ?
You can't get this data via Qt.
But I found for You this web page (http://robocraft.ru/blog/computervision/1063.html).
On this web page some guy wrote code for your question! But all description only on Russian language. I translated it for you.
He is using OpenCV lib.
//
// determination of the prevailing colors in the image
// via k-Means
//
#include <cv.h>
#include <highgui.h>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <algorithm>
// getting an image pixel (by type of image and coordinates)
#define CV_PIXEL(type,img,x,y) (((type*)((img)->imageData+(y)*(img)->widthStep))+(x)*(img)->nChannels)
const CvScalar BLACK = CV_RGB(0, 0, 0); // black
const CvScalar WHITE = CV_RGB(255, 255, 255); // white
const CvScalar RED = CV_RGB(255, 0, 0); // red
const CvScalar ORANGE = CV_RGB(255, 100, 0); // orange
const CvScalar YELLOW = CV_RGB(255, 255, 0); // yellow
const CvScalar GREEN = CV_RGB(0, 255, 0); // green
const CvScalar LIGHTBLUE = CV_RGB(60, 170, 255); // blue
const CvScalar BLUE = CV_RGB(0, 0, 255); // blue 2
const CvScalar VIOLET = CV_RGB(194, 0, 255); // Violet
const CvScalar GINGER = CV_RGB(215, 125, 49); // redhead
const CvScalar PINK = CV_RGB(255, 192, 203); // pink
const CvScalar LIGHTGREEN = CV_RGB(153, 255, 153); // light green
const CvScalar BROWN = CV_RGB(150, 75, 0); // brown
typedef unsigned char uchar;
typedef unsigned int uint;
typedef struct ColorCluster {
CvScalar color;
CvScalar new_color;
int count;
ColorCluster():count(0) {
}
} ColorCluster;
float rgb_euclidean(CvScalar p1, CvScalar p2)
{
float val = sqrtf( (p1.val[0]-p2.val[0])*(p1.val[0]-p2.val[0]) +
(p1.val[1]-p2.val[1])*(p1.val[1]-p2.val[1]) +
(p1.val[2]-p2.val[2])*(p1.val[2]-p2.val[2]) +
(p1.val[3]-p2.val[3])*(p1.val[3]-p2.val[3]));
return val;
}
// sorting flowers by quantity
bool colors_sort(std::pair< int, uint > a, std::pair< int, uint > b)
{
return (a.second > b.second);
}
int main(int argc, char* argv[])
{
// for save image
IplImage* image=0, *src=0, *dst=0, *dst2=0;
//
// loading image
//
char img_name[] = "Image0.jpg";
// name of image in first arg
char* image_filename = argc >= 2 ? argv[1] : img_name;
// get image
image = cvLoadImage(image_filename, 1);
printf("[i] image: %s\n", image_filename);
if(!image){
printf("[!] Error: cant load test image: %s\n", image_filename);
return -1;
}
// show image
cvNamedWindow("image");
cvShowImage("image", image);
// resize image (for processing speed)
src = cvCreateImage(cvSize(image->width/2, image->height/2), IPL_DEPTH_8U, 3);
cvResize(image, src, CV_INTER_LINEAR);
cvNamedWindow("img");
cvShowImage("img", src);
// image for storing cluster indexes
IplImage* cluster_indexes = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvZero(cluster_indexes);
#define CLUSTER_COUNT 10
int cluster_count = CLUSTER_COUNT;
ColorCluster clusters[CLUSTER_COUNT];
int i=0, j=0, k=0, x=0, y=0;
// initial cluster colors
#if 0
clusters[0].new_color = RED;
clusters[1].new_color = ORANGE;
clusters[2].new_color = YELLOW;
// clusters[3].new_color = GREEN;
// clusters[4].new_color = LIGHTBLUE;
// clusters[5].new_color = BLUE;
// clusters[6].new_color = VIOLET;
#elif 0
clusters[0].new_color = BLACK;
clusters[1].new_color = GREEN;
clusters[2].new_color = WHITE;
#else
CvRNG rng = cvRNG(-1);
for(k=0; k<cluster_count; k++)
clusters[k].new_color = CV_RGB(cvRandInt(&rng)%255, cvRandInt(&rng)%255, cvRandInt(&rng)%255);
#endif
// k-means
float min_rgb_euclidean = 0, old_rgb_euclidean=0;
while(1) {
for(k=0; k<cluster_count; k++) {
clusters[k].count = 0;
clusters[k].color = clusters[k].new_color;
clusters[k].new_color = cvScalarAll(0);
}
for (y=0; y<src->height; y++) {
for (x=0; x<src->width; x++) {
// get the RGB components of the pixel
uchar B = CV_PIXEL(uchar, src, x, y)[0]; // B
uchar G = CV_PIXEL(uchar, src, x, y)[1]; // G
uchar R = CV_PIXEL(uchar, src, x, y)[2]; // R
min_rgb_euclidean = 255*255*255;
int cluster_index = -1;
for(k=0; k<cluster_count; k++) {
float euclid = rgb_euclidean(cvScalar(B, G, R, 0), clusters[k].color);
if( euclid < min_rgb_euclidean ) {
min_rgb_euclidean = euclid;
cluster_index = k;
}
}
// set the cluster index
CV_PIXEL(uchar, cluster_indexes, x, y)[0] = cluster_index;
clusters[cluster_index].count++;
clusters[cluster_index].new_color.val[0] += B;
clusters[cluster_index].new_color.val[1] += G;
clusters[cluster_index].new_color.val[2] += R;
}
}
min_rgb_euclidean = 0;
for(k=0; k<cluster_count; k++) {
// new color
clusters[k].new_color.val[0] /= clusters[k].count;
clusters[k].new_color.val[1] /= clusters[k].count;
clusters[k].new_color.val[2] /= clusters[k].count;
float ecli = rgb_euclidean(clusters[k].new_color, clusters[k].color);
if(ecli > min_rgb_euclidean)
min_rgb_euclidean = ecli;
}
//printf("%f\n", min_rgb_euclidean);
if( fabs(min_rgb_euclidean - old_rgb_euclidean)<1 )
break;
old_rgb_euclidean = min_rgb_euclidean;
}
//-----------------------------------------------------
// Now we drive the array into a vector and sort it :)
std::vector< std::pair< int, uint > > colors;
colors.reserve(CLUSTER_COUNT);
int colors_count = 0;
for(i=0; i<CLUSTER_COUNT; i++){
std::pair< int, uint > color;
color.first = i;
color.second = clusters[i].count;
colors.push_back( color );
if(clusters[i].count>0)
colors_count++;
}
// sort
std::sort( colors.begin(), colors.end(), colors_sort );
// show color
dst2 = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 3 );
cvZero(dst2);
int h = dst2->height / CLUSTER_COUNT;
int w = dst2->width;
for(i=0; i<CLUSTER_COUNT; i++ ){
cvRectangle(dst2, cvPoint(0, i*h), cvPoint(w, i*h+h), clusters[colors[i].first].color, -1);
printf("[i] Color: %d %d %d (%d)\n", (int)clusters[colors[i].first].color.val[2],
(int)clusters[colors[i].first].color.val[1],
(int)clusters[colors[i].first].color.val[0],
clusters[colors[i].first].count);
}
cvNamedWindow("colors");
cvShowImage("colors", dst2);
//cvResize(dst2, image, CV_INTER_LINEAR);
//cvSaveImage("dominate_colors_table.png", image);
//-----------------------------------------------------
// show the picture in the colors found
dst = cvCloneImage(src);
for (y=0; y<dst->height; y++) {
for (x=0; x<dst->width; x++) {
int cluster_index = CV_PIXEL(uchar, cluster_indexes, x, y)[0];
CV_PIXEL(uchar, dst, x, y)[0] = clusters[cluster_index].color.val[0];
CV_PIXEL(uchar, dst, x, y)[1] = clusters[cluster_index].color.val[1];
CV_PIXEL(uchar, dst, x, y)[2] = clusters[cluster_index].color.val[2];
}
}
cvNamedWindow("dst");
cvShowImage("dst", dst);
//cvResize(dst, image, CV_INTER_LINEAR);
//cvSaveImage("dominate_colors.png", image);
//-----------------------------------------------------
// waiting for the keystroke
cvWaitKey(0);
// free up resources
cvReleaseImage(&image);
cvReleaseImage(&src);
cvReleaseImage(&cluster_indexes);
cvReleaseImage(&dst);
cvReleaseImage(&dst2);
// delete windows
cvDestroyAllWindows();
return 0;
}
Finally with the help of QImage class I'm able to get this done and I implemented a simple algorithm from scratch.
The idea is to get the most frequent intensity in each color channel , and then combining them to get the final RGB color.
It returns a QVector<int> of three channels.
Helper function:
int MainWindow::get_max(QVector<int> vec){
int max = vec[0];
int index = 0;
for(int i = 0 ; i < 255 ; i++){
if(vec[i] > max){
max = vec[i];
index = i;
}
}
return index;
}
And the picker function :
QVector<int> MainWindow::get_mean_rgb_for_img(const QImage img){
QRgb *ct;
int width , height;
width = img.width();
height = img.height();
QVector<int> red(256);
QVector<int> green(256);
QVector<int> blue(256);
for(int i = 0 ; i < height ; i++){
ct = (QRgb *)img.scanLine(i);
for(int j = 0 ; j < width ; j++){
red[qRed(ct[j])]+=1;
}
}
for(int i = 0 ; i < height ; i++){
ct = (QRgb *)img.scanLine(i);
for(int j = 0 ; j < width ; j++){
green[qGreen(ct[j])]+=1;
}
}
for(int i = 0 ; i < height ; i++){
ct = (QRgb *)img.scanLine(i);
for(int j = 0 ; j < width ; j++){
blue[qBlue(ct[j])]+=1;
}
}
int red_val = get_max(red);
int green_val = get_max(green);
int blue_val = get_max(blue);
QVector<int> result(3);
result.insert(0 , red_val);
result.insert(1 , green_val);
result.insert(2 , blue_val);
return result;
}
Although there is just one thread for those calculations (At least on the paper , but in practice Qt spawned a handful of threads without my notice by itself) it's pretty much fast and requires little CPU time even for 4K images !!!
Related
I have a problem with my ILI9488 TFT Touch LCD module (Arduino Uno Shield). (320x480)
I can show .bmp pictures on the screen, read out of a SD-card.
In another testprogram, I can Serial.print() a char when I touch the display. (That's all it needs to do)
But when I merge the two programs together, it doesnt't work anymore.
I think the libraries are interferring with each other.
Is there any way to fix this, or can anyone recommend two compatible libraries to fix this?
Thanks in advance!
//----------------------------------------------------------------------------------------
// Declaration MCUFRIEND
//----------------------------------------------------------------------------------------
#include "MCUFRIEND_kbv.h"
MCUFRIEND_kbv tft;
#define LOWFLASH (defined(__AVR_ATmega328P__) && defined(MCUFRIEND_KBV_H_))
//----------------------------------------------------------------------------------------
// Declaration SDCard & Dependencies
//----------------------------------------------------------------------------------------
#include <SPI.h>
#include <SD.h>
#include <Adafruit_GFX.h>
#include <stdint.h>
#include "TouchScreen.h"
#define YP A2
#define XM A3
#define YM 8
#define XP 9
#define MINPRESSURE 1000
#define MAXPRESSURE 10000
//---------------------------------------------------------------------------------------
///Variables & Constants
#define BLACK 0x0000 /* 0, 0, 0 */
#define NAVY 0x000F /* 0, 0, 128 */
#define DARKGREEN 0x03E0 /* 0, 128, 0 */
#define DARKCYAN 0x03EF /* 0, 128, 128 */
#define MAROON 0x7800 /* 128, 0, 0 */
#define PURPLE 0x780F /* 128, 0, 128 */
#define OLIVE 0x7BE0 /* 128, 128, 0 */
#define LIGHTGREY 0xC618 /* 192, 192, 192 */
#define DARKGREY 0x7BEF /* 128, 128, 128 */
#define BLUE 0x001F /* 0, 0, 255 */
#define GREEN 0x07E0 /* 0, 255, 0 */
#define CYAN 0x07FF /* 0, 255, 255 */
#define RED 0xF800 /* 255, 0, 0 */
#define MAGENTA 0xF81F /* 255, 0, 255 */
#define YELLOW 0xFFE0 /* 255, 255, 0 */
#define WHITE 0xFFFF /* 255, 255, 255 */
#define ORANGE 0xFDA0 /* 255, 180, 0 */
#define GREENYELLOW 0xB7E0 /* 180, 255, 0 */
#define PINK 0xFC9F
#define SD_CS 10 // Chip Select from SPI Interface
#define LOGO "default"
#define AUTHUSER "authuser"
#define ADDUSER "adduser"
#define PALETTEDEPTH 4
File root;
char ReadMode = '0'; // Sleep = 0, Default = 1, Finger ID = 2, Finger Enroll = 3
char namebuf[32] = "/"; // BMP=Files in the root directory
int x, y, pathlen, count;
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);
bool pressed = false;
void setup()
{
uint16_t ID = tft.readID();
Serial.begin(9600);
tft.begin(ID);
//Please select the mode you want to use
//Sleep --> 0
//Default --> 1
//FingerID --> 2
//FingerEnroll --> 3
//Getting SDCard Ready
bool good = SD.begin(SD_CS);
root = SD.open(namebuf);
pathlen = strlen(namebuf);
x = 0;
y = 0;
}
void loop(void)
{
if (Serial.available()) {
ReadMode = Serial.read();
}
if (ReadMode == '0') {
tft.fillScreen(NULL);
}
if(ReadMode == '1') // The Default HomeScreen displays, Read from the SDCard
{
for(int i = 0; i < 5; i++)
{
char *nm = namebuf + pathlen;
File f = root.openNextFile();
uint8_t ret;
uint32_t start;
if (f != NULL)
{
#ifdef USE_SDFAT
f.getName(nm, 32 - pathlen);
#else
strcpy(nm, (char *)f.name());
#endif
f.close();
strlwr(nm);
if (strstr(nm, ".bmp") != NULL && strstr(nm, LOGO) != NULL)
{
ret = showBMP(namebuf, x, y);
}
}
else root.rewindDirectory();
}
}
if (ReadMode == '2') // The AuthUser displays, Read from the SDCard
{
for(int i = 0; i < 5; i++)
{
char *nm = namebuf + pathlen;
File f = root.openNextFile();
uint8_t ret;
uint32_t start;
if (f != NULL)
{
#ifdef USE_SDFAT
f.getName(nm, 32 - pathlen);
#else
strcpy(nm, (char *)f.name());
#endif
f.close();
strlwr(nm);
if (strstr(nm, ".bmp") != NULL && strstr(nm, AUTHUSER) != NULL)
{
ret = showBMP(namebuf, x, y);
}
}
else root.rewindDirectory();
}
}
TSPoint p = ts.getPoint();
if (p.z > MINPRESSURE && p.z < MAXPRESSURE) { // TOUCH
Serial.println('X');
}
}
//----------------------------------------------------------------------------------------
// Methods for reading from SDCard
//----------------------------------------------------------------------------------------
#define BMPIMAGEOFFSET 54
#define BUFFPIXEL 20
uint16_t read16(File& f) {
uint16_t result; // read little-endian
f.read((uint8_t*)&result, sizeof(result));
return result;
}
uint32_t read32(File& f) {
uint32_t result;
f.read((uint8_t*)&result, sizeof(result));
return result;
}
uint8_t showBMP(char *nm, int x, int y)
{
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24, 16, 8, 4, 1)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
uint16_t lcdbuffer[(1 << PALETTEDEPTH) + BUFFPIXEL], *palette = NULL;
uint8_t bitmask, bitshift;
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col, lcdbufsiz = (1 << PALETTEDEPTH) + BUFFPIXEL, buffidx;
uint32_t pos; // seek position
boolean is565 = false; //
uint16_t bmpID;
uint16_t n; // blocks read
uint8_t ret;
if ((x >= tft.width()) || (y >= tft.height()))
return 1; // off screen
bmpFile = SD.open(nm); // Parse BMP header
bmpID = read16(bmpFile); // BMP signature"
(void) read32(bmpFile); // Read & ignore file size
(void) read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
(void) read32(bmpFile); // Read & ignore DIB header size
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
n = read16(bmpFile); // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
pos = read32(bmpFile); // format
if (bmpID != 0x4D42) ret = 2; // bad ID
else if (n != 1) ret = 3; // too many planes
else if (pos != 0 && pos != 3) ret = 4; // format: 0 = uncompressed, 3 = 565
else if (bmpDepth < 16 && bmpDepth > PALETTEDEPTH) ret = 5; // palette
else {
bool first = true;
is565 = (pos == 3); // ?already in 16-bit format
// BMP rows are padded (if needed) to 4-byte boundary
rowSize = (bmpWidth * bmpDepth / 8 + 3) & ~3;
if (bmpHeight < 0) { // If negative, image is in top-down order.
bmpHeight = -bmpHeight;
flip = false;
}
w = bmpWidth;
h = bmpHeight;
if ((x + w) >= tft.width()) // Crop area to be loaded
w = tft.width() - x;
if ((y + h) >= tft.height()) //
h = tft.height() - y;
if (bmpDepth <= PALETTEDEPTH) { // these modes have separate palette
bmpFile.seek(bmpImageoffset - (4<<bmpDepth)); //54 for regular, diff for colorsimportant
bitmask = 0xFF;
if (bmpDepth < 8)
bitmask >>= bmpDepth;
bitshift = 8 - bmpDepth;
n = 1 << bmpDepth;
lcdbufsiz -= n;
palette = lcdbuffer + lcdbufsiz;
for (col = 0; col < n; col++) {
pos = read32(bmpFile); //map palette to 5-6-5
palette[col] = ((pos & 0x0000F8) >> 3) | ((pos & 0x00FC00) >> 5) | ((pos & 0xF80000) >> 8);
}
}
// Set TFT address window to clipped image bounds
tft.setAddrWindow(x, y, x + w - 1, y + h - 1);
for (row = 0; row < h; row++) { // For each scanline...
uint8_t r, g, b, *sdptr;
int lcdidx, lcdleft;
if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
else // Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize;
if (bmpFile.position() != pos) { // Need seek?
bmpFile.seek(pos);
buffidx = sizeof(sdbuffer); // Force buffer reload
}
for (col = 0; col < w; ) { //pixels in row
lcdleft = w - col;
if (lcdleft > lcdbufsiz) lcdleft = lcdbufsiz;
for (lcdidx = 0; lcdidx < lcdleft; lcdidx++) { // buffer at a time
uint16_t color;
// Time to read more pixel data?
if (buffidx >= sizeof(sdbuffer)) { // Indeed
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
r = 0;
}
switch (bmpDepth) { // Convert pixel from BMP to TFT format
case 24:
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
color = tft.color565(r, g, b);
break;
case 16:
b = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
if (is565)
color = (r << 8) | (b);
else
color = (r << 9) | ((b & 0xE0) << 1) | (b & 0x1F);
break;
case 1:
case 4:
case 8:
if (r == 0)
b = sdbuffer[buffidx++], r = 8;
color = palette[(b >> bitshift) & bitmask];
r -= bmpDepth;
b <<= bmpDepth;
break;
}
lcdbuffer[lcdidx] = color;
}
tft.pushColors(lcdbuffer, lcdidx, first);
first = false;
col += lcdidx;
} // end cols
} // end rows
tft.setAddrWindow(0, 0, tft.width() - 1, tft.height() - 1); //restore full screen
ret = 0; // good render
}
bmpFile.close();
return (ret);
}
I have tried several different methods found around the internet, however none of them seem to work. This code works for cases 0-2 but when it goes into case 3 which is the rainbow chase loop, the pressing of the button does not interrupt the loop and move the counter forward. I'm assuming, as usual, I'm missing something stupid so many thanks in advance.
#define FASTLED_ALLOW_INTERRUPTS 1
#define FASTLED_INTERRUPT_RETRY_COUNT 1
#include <FastLED.h>
#define AnalogIn A0
#define SwIn 2
#define LED_Out 12
#define NUM_LEDS 5
int pushCounterz = 0;
volatile int buttonState; // Set volatile for interrupt DO NOT SHAKE!
int lastButtonState;
CRGB leds[NUM_LEDS];
void setup() {
// put your setup code here, to run once:
FastLED.setMaxRefreshRate(250);
FastLED.addLeds<WS2812, LED_Out, GRB>(leds, NUM_LEDS);
pinMode(SwIn, INPUT);
pinMode(LED_Out, OUTPUT);
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 255, 0, 255 );
}
FastLED.show();
delay(120);
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 0 );
}
FastLED.show();
Serial.begin(115200);
Serial.println(pushCounterz);
lastButtonState = digitalRead(SwIn); // Set the button state to the startup state
attachInterrupt((SwIn-2), button_ISR, CHANGE); // Set SwIn button as an interrupt pin // Change to Low???
}
void loop() {
if (pushCounterz != 3) {
FastLED.show();
}
Serial.println(pushCounterz);
delay(120);
}
void button_ISR () {
buttonState = digitalRead(SwIn);
digitalWrite(13, buttonState);
if (buttonState == LOW && buttonState != lastButtonState) {
if (pushCounterz > 3) {
//Serial.println("Reset to 0: ");
pushCounterz = 0;
} else {
pushCounterz = pushCounterz + 1;
//Serial.println("Incerment");
}
//Serial.println(pushCounterz);
switch (pushCounterz) {
case 0:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB (255, 0, 0);
}
break;
case 1:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 255, 0);
}
break;
case 2:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 255);
}
break;
case 3:
theaterChaseRainbow(1,50);
break;
default:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 0);
}
break;
}
}
lastButtonState = buttonState;
}
// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
for (int j = 0; j < 256 * cycles; j++) { // cycle all 256 colors in the wheel
for (int q = 0; q < 3; q++) {
for (int i = 0; i < NUM_LEDS; i = i + 3) {
int pos = i + q;
leds[pos] = Wheel( (i + j) % 255); //turn every third pixel on
}
FastLED.show();
delay(speed);
for (int i = 0; i < NUM_LEDS; i = i + 3) {
leds[i + q] = CRGB::Black; //turn every third pixel off
}
}
}
}
CRGB Wheel(byte WheelPos) {
if (WheelPos < 85) {
return CRGB(WheelPos * 3, 255 - WheelPos * 3, 0);
}
else if (WheelPos < 170) {
WheelPos -= 85;
return CRGB(255 - WheelPos * 3, 0, WheelPos * 3);
}
else {
WheelPos -= 170;
return CRGB(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
Your issue is not that the button is not changing the value, but rather your code has no exit point if it does; the button will change the value, but nothing in theaterChaseRainbow tells it to stop.
Simply add a check in the method to return out if the button state changes:
// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
for (int j = 0; j < 256 * cycles; j++) { // cycle all 256 colors in the wheel
for (int q = 0; q < 3; q++) {
for (int i = 0; i < NUM_LEDS; i = i + 3) {
int pos = i + q;
leds[pos] = Wheel( (i + j) % 255); //turn every third pixel on
}
FastLED.show();
if (pushCounterz != 3) return; //ADDED THIS HERE*****
delay(speed);
for (int i = 0; i < NUM_LEDS; i = i + 3) {
leds[i + q] = CRGB::Black; //turn every third pixel off
}
}
}
}
In addition, I would suggest simplifying your ISR to just increment the button and not have it handle the logic of the program as well. That should either be contained in the loop method or called from the loop method. This should make for some cleaner and less confusing code, as the ISR's job is simply to adjust the value of the button counter, and the loops job is to deal with the state that the program is currently in.
Also - you can't allow interrupts on AVR, or rather I should say it does nothing. I should put in a warning message when that's happening - AVR/arduino's ISR handling is so slow that even the clock tick ISR would be enough to disrupt writing out WS2812 data (resulting in FastLED cutting the frame off) so I yanked that code out of the avr WS2812 asm implementation. Most arm and esp platforms that FastLED supports do allow for interrupt handling to occur during in the small window between writing out each led's data - courtesy of their higher clock speeds.
If you're using an ARM or ESP based platform, then feel free to ignore this comment (mostly putting it here so folks who stumble on this question in a good search know what's up).
As a reference, the working code with the ISR cleanup. (mind you there is still some serial debugging code in there as I have more work to do with brightness etc)
#define FASTLED_ALLOW_INTERRUPTS 1
#define FASTLED_INTERRUPT_RETRY_COUNT 1
#include <FastLED.h>
#define AnalogIn A0
#define SwIn 2
#define LED_Out 12
#define NUM_LEDS 5
int pushCounterz = 4; // 4 = off
volatile int buttonState; // Set volatile for interrupt DO NOT SHAKE!
int lastButtonState;
CRGB leds[NUM_LEDS];
void setup() {
// put your setup code here, to run once:
FastLED.setMaxRefreshRate(250);
FastLED.addLeds<WS2812, LED_Out, GRB>(leds, NUM_LEDS);
pinMode(SwIn, INPUT);
pinMode(LED_Out, OUTPUT);
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 255, 0, 255 );
}
FastLED.show();
delay(120);
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 0 );
}
FastLED.show();
Serial.begin(19200);
Serial.println(pushCounterz);
lastButtonState = digitalRead(SwIn); // Set the button state to the startup state
attachInterrupt((SwIn-2), button_ISR, LOW); // Set SwIn button as an interrupt pin // Change to Low???
}
void loop() {
// if (pushCounterz != 3) {
//FastLED.show();
//Serial.println(pushCounterz);
// }
//delay(20);
switch (pushCounterz) {
case 0:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB (255, 0, 0);
}
FastLED.show();
break;
case 1:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 255, 0);
}
FastLED.show();
break;
case 2:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 255);
}
FastLED.show();
break;
case 3:
theaterChaseRainbow(1,50);
break;
default:
for (int i = 0; i < NUM_LEDS; i++) {
leds[i] = CRGB ( 0, 0, 0);
}
FastLED.show();
break;
}
}
void button_ISR () {
buttonState = digitalRead(SwIn);
//digitalWrite(13, buttonState);
if (buttonState == LOW && buttonState != lastButtonState) {
if (pushCounterz > 3 || pushCounterz < 0) {
Serial.println("Reset to 0: ");
pushCounterz = 0;
} else {
pushCounterz = pushCounterz + 1;
Serial.println("Incerment");
}
Serial.println(pushCounterz);
}
lastButtonState = buttonState;
}
// Theater-style crawling lights with rainbow effect
void theaterChaseRainbow(int cycles, int speed) { // TODO direction, duration
for (int j = 0; j < 256 * cycles; j++) { // cycle all 256 colors in the wheel
for (int q = 0; q < 3; q++) {
for (int i = 0; i < NUM_LEDS; i = i + 3) {
int pos = i + q;
leds[pos] = Wheel( (i + j) % 255); //turn every third pixel on
}
FastLED.show();
if (pushCounterz != 3) return;
delay(speed);
for (int i = 0; i < NUM_LEDS; i = i + 3) {
leds[i + q] = CRGB::Black; //turn every third pixel off
}
}
}
}
CRGB Wheel(byte WheelPos) {
if (WheelPos < 85) {
return CRGB(WheelPos * 3, 255 - WheelPos * 3, 0);
}
else if (WheelPos < 170) {
WheelPos -= 85;
return CRGB(255 - WheelPos * 3, 0, WheelPos * 3);
}
else {
WheelPos -= 170;
return CRGB(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
I have data from a camera in mono 8bit.
This is converted into an int vector using
std::vector<int> grayVector(size);
// convert / copy pointer data into vector: 8 bit
if (static_cast<XI_IMG_FORMAT>(format) == XI_MONO8)
{
quint8* imageIterator = reinterpret_cast<quint8*> (pMemVoid);
for (size_t count = 0; count < size; ++count)
{
grayVector[count] = static_cast<int>(*imageIterator);
imageIterator++;
}
}
Next, I need to convert this into a QImage. If I set the image format to QImage::Format_Mono the app crashes. With QImage::Format_RGB16 I get strippes, and with QImage::Format_RGB32 everything is black.
I would like to know how to do this the best, efficient and correct way?
// convert gray values into QImage data
QImage image = QImage(static_cast<int>(sizeX), static_cat<int>(sizeY), QImage::Format_RGB16);
for ( int y = 0; y < sizeY; ++y )
{
int yoffset = sizeY*y;
QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)) ;
for ( int x = 0; x < sizeX ; ++x )
{
int pos = x + yoffset;
int color = grayVector[static_cast<size_t>(pos)];
*line++ = qRgb(color, color, color);
}
}
The conversion to int is unnecessary and you do it in a very inefficient way; all you need is to use the QImage::Format_Grayscale8 available since Qt 5.5 (mid-2015).
Anyway, what you really want is a way to go from XI_IMG to QImage. The default BP_UNSAFE buffering policy should be adequate - the QImage will do a format conversion, so taking the data from XiApi's internal buffer is OK. Thus the following - all of the conversions are implemented in Qt and are quite efficient - much better than most any naive code.
I didn't check whether some Xi formats may need a BGR swap. If so, then the swap can be set to true in the format selection code and the rest will happen automatically.
See also: xiAPI manual.
static QVector<QRgb> grayScaleColorTable() {
static QVector<QRgb> table;
if (table.isEmpty()) {
table.resize(256);
auto *data = table.data();
for (int i = 0; i < table.size(); ++i)
data[i] = qRgb(i, i, i);
}
return table;
}
constexpr QImage::Format grayScaleFormat() {
return (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
? QImage::Format_Grayscale8
: QImage::Format_Indexed8;
}
QImage convertToImage(const XI_IMG *src, QImage::Format f) {
Q_ASSERT(src->fmt == XI_MONO16);
Q_ASSERT((src->padding_x % 2) == 0);
if (src->fmt != XI_MONO16) return {};
const quint16 *s = static_cast<const quint16*>(src->bp);
const int s_pad = src->padding_x/2;
if (f == QImage::Format_BGR30 ||
f == QImage::Format_A2BGR30_Premultiplied ||
f == QImage::Format_RGB30 ||
f == QImage::Format_A2RGB30_Premultiplied)
{
QImage ret{src->width, src->height, f};
Q_ASSERT((ret->bytesPerLine() % 4) == 0);
const int d_pad = ret->bytesPerLine()/4 - ret->width();
quint32 *d = (quint32*)ret.bits();
if (s_pad == d_pad) {
const int N = (src->width + s_pad) * src->height - s_pad;
for (int i = 0; i < N; ++i) {
quint32 const v = (*s++) >> (16-10);
*d++ = 0xC0000000 | v << 20 | v << 10 | v;
}
} else {
for (int j = 0; j < src->height; ++j) {
for (int i = 0; i < src->width; ++i) {
quint32 const v = (*s++) >> (16-10);
*d++ = 0xC0000000u | v << 20 | v << 10 | v;
}
s += s_pad;
d += d_pad;
}
}
return ret;
}
QImage ret{src->width, src->height, grayScaleFormat()};
const int d_pad = ret->bytesPerLine() - ret->width();
auto *d = ret.bits();
if (s_pad == d_pad) {
const int N = (src->width + s_pad) * src->height - s_pad;
for (int i = 0; i < N; ++i) {
*d++ = (*s++) >> 8;
} else {
for (int j = 0; j < src->height; ++j) {
for (int i = 0; i < src->width; ++i)
*d++ = (*s++) >> 8;
s += s_pad;
d += d_pad;
}
}
return ret;
}
QImage fromXiImg(const XI_IMG *src, QImage::Format dstFormat = QImage::Format_ARGB32Premultiplied) {
Q_ASSERT(src->width > 0 && src->height > 0 && src->padding_x >= 0 && src->bp_size > 0);
Q_ASSERT(dstFormat != QImage::Format_Invalid);
bool swap = false;
int srcPixelBytes = 0;
bool externalConvert = false;
QImage::Format srcFormat = QImage::Format_Invalid;
switch (src->fmt) {
case XI_MONO8:
srcPixelBytes = 1;
srcFormat = grayScaleFormat();
break;
case XI_MONO16:
srcPixelBytes = 2;
externalConvert = true;
break;
case XI_RGB24:
srcPixelBytes = 3;
srcFormat = QImage::Format_RGB888;
break;
case XI_RGB32:
srcPixelBytes = 4;
srcFormat = QImage::Format_RGB32;
break;
};
if (srcFormat == QImage::Format_Invalid && !externalConvert) {
qWarning("Unhandled XI_IMG image format");
return {};
}
Q_ASSERT(srcPixelBytes > 0 && srcPixelBytes <= 4);
int bytesPerLine = src->width * srcPixelBytes + src->padding_x;
if ((bytesPerLine * src->height - src->padding_x) > src->bp_size) {
qWarning("Inconsistent XI_IMG data");
return {};
}
QImage ret;
if (!externalConvert)
ret = QImage{static_cast<const uchar*>(src->bp), src->width, src->height,
bytesPerLine, srcFormat};
else
ret = convertToImage(src, dstFormat);
if (ret.format() == QImage::Format_Indexed8)
ret.setColorTable(grayScaleColorTable());
if (ret.format() != dstFormat)
ret = std::move(ret).convertToFormat(dstFormat);
if (swap)
ret = std::move(ret).rgbSwapped();
if (!ret.isDetached()) // ensure that we don't share XI_IMG's data buffer
ret.detach();
return ret;
}
sketch_Show_The_Hat.ino:77:5: error: expected declaration before ‘}’ token
Can some please help figure where this error is coming from?
Here is my code:
#include "LPD8806.h"
#include "SPI.h"
//#define HOLIDAY_MODE
#define MOMENTARY_SWITCH
// Example to control LPD8806-based RGB LED Modules in a strip!
int dataPin = 2;
int clockPin = 3;
int onOffPin = 4;
// Set the first variable to the NUMBER of pixels. 32 = 32 pixels in a row
// The LED strips are 32 LEDs per meter but you can extend/cut the strip
LPD8806 strip = LPD8806(32, dataPin, clockPin);
void setup() {
// Start up the LED strip
strip.begin();
// Update the strip, to start they are all 'off'
strip.show();
randomSeed(analogRead(A0));
// On/Off switch
pinMode(onOffPin, INPUT_PULLUP);
}
int x = 0;
int dim = 0;
#ifdef MOMENTARY_SWITCH
int tdim = 0;
#endif
void loop();
int r, g, b, y, z;
#ifdef MOMENTARY_SWITCH
} if (digitalRead(onOffPin) == LOW) {
if ((dim == 0) && (tdim == 0)
tdim = !0;
} else if ((dim == 128) && (tdim != 0))
tdim = 0;
if (tdim) {
if (dim < 128) {
dim++;
} else {
}
if (dim > 0) {
dim--;
}
} if (digitalRead(onOffPin) == LOW) {
}
}
} if (dim < 128) {
dim++;
} else {
if (dim > 0) {
dim--;
}
}
#endif
for (int i = 0; i < strip.numPixels(); i++) {
y = (x + (i << 5)) & 511;
#ifdef HOLIDAY_MODE
// Holiday
if (y >= 256) {
y = 511 - y;
}
z = 255 - y;
r = (y <= 128) ? (y - 128) << 1 : 0;
g = (z <= 128) ? (z - 128) << 1 : 0;
b = 0;
# else
// Normal
if (y < 128) {
r = y;
g = 0;
b = 255 - y;
} else if (y < 256) {
r = 127;
g = y - 128;
b = 0;
} else if (y < 384) {
r = 127;
g = 127 - (y - 256);
b = 0;
} else {
r = 127 - (y - 384);
g = 0;
b = y - 384;
}
#endif
if (dim == 0) {
r = 0;
g = 0;
b = 0;
} else if (dim < 128) {
r = (r * dim) / 128;
g = (g * dim) / 128;
b = (b * dim) / 128;
}
strip.setPixelColor(i, r & 127, g & 127, b & 127);
}
strip.show();
x += 2;
x &= 511;
delay(5);
// for(int j=0; j < random(100,200); j++ ) {
// int i = random(0,strip.numPixels()-2) + 1;
// strip.setPixelColor(i-1, 43,54,219);
// strip.setPixelColor(i, 255,255,0);
// strip.setPixelColor(i+1, 255,69,0);
// strip.setPixelColor(i-1, 0,0,255);
// strip.setPixelColor(i, 255,0,0);
// strip.setPixelColor(i+1, 0,255,0);
// strip.show();
// delay(5);
// strip.setPixelColor(i-1, 255,128,0);
// strip.setPixelColor(i-4, 255,128,0);
// strip.setPixelColor(i+1, 4,0,8);
// strip.setPixelColor(i-4, 104,10,80);
// strip.setPixelColor(i-10, 43,54,219);
// strip.setPixelColor(i-30, 255,165,0);
// strip.setPixelColor(i+5, 219,125,45);
// strip.setPixelColor(i, 100,100,127);
// strip.setPixelColor(i+1, 104,100,198);
// strip.show();
// delay(1);
// strip.setPixelColor(i+32, 219,112,147);
//
// strip.setPixelColor(i+1, 255,69,0);
// strip.setPixelColor(i-2, 255,127,75);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i+5, 255,128,0);
// strip.setPixelColor(i+2, 10,227,100);
// strip.show();
// delay(0);
// strip.setPixelColor(i-1, 255,69,0);
// strip.setPixelColor(i, 255,69,0);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i-12, 148,0,211);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i-3, 148,0,211);
// strip.setPixelColor(i-1, 148,0,211);
// strip.setPixelColor(i-6, 255,69,0);
// strip.setPixelColor(i+2, 148,0,211);
// strip.show();
// delay(random(10,100));
// }
// for(int j=0; j < random(10,15); j++ ) {
// for (int i=32; i < strip.numPixels(); i++) {
// strip.setPixelColor(i, 0,0,255);
// }
// strip.show();
// delay(15);
// for (int i=2; i < strip.numPixels(); i++) {
// strip.setPixelColor(i, 255,110,0);
// }
// strip.show();
// delay(15);
// }
First of all I have corrected your formatting with an edit. The error is saying that in 77. line 5. character there is a mistake related with } character. The mistake is you did not declared any { before }.
When I tried to correct your formatting (on my version of your file) 77. line is an empty line. But the problem is probably in 76. line } if (digitalRead(onOffPin) == LOW) { so try to remove { character. I mean try to replace the line with if (digitalRead(onOffPin) == LOW) { and let's see the error persists or not.
Curly Braces: https://www.arduino.cc/en/reference/braces
Reminding: Please always pay attention to your formatting on StackOverflow while asking a question. Formatting the text especially in the question is the key to properly interchange the knowledge. While sharing one of your code file in here, formatting is a necessity. Because if you did not format your text (your codes actually) we can not test them.
I would like to display image in qt window , so I used Qlabel->setpixmap
but how can I convert from IPLImage to QImage to display it in the label??
I found the follwing function to convert it but I did not know how to use it in call statement
QImage *IplImageToQImage(const IplImage * iplImage, uchar **data, double mini, double maxi)
{
uchar *qImageBuffer = NULL;
int width = iplImage->width;
int widthStep = iplImage->widthStep;
int height = iplImage->height;
switch (iplImage->depth)
{
case IPL_DEPTH_8U:
if (iplImage->nChannels == 1)
{
// OpenCV image is stored with one byte grey pixel. We convert it
// to an 8 bit depth QImage.
//
qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
const uchar *iplImagePtr = (const uchar *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
// Copy line by line
memcpy(QImagePtr, iplImagePtr, width);
QImagePtr += width;
iplImagePtr += widthStep;
}
}
else if (iplImage->nChannels == 3)
{
/* OpenCV image is stored with 3 byte color pixels (3 channels).
We convert it to a 32 bit depth QImage.
*/
qImageBuffer = (uchar *) malloc(width*height*4*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
const uchar *iplImagePtr = (const uchar *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// We cannot help but copy manually.
QImagePtr[0] = iplImagePtr[0];
QImagePtr[1] = iplImagePtr[1];
QImagePtr[2] = iplImagePtr[2];
QImagePtr[3] = 0;
QImagePtr += 4;
iplImagePtr += 3;
}
iplImagePtr += widthStep-3*width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=8U and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_16U:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with 2 bytes grey pixel. We convert it
to an 8 bit depth QImage.
*/
qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
//const uint16_t *iplImagePtr = (const uint16_t *);
const unsigned int *iplImagePtr = (const unsigned int *)iplImage->imageData;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// We take only the highest part of the 16 bit value. It is
//similar to dividing by 256.
*QImagePtr++ = ((*iplImagePtr++) >> 8);
}
iplImagePtr += widthStep/sizeof(unsigned int)-width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=16U and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_32F:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with float (4 bytes) grey pixel. We
convert it to an 8 bit depth QImage.
*/
qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
const float *iplImagePtr = (const float *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uchar p;
float pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);
if (pf < 0) p = 0;
else if (pf > 255) p = 255;
else p = (uchar) pf;
*QImagePtr++ = p;
}
iplImagePtr += widthStep/sizeof(float)-width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=32F and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_64F:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with double (8 bytes) grey pixel. We
convert it to an 8 bit depth QImage.
*/
qImageBuffer = (uchar *) malloc(width*height*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
const double *iplImagePtr = (const double *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uchar p;
double pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);
if (pf < 0) p = 0;
else if (pf > 255) p = 255;
else p = (uchar) pf;
*QImagePtr++ = p;
}
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=64F and %d channels\n", iplImage->nChannels);
}
break;
default:
qDebug("IplImageToQImage: image format is not supported : depth=%d and %d channels\n", iplImage->depth, iplImage->nChannels);
}
QImage *qImage;
QVector<QRgb> vcolorTable;
if (iplImage->nChannels == 1)
{
// We should check who is going to destroy this allocation.
QRgb *colorTable = new QRgb[256];
for (int i = 0; i < 256; i++)
{
colorTable[i] = qRgb(i, i, i);
vcolorTable[i] = colorTable[i];
}
qImage = new QImage(qImageBuffer, width, height, QImage::Format_Indexed8);
qImage->setColorTable(vcolorTable);
}
else
{
qImage = new QImage(qImageBuffer, width, height, QImage::Format_RGB32);
}
*data = qImageBuffer;
return qImage;
}
The parameter was:
const IplImage * iplImage, uchar **data, double mini, double maxi
what are data,mini,max? how can I get it from my IPLImage to use it in call statement?
Thanks alot :)
Looks like data is not used by the code, and mini and maxi are used for converting floating point values that certain image formats use to integer values in the range 0-255.
I'd try using NULL for data. mini and maxi really depend on the image data, and I don't know what reasonable ranges are. But if your IplImage is not stored as floating point values then these values shouldn't make any difference.
You can simply create a QImage where the data is owned by something else (eg the IPLImage) using the QImage(data,widht,height,format) and data is the IPLImage data ptr as long as the format isthe samein both QImage and IPLImage (eg RGB888 = 8U_C3)
I have found some bugs in the code.... maybe there are still more bugs in it but for now it looks fine for me. QImage with Format_Index8 needs sometimes (depending on the image resolution....) 2 byte added on the right side (don t know why but it seems like to be like this).
Here is the new adapted code
QImage *IplImageToQImage(const IplImage * iplImage, uchar **data, double mini, double maxi)
{
uchar *qImageBuffer = NULL;
int width = iplImage->width;
int widthStep = iplImage->widthStep;
int height = iplImage->height;
QImage *qImage;
switch (iplImage->depth)
{
case IPL_DEPTH_8U:
if (iplImage->nChannels == 1)
{
// OpenCV image is stored with one byte grey pixel. We convert it
// to an 8 bit depth QImage.
qImage = new QImage(width,height,QImage::Format_Indexed8);
uchar *QImagePtr = qImage->scanLine(0);
qImageBuffer = qImage->scanLine(0);
const uchar *iplImagePtr = (const uchar *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
// Copy line by line
QImagePtr = qImage->scanLine(y);
memcpy(QImagePtr, iplImagePtr, width);
iplImagePtr += widthStep;
}
/*
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// We take only the highest part of the 16 bit value. It is
//similar to dividing by 256.
//*QImagePtr++ = ((*iplImagePtr++) >> 8);
*QImagePtr = *iplImagePtr;
QImagePtr++;
iplImagePtr++;
}
iplImagePtr += widthStep/sizeof(uchar)-width;
}*/
}
else if (iplImage->nChannels == 3)
{
/* OpenCV image is stored with 3 byte color pixels (3 channels).
We convert it to a 32 bit depth QImage.
*/
qImageBuffer = (uchar *) malloc(width*height*4*sizeof(uchar));
uchar *QImagePtr = qImageBuffer;
const uchar *iplImagePtr = (const uchar *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
// We cannot help but copy manually.
QImagePtr[0] = iplImagePtr[0];
QImagePtr[1] = iplImagePtr[1];
QImagePtr[2] = iplImagePtr[2];
QImagePtr[3] = 0;
QImagePtr += 4;
iplImagePtr += 3;
}
iplImagePtr += widthStep-3*width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=8U and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_16U:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with 2 bytes grey pixel. We convert it
to an 8 bit depth QImage.
*/
qImage = new QImage(width,height,QImage::Format_Indexed8);
uchar *QImagePtr = qImage->scanLine(0);
qImageBuffer = qImage->scanLine(0);
//const uint16_t *iplImagePtr = (const uint16_t *);
const unsigned short *iplImagePtr = (const unsigned short *)iplImage->imageData;
for (int y = 0; y < height; y++)
{
QImagePtr = qImage->scanLine(y);
for (int x = 0; x < width; x++)
{
// We take only the highest part of the 16 bit value. It is
//similar to dividing by 256.
//*QImagePtr++ = ((*iplImagePtr++) >> 8);
//change here 16 bit could be everything !! set max min to your desire
*QImagePtr = 255*(((*iplImagePtr) - mini) / (maxi - mini));
QImagePtr++;
iplImagePtr++;
}
iplImagePtr += widthStep/sizeof(unsigned short)-width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=16U and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_32F:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with float (4 bytes) grey pixel. We
convert it to an 8 bit depth QImage.
*/
qImage = new QImage(width,height,QImage::Format_Indexed8);
uchar *QImagePtr = qImage->scanLine(0);
qImageBuffer = qImage->scanLine(0);
const float *iplImagePtr = (const float *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
QImagePtr = qImage->scanLine(y);
for (int x = 0; x < width; x++)
{
uchar p;
float pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);
if (pf < 0) p = 0;
else if (pf > 255) p = 255;
else p = (uchar) pf;
*QImagePtr++ = p;
}
iplImagePtr += widthStep/sizeof(float)-width;
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=32F and %d channels\n", iplImage->nChannels);
}
break;
case IPL_DEPTH_64F:
if (iplImage->nChannels == 1)
{
/* OpenCV image is stored with double (8 bytes) grey pixel. We
convert it to an 8 bit depth QImage.
*/
qImage = new QImage(width,height,QImage::Format_Indexed8);
uchar *QImagePtr = qImage->scanLine(0);
qImageBuffer = qImage->scanLine(0);
const double *iplImagePtr = (const double *) iplImage->imageData;
for (int y = 0; y < height; y++)
{
QImagePtr = qImage->scanLine(y);
for (int x = 0; x < width; x++)
{
uchar p;
double pf = 255 * ((*iplImagePtr++) - mini) / (maxi - mini);
if (pf < 0) p = 0;
else if (pf > 255) p = 255;
else p = (uchar) pf;
*QImagePtr++ = p;
}
}
}
else
{
qDebug("IplImageToQImage: image format is not supported : depth=64F and %d channels\n", iplImage->nChannels);
}
break;
default:
qDebug("IplImageToQImage: image format is not supported : depth=%d and %d channels\n", iplImage->depth, iplImage->nChannels);
}
QVector<QRgb> vcolorTable;
if (iplImage->nChannels == 1)
{
// We should check who is going to destroy this allocation.
vcolorTable.resize(256);
for (int i = 0; i < 256; i++)
{
vcolorTable[i] = qRgb(i, i, i);
}
//Qt vector is difficult to use... start with std to qvector
//here I allocate QImage using qt constructor (Forma_Indexed8 adds sometimes 2 bytes on the right side !!! o.O not specified nowhere !!!)
//qImage = new QImage(tmpImg->scanLine(0), width, height, QImage::Format_Indexed8);
qImage->setColorTable(vcolorTable);
}
else
{
qImage = new QImage(qImageBuffer, width, height, QImage::Format_RGB32);
}
*data = qImageBuffer;
return qImage;
}
I don t know if 3 channels have also the same bug but I hope not