1//
2// MainPage.xaml.cpp
3// Implementation of the MainPage class.
4//
5
6#include "pch.h"
7#include "MainPage.xaml.h"
8#include <ppltasks.h>
9#include <wrl\client.h>
10#include <Robuffer.h>
11#include <vector>
12#include <opencv2\imgproc\types_c.h>
13#include <opencv2\imgcodecs\imgcodecs.hpp>
14#include <opencv2\core\core.hpp>
15
16#include <windows.storage.h>
17
18using namespace OcvImageProcessing;
19
20using namespace Microsoft::WRL;
21using namespace concurrency;
22using namespace Platform;
23using namespace Windows::Foundation;
24using namespace Windows::Storage::Streams;
25using namespace Windows::Storage;
26using namespace Windows::UI::Xaml::Media::Imaging;
27using namespace Windows::Graphics::Imaging;
28using namespace Windows::Foundation::Collections;
29using namespace Windows::UI::Xaml;
30using namespace Windows::UI::Xaml::Controls;
31using namespace Windows::UI::Xaml::Controls::Primitives;
32using namespace Windows::UI::Xaml::Data;
33using namespace Windows::UI::Xaml::Input;
34using namespace Windows::UI::Xaml::Media;
35using namespace Windows::UI::Xaml::Navigation;
36
37Uri^ InputImageUri = ref new Uri(L"ms-appx:///Assets/Lena.png");
38
39// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
40
41MainPage::MainPage()
42{
43    InitializeComponent();
44
45#ifdef __OPENCV_IMGCODECS_HPP__
46
47    // Image loading OpenCV way ... way more simple
48    cv::Mat image = cv::imread("Assets/Lena.png");
49    Lena = cv::Mat(image.rows, image.cols, CV_8UC4);
50    cvtColor(image, Lena, CV_BGR2BGRA);
51    UpdateImage(Lena);
52
53#else
54
55    // Image loading WinRT way
56    RandomAccessStreamReference^ streamRef = RandomAccessStreamReference::CreateFromUri(InputImageUri);
57
58    task<IRandomAccessStreamWithContentType^> (streamRef->OpenReadAsync()).
59    then([](task<IRandomAccessStreamWithContentType^> thisTask)
60    {
61        IRandomAccessStreamWithContentType^ fileStream = thisTask.get();
62        return BitmapDecoder::CreateAsync(fileStream);
63    }).
64    then([](task<BitmapDecoder^> thisTask)
65    {
66        BitmapDecoder^ decoder = thisTask.get();
67        return decoder->GetFrameAsync(0);
68    }).
69    then([this](task<BitmapFrame^> thisTask)
70    {
71        BitmapFrame^ frame = thisTask.get();
72
73        // Save some information as fields
74        frameWidth = frame->PixelWidth;
75        frameHeight = frame->PixelHeight;
76
77        return frame->GetPixelDataAsync();
78    }).
79    then([this](task<PixelDataProvider^> thisTask)
80    {
81        PixelDataProvider^ pixelProvider = thisTask.get();
82        Platform::Array<byte>^ srcPixels = pixelProvider->DetachPixelData();
83        Lena = cv::Mat(frameHeight, frameWidth, CV_8UC4);
84        memcpy(Lena.data, srcPixels->Data, 4*frameWidth*frameHeight);
85        UpdateImage(Lena);
86    });
87
88#endif
89}
90
91/// <summary>
92/// Temporary file creation example. Will be created in WinRT application temporary directory
93/// which usually is "C:\Users\{username}\AppData\Local\Packages\{package_id}\TempState\{random_name}.{suffix}"
94/// </summary>
95/// <param name="suffix">Temporary file suffix, e.g. "tmp"</param>
96std::string OcvImageProcessing::MainPage::CreateTempFile(const std::string &suffix) {
97    return cv::tempfile(suffix.c_str());
98}
99
100/// <summary>
101/// Creating/writing a file in the application local directory
102/// </summary>
103/// <param name="path">Image to save</param>
104bool OcvImageProcessing::MainPage::SaveImage(cv::Mat image) {
105    StorageFolder^ localFolderRT = ApplicationData::Current->LocalFolder;
106    cv::String localFile = ConvertPath(ApplicationData::Current->LocalFolder->Path) + "\\Lena.png";
107
108    return cv::imwrite(localFile, image);
109}
110
111/// <summary>
112/// Getting std::string from managed string via std::wstring.
113/// Provides an example of three ways to do it.
114/// Can't use this one: https://msdn.microsoft.com/en-us/library/bb384865.aspx, not available on WinRT.
115/// </summary>
116/// <param name="path">Path to be converted</param>
117cv::String OcvImageProcessing::MainPage::ConvertPath(Platform::String^ path) {
118    std::wstring localPathW(path->Begin());
119
120    // Opt #1
121    //std::string localPath(localPathW.begin(), localPathW.end());
122
123    // Opt #2
124    //std::string localPath(StrToWStr(localPathW));
125
126    // Opt #3
127    size_t outSize = localPathW.length() + 1;
128    char* localPathC = new char[outSize];
129    size_t charsConverted = 0;
130    wcstombs_s(&charsConverted, localPathC, outSize, localPathW.c_str(), localPathW.length());
131    cv::String localPath(localPathC);
132
133    // Implicit conversion from std::string to cv::String
134    return localPath;
135}
136
137std::string OcvImageProcessing::MainPage::StrToWStr(const std::wstring &input) {
138    if (input.empty()) {
139        return std::string();
140    }
141
142    int size = WideCharToMultiByte(CP_UTF8, 0, &input[0], (int)input.size(), NULL, 0, NULL, NULL);
143    std::string result(size, 0);
144
145    WideCharToMultiByte(CP_UTF8, 0, &input[0], (int)input.size(), &result[0], size, NULL, NULL);
146
147    return result;
148}
149
150/// <summary>
151/// Invoked when this page is about to be displayed in a Frame.
152/// </summary>
153/// <param name="e">Event data that describes how this page was reached.  The Parameter
154/// property is typically used to configure the page.</param>
155void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
156{
157    (void) e;    // Unused parameter
158}
159
160void OcvImageProcessing::MainPage::UpdateImage(const cv::Mat& image)
161{
162    // Create the WriteableBitmap
163    WriteableBitmap^ bitmap = ref new WriteableBitmap(image.cols, image.rows);
164
165    // Get access to the pixels
166    IBuffer^ buffer = bitmap->PixelBuffer;
167    unsigned char* dstPixels;
168
169    // Obtain IBufferByteAccess
170    ComPtr<IBufferByteAccess> pBufferByteAccess;
171    ComPtr<IInspectable> pBuffer((IInspectable*)buffer);
172    pBuffer.As(&pBufferByteAccess);
173
174    // Get pointer to pixel bytes
175    pBufferByteAccess->Buffer(&dstPixels);
176    memcpy(dstPixels, image.data, image.step.buf[1]*image.cols*image.rows);
177
178    // Set the bitmap to the Image element
179    PreviewWidget->Source = bitmap;
180}
181
182
183cv::Mat OcvImageProcessing::MainPage::ApplyGrayFilter(const cv::Mat& image)
184{
185    cv::Mat result;
186    cv::Mat intermediateMat;
187    cv::cvtColor(image, intermediateMat, CV_RGBA2GRAY);
188    cv::cvtColor(intermediateMat, result, CV_GRAY2BGRA);
189    return result;
190}
191
192cv::Mat OcvImageProcessing::MainPage::ApplyCannyFilter(const cv::Mat& image)
193{
194    cv::Mat result;
195    cv::Mat intermediateMat;
196    cv::Canny(image, intermediateMat, 80, 90);
197    cv::cvtColor(intermediateMat, result, CV_GRAY2BGRA);
198    return result;
199}
200
201cv::Mat OcvImageProcessing::MainPage::ApplyBlurFilter(const cv::Mat& image)
202{
203    cv::Mat result;
204    cv::blur(image, result, cv::Size(3,3));
205    return result;
206}
207
208cv::Mat OcvImageProcessing::MainPage::ApplyFindFeaturesFilter(const cv::Mat& image)
209{
210    cv::Mat result;
211    cv::Mat intermediateMat;
212    cv::Ptr<cv::FastFeatureDetector> detector = cv::FastFeatureDetector::create(50);
213    std::vector<cv::KeyPoint> features;
214
215    image.copyTo(result);
216    cv::cvtColor(image, intermediateMat, CV_RGBA2GRAY);
217    detector->detect(intermediateMat, features);
218
219    for( unsigned int i = 0; i < std::min(features.size(), (size_t)50); i++ )
220    {
221        const cv::KeyPoint& kp = features[i];
222        cv::circle(result, cv::Point((int)kp.pt.x, (int)kp.pt.y), 10, cv::Scalar(255,0,0,255));
223    }
224
225    return result;
226}
227
228cv::Mat OcvImageProcessing::MainPage::ApplySepiaFilter(const cv::Mat& image)
229{
230    const float SepiaKernelData[16] =
231    {
232        /* B */0.131f, 0.534f, 0.272f, 0.f,
233        /* G */0.168f, 0.686f, 0.349f, 0.f,
234        /* R */0.189f, 0.769f, 0.393f, 0.f,
235        /* A */0.000f, 0.000f, 0.000f, 1.f
236    };
237    const cv::Mat SepiaKernel(4, 4, CV_32FC1, (void*)SepiaKernelData);
238    cv::Mat result;
239    cv::transform(image, result, SepiaKernel);
240    return result;
241}
242
243void OcvImageProcessing::MainPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
244{
245    switch(FilterTypeWidget->SelectedIndex)
246    {
247    case PREVIEW:
248        UpdateImage(Lena);
249        break;
250    case GRAY:
251        UpdateImage(ApplyGrayFilter(Lena));
252        break;
253    case CANNY:
254        UpdateImage(ApplyCannyFilter(Lena));
255        break;
256    case BLUR:
257        UpdateImage(ApplyBlurFilter(Lena));
258        break;
259    case FEATURES:
260        UpdateImage(ApplyFindFeaturesFilter(Lena));
261        break;
262    case SEPIA:
263        UpdateImage(ApplySepiaFilter(Lena));
264        break;
265    default:
266        UpdateImage(Lena);
267    }
268}
269