How to remove EXIF data without recompressing the JPEG? - unix

I want to remove the EXIF information (including thumbnail, metadata, camera info... everything!) from JPEG files, but I don't want to recompress it, as recompressing the JPEG will degrade the quality, as well as usually increasing the file size.
I'm looking for a Unix/Linux solution, even better if using the command-line. If possible, using ImageMagick (convert tool). If that's not possible, a small Python, Perl, PHP (or other common language on Linux) script would be ok.
There is a similar question, but related to .NET.

exiftool does the job for me, it's written in perl so should work for you on any o/s
https://exiftool.org/
usage :
exiftool -all= image.jpg
UPDATED - as PeterCo explains below this will remove ALL of the tags. if you just want to remove the EXIF tags then you should use
exiftool -EXIF= image.jpg

With imagemagick:
convert <input file> -strip <output file>

ImageMagick has the -strip parameter, but it recompresses the image before saving. Thus, this parameter is useless for my need.
This topic from ImageMagick forum explains that there is no support for JPEG lossless operations in ImageMagick (whenever this changes, please post a comment with a link!), and suggests using jpegtran (from libjpeg):
jpegtran -copy none -progressive image.jpg > newimage.jpg
jpegtran -copy none -progressive -outfile newimage.jpg image.jpg
(If you are unsure about me answering my own question, read this and this and this)

You might also want to look into Exiv2 -- it's really fast (C++ and no recompression), it's command line, and it also provides a library for EXIF manipulation you can link against. I don't know how many Linux distros make it available, but in CentOS it's currently available in the base repo.
Usage:
exiv2 rm image.jpg

I'd propose jhead:
man jhead
jhead -purejpg image.jpg
Only 123Kb on debian/ubuntu, it's fast, and it only touches EXIF keeping the image itself intact. Note that you need to create a copy if you want to preserve the original file with EXIF in it.

I recently undertook this project in C. The code below does the following:
1) Gets the current orientation of the image.
2) Removes all data contained in APP1 (Exif data) and APP2 (Flashpix data) by blanking.
3) Recreates the APP1 orientation marker and sets it to the original value.
4) Finds the first EOI marker (End of Image) and truncates the file if nessasary.
Some things to note first are:
1) This program is used for my Nikon camera. Nikon's JPEG format adds somthing to the very end of each file it creates. They encode this data on to the end of the image file by creating a second EOI marker. Normally image programs read up to the first EOI marker found. Nikon has information after this which my program truncates.
2) Because this is for Nikon format, it assumes big endian byte order. If your image file uses little endian, some adjustments need to be made.
3) When trying to use ImageMagick to strip exif data, I noticed that I ended up with a larger file than what I started with. This leads me to believe that Imagemagick is encoding the data you want stripped away, and is storing it somewhere else in the file. Call me old fashioned, but when I remove something from a file, I want a file size the be smaller if not the same size. Any other results suggest data mining.
And here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>
// Declare constants.
#define COMMAND_SIZE 500
#define RETURN_SUCCESS 1
#define RETURN_FAILURE 0
#define WORD_SIZE 15
int check_file_jpg (void);
int check_file_path (char *file);
int get_marker (void);
char * ltoa (long num);
void process_image (char *file);
// Declare global variables.
FILE *fp;
int orientation;
char *program_name;
int main (int argc, char *argv[])
{
// Set program name for error reporting.
program_name = basename(argv[0]);
// Check for at least one argument.
if(argc < 2)
{
fprintf(stderr, "usage: %s IMAGE_FILE...\n", program_name);
exit(EXIT_FAILURE);
}
// Process all arguments.
for(int x = 1; x < argc; x++)
process_image(argv[x]);
exit(EXIT_SUCCESS);
}
void process_image (char *file)
{
char command[COMMAND_SIZE + 1];
// Check that file exists.
if(check_file_path(file) == RETURN_FAILURE)
return;
// Check that file is an actual JPEG file.
if(check_file_jpg() == RETURN_FAILURE)
{
fclose(fp);
return;
}
// Jump to orientation marker and store value.
fseek(fp, 55, SEEK_SET);
orientation = fgetc(fp);
// Recreate the APP1 marker with just the orientation tag listed.
fseek(fp, 21, SEEK_SET);
fputc(1, fp);
fputc(1, fp);
fputc(18, fp);
fputc(0, fp);
fputc(3, fp);
fputc(0, fp);
fputc(0, fp);
fputc(0, fp);
fputc(1, fp);
fputc(0, fp);
fputc(orientation, fp);
// Blank the rest of the APP1 marker with '\0'.
for(int x = 0; x < 65506; x++)
fputc(0, fp);
// Blank the second APP1 marker with '\0'.
fseek(fp, 4, SEEK_CUR);
for(int x = 0; x < 2044; x++)
fputc(0, fp);
// Blank the APP2 marker with '\0'.
fseek(fp, 4, SEEK_CUR);
for(int x = 0; x < 4092; x++)
fputc(0, fp);
// Jump the the SOS marker.
fseek(fp, 72255, SEEK_SET);
while(1)
{
// Truncate the file once the first EOI marker is found.
if(fgetc(fp) == 255 && fgetc(fp) == 217)
{
strcpy(command, "truncate -s ");
strcat(command, ltoa(ftell(fp)));
strcat(command, " ");
strcat(command, file);
fclose(fp);
system(command);
break;
}
}
}
int get_marker (void)
{
int c;
// Check to make sure marker starts with 0xFF.
if((c = fgetc(fp)) != 0xFF)
{
fprintf(stderr, "%s: get_marker: invalid marker start (should be FF, is %2X)\n", program_name, c);
return(RETURN_FAILURE);
}
// Return the next character.
return(fgetc(fp));
}
int check_file_jpg (void)
{
// Check if marker is 0xD8.
if(get_marker() != 0xD8)
{
fprintf(stderr, "%s: check_file_jpg: not a valid jpeg image\n", program_name);
return(RETURN_FAILURE);
}
return(RETURN_SUCCESS);
}
int check_file_path (char *file)
{
// Open file.
if((fp = fopen(file, "rb+")) == NULL)
{
fprintf(stderr, "%s: check_file_path: fopen failed (%s) (%s)\n", program_name, strerror(errno), file);
return(RETURN_FAILURE);
}
return(RETURN_SUCCESS);
}
char * ltoa (long num)
{
// Declare variables.
int ret;
int x = 1;
int y = 0;
static char temp[WORD_SIZE + 1];
static char word[WORD_SIZE + 1];
// Stop buffer overflow.
temp[0] = '\0';
// Keep processing until value is zero.
while(num > 0)
{
ret = num % 10;
temp[x++] = 48 + ret;
num /= 10;
}
// Reverse the word.
while(y < x)
{
word[y] = temp[x - y - 1];
y++;
}
return word;
}
Hope this helps someone!

We used this to remove latitude data from TIFF file:
exiv2 mo -M"del Exif.GPSInfo.GPSLatitude" IMG.TIF
where you can use exiv2 -pa IMG.TIF to list all metadata.

Hint for convenience: If you are on Windows, you can apply a REG file to the registry, to install an entry in the context menu, so you can easily remove metadata by right-clicking the file and selecting the command.
For example (remember to edit the paths to point to where the executables are installed on your computer):
For JPEG,JPG,JPE,JFIF files: command "Remove metadata"
(using ExifTool, preserves original file as backup)
exiftool -all= image.jpg
JPG-RemoveExif.reg
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata]
#="Remove metadata"
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata\command]
#="\"C:\\Path to\\exiftool.exe\" -all= \"%1\""
[HKEY_CURRENT_USER\Software\Classes\jpegfile\shell\RemoveMetadata]
"Icon"="C:\\Path to\\exiftool.exe,0"
For PNG files: command "Convert to minified PNG"
(using ImageMagick, changes data overwriting original file)
convert -background none -strip -set filename:n "%t" image.png "%[filename:n].png"
PNG-Minify.reg
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG]
#="Convert to minified PNG"
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG\command]
#="\"C:\\Path to\\convert.exe\" -background none -strip -set filename:n \"%%t\" \"%1\" \"%%[filename:n].png\""
[HKEY_CURRENT_USER\Software\Classes\pngfile\shell\ConvertToMinifiedPNG]
"Icon"="C:\\Path to\\convert.exe,0"
Related: convert PNGs to ICO in context menu.

For lossless EXIF strip you can use libexif, which is available with cygwin. Remove both EXIF and thumbnail to anonymize an image:
$ exif --remove --tag=0 --remove-thumbnail exif.jpg -o anonymized.jpg
Drag-n-drop .bat file for use with cygwin:
#ECHO OFF
exif --remove --tag=0 --remove-thumbnail %~1

If you already use jpegoptim you can use it to remove the exif, too.
jpegoptim -s *

Related

arduino, setup ethernet & network using data from SD config file

Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)
I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.
I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:
mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;
and the code that I use to read is:
byte myMAC[6];
byte myIP[4];
File fset;
fset = SD.open("config.txt");
if (fset){
char ff[40];
while (fset.available()>1){
bool eol=false;
for (int i=0; !eol;i++){
ff[i]=fset.read();
if (ff[i]=='\n'){
eol=true;
}
}
String par="";
bool DONE=false;
for (int i=0; !DONE;i++){
par+=ff[i];
if (ff[i]== '='){DONE=true;}
}
String pval="";
DONE=false;
//------------------------
if (par=="ip=" ){
int x=0;
while(!DONE){
for(int i=3;i<=i+21;i++){
if(ff[i]=='.'){
myIP[x]=pval.toInt();
x++;
i++;
pval="";
}
else if(ff[i]==';' || i>20){
myIP[x]=pval.toInt();
DONE=true;
break;
}
pval+=ff[i];
}
}
}
} //while (fset.available()>1)
} //if (fset)
I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!
Best regards
Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
line one compares the first 4 characters and if it is 0 (meaning its a fit)
line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
The file structure should be with no ; as you would have to parse ; and \n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'\0'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'\0'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'\0'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'\0'}; // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
int postport=0;
// .... you can easyly implement for all other data you want to store/retrieve
// Counts the lines of a file
uint16_t countLines() {
uint16_t currentLineCount = 0;
File cfgFile = SD.open(configurationFilePath, "r");
if (!cfgFile) {
Serial.println(F("Config file open failed on read"));
} else {
while (cfgFile.available()) {
/** Lets read line by line from the file */
if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
}
cfgFile.close();
}
return currentLineCount;
}
//Load the config file from SD/SPIFFS/LittleFS
bool loadConfigFile() {
uint16_t lineCounter = countLines();
if (lineCounter <= 0) {
Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
return false;
}
else {
File cfgFile = SD.open(configurationFilePath, "r");
while (cfgFile.available()) {
strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str())); // normaly you use new line, we copy one line at a time
// Serial.println(cfgLine); /** Printing for debuging purpose */
while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
loadSingleCfgLine();
}
}
cfgFile.close();
Serial.println(F("[Success] Loaded config !"));
return true;
}
}
//Load the data of a single line into a char array
void loadSingleCfgLine() {
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
if (strncmp(cfgLine, "ip=", 3) == 0) {
strcpy (ipAddr, cfgLine + 3);
}
if (strncmp(cfgLine, "postport=", 9) == 0) {
strcpy (numBuffer, cfgLine + 9);
postport = atoi(numBuffer); // One extra step to convert to int
}
// ... easy to implement for all other data
}
I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.

Decryption of .png and .jpg files

I'm trying to modify graphic assets of the software I'm using (for aesthetic puroposess, I guess it's hard to do something harmful with graphic assets) but developer encrypted them. I'm not sure why he decided to do that since I used and modified a bunch of similar softwares and developers of those didn't bother (as I can see no reason why encrypting those assets would be necessary).
So anyway here are examples of those encrypted graphic assets:
http://www.mediafire.com/view/sx2yc0w5wkr9m2h/avatars_50-alpha.jpg
http://www.mediafire.com/download/i4fc52438hkp55l/avatars_80.png
Is there a way of decrypting those? If so how should I go about this?
The header "CF10" seems to be a privately added signature to signify the rest of the file is "encoded". This is a very simple XOR encoding: xor 8Dh was the first value I tried, and I got it right first time too. The reasoning behind trying that as the first value is that the value 8D occurs very frequently in the first 100 bytes-or-so, where they typically could be lots of zeroes.
"Decrypting" is thus very straightforward: if a file starts with the four bytes CF10, remove them and apply xor 8Dh on the rest of the file. Decoding the files show the first "JPG" is in fact a tiny PNG image (and not a very interesting one to boot), the second is indeed a PNG file:
The file extension may or may not be the original file extension; the one sample called ".jpg" is in fact also a PNG file, as can be seen by its header signature.
The following quick-and-dirty C source will decode the images. The same program can be adjusted to encode them as well, because the xor operation is exactly the same. The only thing needed is add a bit of logic flow:
read the first 4 bytes (maximum) of the input file and test if this forms the string CF10
if not, the file is not encoded:
a. write CF10 to the output file
b. encode the image by applying xor 8Dh on each byte
if so,
b. decode the image by applying xor 8Dh on each byte.
As you can see, there is no "3a" and both "b" steps are the same.
#include <stdio.h>
#include <string.h>
#ifndef MAX_PATH
#define MAX_PATH 256
#endif
#define INPUTPATH "c:\\documents"
#define OUTPUTPATH ""
int main (int argc, char **argv)
{
FILE *inp, *outp;
int i, encode_flag = 0;
char filename_buffer[MAX_PATH];
char sig[] = "CF10", *ptr;
if (argc != 3)
{
printf ("usage: decode [input] [output]\n");
return -1;
}
filename_buffer[0] = 0;
if (!strchr(argv[1], '/') && !strchr(argv[1], 92) && !strchr(argv[1], ':'))
strcpy (filename_buffer, INPUTPATH);
strcat (filename_buffer, argv[1]);
inp = fopen (filename_buffer, "rb");
if (inp == NULL)
{
printf ("bad input file '%s'\n", filename_buffer);
return -2;
}
ptr = sig;
while (*ptr)
{
i = fgetc (inp);
if (*ptr != i)
{
encode_flag = 1;
break;
}
ptr++;
}
if (encode_flag)
{
/* rewind file because we already read some bytes */
fseek (inp, 0, SEEK_SET);
printf ("encoding input file: '%s'\n", filename_buffer);
} else
printf ("decoding input file: '%s'\n", filename_buffer);
filename_buffer[0] = 0;
if (!strchr(argv[2], '/') && !strchr(argv[2], 92) && !strchr(argv[2], ':'))
strcpy (filename_buffer, OUTPUTPATH);
strcat (filename_buffer, argv[2]);
outp = fopen (filename_buffer, "wb");
if (outp == NULL)
{
printf ("bad output file '%s'\n", filename_buffer);
return -2;
}
printf ("output file: '%s'\n", filename_buffer);
if (encode_flag)
fwrite (sig, 1, 4, outp);
do
{
i = fgetc(inp);
if (i != EOF)
fputc (i ^ 0x8d, outp);
} while (i != EOF);
fclose (inp);
fclose (outp);
printf ("all done. bye bye\n");
return 0;
}
Ok so when it comes to practical usage of the code provided by #Jongware that was unclear to me - I figured it out with some help:)
I compiled the code using Visual Studio (you can find guides on how to do that, basically create new Visual C++ project and in Project -> Project Propeties choose C/C++ -> All options and Compile as C Code (/TC)).
Then I opened program in command prompt using parameter "program encrypted_file decrypted_file".
Thanks a lot for help Jongware!

variable in a header file shared between different projects

I have a solution which includes three projects. one is creating static library i.e .lib file. It contains one header file main.h and one main.cpp file. cpp file contains the definition of functions of header file.
second project is .exe project which includes the header file main.h and calls a function of header file.
third project is also a .exe project which includes the header file and uses a variable flag of header file.
Now both .exe projects are creating different instance of the variable. But I want to share same instance of the variable between the projects dynamically. as I have to map the value generated by one project into other project at the same instant.
Please help me as I am nearing my project deadline.
Thanks for the help.
Here are some part of the code.
main.cpp and main.h are files of .lib project
main.h
extern int flag;
extern int detect1(void);
main.cpp
#include<stdio.h>
#include"main.h"
#include <Windows.h>
#include <ShellAPI.h>
using namespace std;
using namespace cv;
int flag=0;
int detect1(void)
{
int Cx=0,Cy=0,Kx=20,Ky=20,Sx=0,Sy=0,j=0;
//create the cascade classifier object used for the face detection
CascadeClassifier face_cascade;
//use the haarcascade_frontalface_alt.xml library
face_cascade.load("E:\\haarcascade_frontalface_alt.xml");
//System::DateTime now = System::DateTime::Now;
//cout << now.Hour;
//WinExec("E:\\FallingBlock\\FallingBlock\\FallingBlock\\bin\\x86\\Debug\\FallingBlock.exe",SW_SHOW);
//setup video capture device and link it to the first capture device
VideoCapture captureDevice;
captureDevice.open(0);
//setup image files used in the capture process
Mat captureFrame;
Mat grayscaleFrame;
//create a window to present the results
namedWindow("capture", 1);
//create a loop to capture and find faces
while(true)
{
//capture a new image frame
captureDevice>>captureFrame;
//convert captured image to gray scale and equalize
cvtColor(captureFrame, grayscaleFrame, CV_BGR2GRAY);
equalizeHist(grayscaleFrame, grayscaleFrame);
//create a vector array to store the face found
std::vector<Rect> faces;
//find faces and store them in the vector array
face_cascade.detectMultiScale(grayscaleFrame, faces, 1.1, 3, CV_HAAR_FIND_BIGGEST_OBJECT|CV_HAAR_SCALE_IMAGE, Size(30,30));
//draw a rectangle for all found faces in the vector array on the original image
for(unsigned int i = 0; i < faces.size(); i++)
{
Point pt1(faces[i].x + faces[i].width, faces[i].y + faces[i].height);
Point pt2(faces[i].x, faces[i].y);
rectangle(captureFrame, pt1, pt2, cvScalar(0, 255, 0, 0), 1, 8, 0);
if(faces.size()>=1)
j++;
Cx = faces[i].x + (faces[i].width / 2);
Cy = faces[i].y + (faces[i].height / 2);
if(j==1)
{
Sx=Cx;
Sy=Cy;
flag=0;
}
}
if(Cx-Sx > Kx)
{
flag = 1;
printf("%d",flag);
}
else
{
if(Cx-Sx < -Kx)
{
flag = 2;
printf("%d",flag);
//update(2);
}
else
{
if(Cy-Sy > Ky)
{
flag = 3;
printf("%d",flag);
//update(3);
}
else
{
if(Cy-Sy < -Ky)
{
flag = 4;
printf("%d",flag);
//update(4);
}
else
if(abs(Cx-Sx) < Kx && abs(Cy-Sy)<Ky)
{
flag = 0;
printf("%d",flag);
//update(0);
}
}
}
}
2nd project's code
face.cpp
#include"main.h"
#include<stdio.h>
int main()
{
detect1();
}
3rd project's code
tetris.cpp
#include"main.h"
int key;
key = flag;
if(key==0)
{
MessageBox(hwnd,"Space2","TetRiX",0);
}
if(key==4)
{
tetris.handleInput(1);
tetris.drawScreen(2);
//MessageBox(hwnd,"Space2","TetRiX",0);
}
You need to look up how to do inter-process communication in the operating system under which your applications will run. (At this point I assume that the processes are running on the same computer.) It looks like you're using Windows (based on seeing a call to "MessageBox") so the simplest means would be for both processes to use RegisterWindowMessage create a commonly-understood message value, and then send the data via LPARAM using either PostMessage or SendMessage. (You'll need each of them to get the window handle of the other, which is reasonably easy.) You'll want to have some sort of exclusion mechanism (mutex or critical section) in both processes to ensure that the shared value can't be read and written at the same time. If both processes can do the "change and exchange" then you'll have an interesting problem to solve if both try to do that at the same time, because you'll have to deal with the possibility of deadlocks over that shared value.
You can also use shared memory, but that's a bit more involved.
If the processes are on different computers you'll need to do it via TCP/IP or a protocol on top of TCP/IP. You could use a pub-sub arrangement--or any number of things. Without an understanding of exactly what you're trying to accomplish, it's difficult to know what to recommend.
(For the record, there is almost no way in a multi-process/multi-threaded O/S to share something "at the same instant." You can get arbitrarily close, but computers don't work like that.)
Given the level of difficulty involved, is there some other design that might make this cleaner? Why do these processes have to exchange information this way? Must it be done using separate processes?

cv::Mat to QImage conversion

I've found very similiar topic: how to convert an opencv cv::Mat to qimage , but it does not solve my problem.
I have function converting cv::Mat to QImage
QImage cvMatToQImg(cv::Mat& mat)
{
cv::Mat rgb;
if(mat.channels()==1)
{
cv::cvtColor(mat,rgb,CV_GRAY2BGR);
cv::cvtColor(rgb,rgb,CV_BGR2BGRA);
QImage temp = QImage((unsigned char*)(rgb.data), rgb.cols,
rgb.rows,QImage::Format_ARGB32 );
QImage returnImage = temp.copy();
return returnImage;
}
And it's works for my but I want to make it more efficient.
First: why changing 2 cvtColor functions with:
cv::cvtColor(mat,rgb,CV_GRAY2BGRA)
fails on
QImage returnImage = temp.copy()
with segfault.
Then how to eliminate copying of QImage. When I simply return temp image, I'm getting segfault.
Any other optimalizations can be done there? It's very often used function so I want to make it as fast as possible.
Your solution to the problem is not efficient, in particular it is less efficient then the code I posted on the other question you link to.
Your problem is that you have to convert from grayscale to color, or RGBA. As soon as you need this conversation, naturally a copy of the data is needed.
My solution does the conversion between grayscale and color, as well as between cv::Mat and QImage at the same time. That's why it is the most efficient you can get.
In your solution, you first try to convert and then want to build QImage around OpenCV data directly to spare a second copy. But, the data you point to is temporary. As soon as you leave the function, the cv::Mat free's its associated memory and that's why it is not valid anymore also within the QImage. You could manually increase the reference counter of the cv::Mat beforehand, but that opens the door for a memory leak afterwards.
In the end, you attempt a dirty solution to a problem better solved in a clean fashion.
It may be easiest to roll your own solution. Below is the current OpenCV implementation for going from gray to RGBA format:
template<typename _Tp>
struct Gray2RGB
{
typedef _Tp channel_type;
Gray2RGB(int _dstcn) : dstcn(_dstcn) {}
void operator()(const _Tp* src, _Tp* dst, int n) const
{
if( dstcn == 3 )
for( int i = 0; i < n; i++, dst += 3 )
{
dst[0] = dst[1] = dst[2] = src[i];
}
else
{
_Tp alpha = ColorChannel<_Tp>::max();
for( int i = 0; i < n; i++, dst += 4 )
{
dst[0] = dst[1] = dst[2] = src[i];
dst[3] = alpha;
}
}
}
int dstcn;
};
Here is where the actual cvtColor call occurs:
case CV_GRAY2BGR: case CV_GRAY2BGRA:
if( dcn <= 0 ) dcn = 3;
CV_Assert( scn == 1 && (dcn == 3 || dcn == 4));
_dst.create(sz, CV_MAKETYPE(depth, dcn));
dst = _dst.getMat();
if( depth == CV_8U )
CvtColorLoop(src, dst, Gray2RGB<uchar>(dcn));
This code is contained in the color.cpp file in the imgproc library.
As you can see, since you are not setting the dstCn parameter in your cvtColor calls, it defaults to dcn = 3. To go straight from gray to BGRA, set dstCn to 4. Since OpenCV's default color order is BGR, you'll still need to swap the color channels for it to look right (assuming you get your image data from an OpenCV function). So, it may be worth it to implement your own converter possibly following the above example, or using ypnos answer here.
Also, have a look at my other answer involving how to integrate OpenCV with Qt.
The problem is that both the cv::Mat and QImage data isn't necessarily contiguous.
New data rows in opencv start on a 32bit boundary (not sure about QImage - I think it's system dependant) so you can't copy a memeory block unless your rows happen to be exact multiples of 4bytes
See How to output this 24 bit image in Qt

how to print a uint16 monochrome image in Qt?

I'm trying to print a image from a Dicom file. I pass the raw data to a convertToFormat_RGB888 function. As far as I know, Qt can't handle monochrome 16 bits images.
Here's the original image (converted to jpg here):
http://imageshack.us/photo/my-images/839/16bitc.jpg/
bool convertToFormat_RGB888(gdcm::Image const & gimage, char *buffer, QImage* &imageQt)
Inside this function, I get inside this...
...
else if (gimage.GetPixelFormat() == gdcm::PixelFormat::UINT16)
{
short *buffer16 = (short*)buffer;
unsigned char *ubuffer = new unsigned char[dimX*dimY*3];
unsigned char *pubuffer = ubuffer;
for (unsigned int i = 0; i < dimX*dimY; i++)
{
*pubuffer++ = *buffer16;
*pubuffer++ = *buffer16;
*pubuffer++ = *buffer16;
buffer16++;
}
imageQt = new QImage(ubuffer, dimX, dimY, QImage::Format_RGB888);
...
This code is a little adaptation from here:
gdcm.sourceforge.net/2.0/html/ConvertToQImage_8cxx-example.html
But the original one I got a execution error. Using mine at least I get an image, but it's not the same.
Here is the new image (converted to jpg here):
http://imageshack.us/photo/my-images/204/8bitz.jpg/
What am I doing wrong?
Thanks.
Try to get values of pixels from buffer manually and pass it to QImage::setPixel. It can be simplier.
You are assigning 16-bit integer to 8-bit variables here:
*pubuffer++ = *buffer16;
The result is undefined and most compilers just move the lower 8 bits to the destination. You want the upper 8 bits
*pubuffer++ = (*buffer16) >> 8;
The other issue is endianness. Depending to the endianness of the source data, you may need to call one of the QtEndian functions.
Lastly, you don't really need to use any of the 32 or 24-bit Qt image formats. Use 8-bit QImage::Format_Indexed8 and set the color table to grays.

Resources