-1

This is not a Most Vexing Parse problem, because doing the following will also result in linker errors although there's no ambiguity:

QImage baseline("C:\img1.png");
QImage test_image("C:\img1.png");

My application gets 2 command line arguments which are paths to 2 different images.

I would like to convert them to QImage after so I can compare them using the == operator Qt provides.

I have tried the following ways to go about it but I get linker issues every time:

Attempt 1:

    QImage baseline(argv[1]);
    QImage test_image(argv[2]);

Attempt 2 (convert to Qstring first):

    QString base_path = QString::fromStdString(argv[1]);
    QString test_path = QString::fromStdString(argv[2]);
    QDir baseline(base_path);
    QDir test_image(test_path);

I don't think this is related to not having the correct library included because if I simply do the following, the program compiles and links fine:

    Qimage test_image();

Already looked at the thread below, unfortunately it wasn't very helpful in my case: Constructing QImage from unsigned char* data

Any thoughts on what could be causing the linker errors? I doubt they'd be very helpful but I'll leave the linker error messages here:

 Severity Code Description Project File Line Suppression State Error LNK2019 unresolved external symbol "__declspec(dllimport) public: __thiscall QString::~QString(void)" (__imp_??1QString@@QAE@XZ) referenced in function _main image_compare_executable c:\Users\gela8178\documents\visual studio 2015\Projects\image_compare_executable\image_compare_executable\image_compare_executable.obj 

1 Error LNK2019 unresolved external symbol "__declspec(dllimport) public:  static class QString __cdecl QString::fromStdString(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (__imp_?fromStdString@QString@@SA?AV1@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z) referenced in function _main image_compare_executable c:\Users\gela8178\documents\visual studio 2015\Projects\image_compare_executable\image_compare_executable\image_compare_executable.obj 

1 Error LNK1120 2 unresolved externals image_compare_executable c:\users\gela8178\documents\visual studio 2015\Projects\image_compare_executable\Debug\image_compare_executable.exe 1

Including the relevant snippet of my code causing the linker issues:

#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <QtGui/QtGui>

using namespace cv;

int main(int argc, char* argv[])
{
// Check the number of parameters
if (argc < 3) {
    std::cerr << "Error: " << argv[0] << " : " << "Missing arguments" << std::endl;
    return 1;
}
std::cout << "Baseline: " << argv[1] << "\n" << "Test Image: " << argv[2] << std::endl;

QImage baseline(QString(argv[1]));
QImage test_image(QString(argv[2]));

if (baseline == test_image)
{
    std::cout << "Algorithm: Mem Compare" << "\n" << "Result: = Pass" << std::endl;
}
else
{
    std::cout << "Algorithm: Mem Compare" << "\n" << "Result: = Fail" << std::endl;
}
std::cout << "--------------------------------------------------------" << std::endl;

// ***************************************************************************** //

//Run MSSIM and output tuple
Mat i1 = imread(argv[1]);
Mat i2 = imread(argv[2]);

Scalar min_ssim = getMSSIM(i1, i2);
double R = min_ssim[0];
double G = min_ssim[1];
double B = min_ssim[2];

std::cout << "Algorithm: MSSIM" << "\n" << "Result: " << "(" << R << ", " << G << ", " << B << ")" << std::endl;

std::cout << "--------------------------------------------------------" << std::endl;

return 0;
}

SOLUTION:

Looking at the QImage constructors here the only instance that takes a QString takes it as a const reference:

QImage(const QString &fileName, const char *format = Q_NULLPTR)

Doing the following

    QImage baseline(QString(argv[1]));

was throwing linker errors because the QString was being passed by r reference, being destroyed by the time QImage was trying to access it. So the possible fixes would be to either do the QString assignment separately and then pass it to QImage so just do:

QImage baseline(argv[1]);
QImage test_image(argv[2]);
Community
  • 1
  • 1
Gela
  • 1
  • 7
  • `argv` is not a collection of std::string. It is an array of zero terminated C style strings. try something like `QImage baseline( QString( argv[1] ) );` But I have doubts that your attempt to compare will be fruitful. Image formatting will get in the way of an exact comparison – infixed Dec 01 '16 at 21:23
  • @infixed Thank you. Your suggestion did resolve the linker error. Can you explain what you mean by the image formatting getting in the way? I'm looking for a simple pixel by pixel comparison which is what the == operator is doing from what I understand. But now when I do `if (baseline == test_image)` I get more linker errors. – Gela Dec 01 '16 at 22:06
  • Because the image files look the same to our eyes does not mean that their binary representation is the same in memory. Yet the `==` will do a binary bit by bit comparaision. For instance consider two JPEG files with different quality settings. The JPEG compression algorithm will combine different sets of pixels together, so when you compare the binaries, they will be different. In effect it'd be as if you were just doing a byte by byte compare of the actual file byte sequences to see if they matched. Decoding them to a QImage might work for BMP or PNG files, but I wouldn't hope too much – infixed Dec 01 '16 at 22:15
  • as to your new linking errors, I'd have to see the actual program – infixed Dec 01 '16 at 22:16
  • Makes sense, thanks. I just added the bit of code that's using QImage comparison to the original post, could you see what I'm doing wrong? Basically I'm trying to feed a bunch of sets of 2 (almostor fully) identical images to the function; it'll run the basic `==` comparison first and then use a more complicated algorithm (MSSIM) to get the similarity percentage. @infixed – Gela Dec 01 '16 at 22:22
  • I'm not sure. The class does implement `operator==(const QImage &image) const` So what are the error messages – infixed Dec 01 '16 at 22:33
  • @infixed That is wrong. `QPixmap` has specialized uses only. By default, Qt uses the raster backend where a `QPixmap` is a thin wrapper around a `QImage`. In no case does `QPixmap` give you access to raw pixel values: it's essentially a black box that you can potentially expensively convert to a `QImage`. On raster backend the conversion is free as long as no painters are active on the pixmap, though. But still: you won't go wrong by using a `QImage` when you need an image. – Kuba hasn't forgotten Monica Dec 01 '16 at 22:50
  • I guess I had it inverted. I'll delete the bad comment Thanks @KubaOber – infixed Dec 01 '16 at 22:54
  • It'd have helped if you included details that actually determine what the linker does: where does your Qt come from/how it's configured, what compiler you're using, what's your project file, etc. – Kuba hasn't forgotten Monica Dec 01 '16 at 23:05
  • Note that `#include ` is incorrect and will obscure a misconfigured project, relegating the compile-time errors to link-time. Perhaps that's your problem. Replace it with `#include `, and then **delete the build folder** and build your project again. Do not ever use `#include ` nor `#include ` format. There's no need for it in user code, it only obscures misconfigured projects or borked build folders. – Kuba hasn't forgotten Monica Dec 01 '16 at 23:06
  • @KubaOber changing the include and rebuilding did not resolve the issue either... `unresolved external symbol class QImage __cdecl test_image(class QString * const)....` and `unresolved external symbol class QImage __cdecl baseline(class QString * const)....` – Gela Dec 01 '16 at 23:13
  • Sorry, you've run into the most vexing parse, then. Have fun googling it :) – Kuba hasn't forgotten Monica Dec 01 '16 at 23:14
  • I also disagree that this is a Most Vexing Parse case because doing the following results in the same linker error, but there shouldn't be any ambiguity as far as I can tell: `QImage baseline("C:\img1.png");` – Gela Dec 01 '16 at 23:39
  • If it is a most vexing parse, then `QImage baseline = QImage("C:\img1.png");` would bypass it I would think. But I expect it really should be `QImage baseline = QImage( QString("C:\img1.png") ); Because I always worry about when initializer strings get converted to QStrings or not` – infixed Dec 02 '16 at 15:54
  • If you still have the error about unresolved `QImage __cdecl test_image(class QString * const)`, that **is** the most vexing parse. A function that you declared is missing. It doesn't matter what you think. That's what the system is telling you it reads your code as. I'm also absolutely positive that your "Solution" is a red herring. There's no such thing as linker warning of this sort of undefined behavior. Heck, there is no undefined behavior in passing a temporary as an argument of a const-reference type. The compiler wouldn't let you compile the code for non-existent `QImage` constructors. – Kuba hasn't forgotten Monica Dec 02 '16 at 17:57
  • It is not a non existent constructor, just an incorrect use of the constructor is what was causing this because by the time the constructor tried to access the "supposedly there" QImage, it would have been destroyed, causing linker errors. It'd make sense once you look at the QImage constructors and how I was using them. – Gela Dec 02 '16 at 18:03

1 Answers1

0

First of all, QImage baseline(QString(argv[1])) is a most vexing parse. Period. You're using a compiler that doesn't warn you about it. The parentheses around (argv[1]) are superfluous. The canonical form for that declaration reads:

QImage baseline(QString [1]);

You also should not use argv because it's not portable. On Windows, for example, argv is rubbish.

Use QCoreApplication::arguments() instead:

int main(int argc, char ** argv) {
  QGuiApplication app{argc, argv};
  auto const args = app.arguments();
  if (args.count() < 3)
    return 1;
  QImage baseline(args[1]);
  QImage test_image(args[2]);  
  //...
}

Had you used it from the beginning, you wouldn't even run into the problem you had. So as you can see the benefits are double: it protects you from the most vexing parse, and it works correctly on Windows :)

Of course you can also use QApplication if you'll have a widget-based Ui. But for QImage all you need is QGuiApplication.

The linker errors for common Qt methods/constructors, such as __declspec(dllimport) public: __thiscall QString::~QString(void) indicate that you're not linking your project with Qt.

If you're already using qmake, then #include <QtGui/QtGui> would only be needed when you've not added the gui module to your project. The proper form is #include <QtGui>. If the compiler complains that it can't find the file, you haven't set up your project to use Qt correctly. Once you do, the file should compile and link - again, assuming that you use qmake or cmake and Qt macros.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Thank you, but that doesn't address the linker issues. I just posted my entire code (minus the actual implementation of `getMSSIM`), could you have a look at that and let me know if you can see what I'm doing wrong? – Gela Dec 01 '16 at 22:59
  • You might be using an incompatible compiler. Remember that a given Qt build is compatible with one particular compiler family only. E.g. if you have Qt built using MSVC 2013, it has no chance of working with any other version of MSVC, or any other compiler (e.g. gcc or clang). You're also not saying anything about how you build your project. Are you using `qmake+jom/nmake`, or something else? – Kuba hasn't forgotten Monica Dec 01 '16 at 23:04
  • I don't think it's a compiler compatibility issue since I am using the same set up in a different project without any issues. I am using Visual Studio 2015. The linker error occurs where I am calling `if (baseline == test_image)` – Gela Dec 01 '16 at 23:11
  • "Thank you, but that doesn't address the linker issue" [**It does.**](https://www.youtube.com/watch?v=CZR2k5c_198) You haven't even tried it, tsk tsk. – Kuba hasn't forgotten Monica Dec 02 '16 at 18:12
  • ` QImage baseline(args[1]);` is actually how I had mine originally, but changed it to the QString assignment after Infixed commented above which temporarily resolved that particular linker issue but caused some other problems down the road. So yes I did try your solution, but at the time I was also missing a lib too so I was still getting other linker errors. – Gela Dec 02 '16 at 18:22
  • You had two problems. And I did point out that using `#include ` will hide the problem that you didn't configure your project correctly. – Kuba hasn't forgotten Monica Dec 02 '16 at 18:26
  • I'm not sure what happens if the given path is invalid when passed to `QImage`. I would advise to check the docs. In other frameworks it's a common practice to first use the file API (`QDir`, `QFile` etc.) to see if such a file exists. – rbaleksandar Dec 02 '16 at 18:31
  • @rbaleksandar You get a null image. It's safe, there's no point in extraneous tests other than for more detailed diagnostics for the user. If the image is null it's either because the file doesn't exist or it's not in a format that Qt knows how to read. – Kuba hasn't forgotten Monica Dec 02 '16 at 18:34
  • What should I do instead of using `#include ` then? It is required to use QImage @KubaOber – Gela Dec 02 '16 at 18:41