6

I'm trying to get a simple OpenCV sample working in C++ on Windows and my C++ is more than rusty.

The sample is fairly simple:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    if( argc != 2)
    {
        cout <<" Usage: display_image ImageToLoadAndDisplay" << endl;
        return -1;
    }
    Mat image;
    image = imread(argv[1], IMREAD_COLOR); // Read the file
    if(! image.data )                      // Check for invalid input
    {
        cout <<  "Could not open or find the image" << std::endl ;
        return -1;
    }
    namedWindow( "Display window", WINDOW_AUTOSIZE ); // Create a window for display.
    imshow( "Display window", image );                // Show our image inside it.
    waitKey(0); // Wait for a keystroke in the window
    return 0;
}

When I make a new simple C++ console application (with ATL) in Visual Studio 2012 I get a different template for main:

int _tmain( int argc, _TCHAR* argv[] )

So before I send the filename to OpenCV's imread function I need to convert the _TCHAR* arg[1] to a char*. Using a simple filename, 'opencv-logo.jpg', from the memory in the memory window I can see that the _TCHAR are taking two bytes each

o.p.e.n.c.v.-.l.o.g.o...j.p.g...
6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00

Following the conversion recommendation in another answer I am trying to use ATL 7.0 String Conversion Classes and Macros by inserting the following code:

char* filename = CT2A(argv[1]);

But the resulting memory is a mess, certainly not 'opencv-logo.jpg' as an ascii string:

fe fe fe fe fe fe fe fe fe fe ...
þþþþþþþþþþ ...

Which conversion technique, function, or macro should I be using?

(N.B. This may be a related question but I cannot see how to apply the answer here.)

Community
  • 1
  • 1
dumbledad
  • 16,305
  • 23
  • 120
  • 273
  • Your project template is trying to create a C++ project for unicode support. I don't recall the specific project/compiler options, but there should be something to specify whether using unicode or not. – OldProgrammer Oct 10 '13 at 16:50
  • It's in the [general project properties page](http://msdn.microsoft.com/en-us/library/vstudio/8x480de8.aspx) – user786653 Oct 10 '13 at 17:15
  • Do you need a USES_CONVERSION; statement at the top of each function that makes use of the conversion macros? – Duncan Smith Oct 10 '13 at 16:55

3 Answers3

21

The quickest solution is to just change the signature to the standard one. Replace:

int _tmain( int argc, _TCHAR* argv[] )

With

int main( int argc, char *argv[] )

This does mean on Windows that the command line arguments get converted to the system's locale encoding and since Windows doesn't support UTF-8 here not everything converts correctly. However unless you actually need internationalization then it may not be worth your time to do anything more.

bames53
  • 86,085
  • 15
  • 179
  • 244
  • I tried this - I'll edit the question to explain what (I think) went wrong – dumbledad Oct 10 '13 at 17:20
  • @dumbledad Okay. Comment here when you've edited your question so I get a notification. – bames53 Oct 10 '13 at 17:34
  • 2
    @bames This does work. Last time I used the signature `int _tmain( int argc, char** argv )` and the `char*` ended up as `6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00` which is the char `c` followed by `\0` plus more bytes, i.e. the string "c"! Swapping `_tmain` to `main` makes all the difference, thank you. – dumbledad Oct 10 '13 at 17:34
7

_TCHAR, i.e. TCHAR is a type that depends on your project's settings. It can be either wchar_t (when you use Unicode) or char (when you use Multi-byte). You will find this in Project Properties - General, there's a setting Character Set.

Probably the simplest thing that you could do is just to use multi-byte option and treat _TCHAR* type as a simple char* and use it to construct std::string object ASAP:

std::string filename(argv[1]);

But in case you are going to work with special characters A LOT, then I find it more reasonable to use Unicode and hold strings in form of std::wstring objects wherever it's possible. If that's the case, then just use std::wstring's constructor instead:

std::wstring filename(argv[1]);

And in case you'll end up working with wide strings, sometimes you'll need a conversion between wide strings and multi-byte strings and these helpers might help you:

// multi byte to wide char:
std::wstring s2ws(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

// wide char to multi byte:
std::string ws2s(const std::wstring& wstr)
{
    int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), 0, 0, 0, 0); 
    std::string strTo(size_needed, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), &strTo[0], size_needed, 0, 0); 
    return strTo;
}
LihO
  • 41,190
  • 11
  • 99
  • 167
  • "I recommend you using Unicode" Of course using Unicode doesn't preclude using `char*`. Typically I encode Unicode data as UTF-8 and stick with `char*`. – bames53 Oct 10 '13 at 17:00
  • @bames53: Depends on the context of program as well as APIs that he's going to use, but I admit that multi-byte seems to be a better choice here. – LihO Oct 10 '13 at 17:18
  • 1
    @bames53: True, but unfortunately on windows unicode more or less implies utf-16 and `wchar_t` as that's what the win32 api uses. – user786653 Oct 10 '13 at 17:19
  • Thanks @LihO but I'm nut sure I understand this. What would I then pass to `imread` as its first argument? – dumbledad Oct 10 '13 at 17:36
  • @dumbledad: in case of `std::string filename(argv[1]);` you would pass `filename.c_str()` – LihO Oct 10 '13 at 17:38
  • I'm getting an error from `std::string filename(argv[1]);` : `error C2664: 'std::basic_string<_Elem,_Traits,_Alloc>::basic_string(const std::basic_string<_Elem,_Traits,_Alloc> &)' : cannot convert parameter 1 from '_TCHAR *' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'` – dumbledad Oct 10 '13 at 17:45
  • 1
    @dumbledad: Are you sure you are using multi-byte character set? – LihO Oct 10 '13 at 17:47
  • Well spotted @LihO. I've changed the Character Set from `Use Unicode Character Set` to `Use Multi-Byte Character Set` in the General section of the Property Changes and now your `std::string filename(argv[1]);` with `filename.c_str()` work. Thanks! – dumbledad Oct 11 '13 at 05:57
-1

other than reading the 26,000 page cpp 2003 VS manual which hasn't changed much.... std::string test(print); test = "";

int _tmain( int argc, _TCHAR* argv[] )  

int main( int argc, char *argv[] ) 

work the same unless you were using some securitys function.... and couldn't be unicode in the character set property ... if you were to use the CreateFile function in file Management Functions unless you multi-threaded it somehow

Community
  • 1
  • 1