1.今回やること
・GitHub Copilotを使用してコードレビューをしてもらいました。
・お願いしたことは、「コードレビューをお願いできますか?」
MainWindow.xaml.cpp-copilotの提案
// エラーハンドリングとリソース解放を追加した MatToBitmapAsync 関数
winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage>
winrt::WinUI_OpenCV::implementation::MainWindow::MatToBitmapAsync(const cv::Mat& mat)
{
if (mat.empty())
{
co_return nullptr;
}
try
{
// ① imencode を使用してバイト配列へエンコード
std::vector<uchar> imagebuf;
if (!cv::imencode(".bmp", mat, imagebuf))
{
co_return nullptr;
}
// ② InMemoryRandomAccessStream を作成
winrt::Windows::Storage::Streams::InMemoryRandomAccessStream rcstream;
// ③ DataWriter を使用してストリームへデータを書き込む
{
auto outputstream = rcstream.GetOutputStreamAt(0);
winrt::Windows::Storage::Streams::DataWriter writer(outputstream);
writer.WriteBytes(winrt::array_view<const uint8_t>(imagebuf));
co_await writer.StoreAsync();
co_await outputstream.FlushAsync();
// リソースを解放
writer.Close();
outputstream.Close();
}
// ④ ストリームの位置を先頭に戻す
rcstream.Seek(0);
// ⑤ BitmapImage を作成
winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage bitmap;
co_await bitmap.SetSourceAsync(rcstream);
// ストリームを解放
rcstream.Close();
co_return bitmap;
}
catch (const std::exception& ex)
{
// エラーのログ出力(必要に応じて)
OutputDebugStringA(ex.what());
co_return nullptr;
}
}
// 共通処理をまとめた ResizeAndDisplayImage 関数
winrt::Windows::Foundation::IAsyncAction
winrt::WinUI_OpenCV::implementation::MainWindow::ResizeAndDisplayImage(double scale)
{
if (_loadImg.empty())
{
co_return;
}
// 画像のリサイズ
ResizeImage(scale);
// 画像を BitmapImage に変換
auto bitmap = co_await MatToBitmapAsync(_loadImg);
if (!bitmap)
{
co_return;
}
// ウィンドウサイズを画像サイズに合わせる
ResizeWindowToImage(bitmap);
// 画像を表示
_showImage().Source(bitmap);
}
// Enlarge2Times_Click と Reduce2Times_Click の修正
winrt::Windows::Foundation::IAsyncAction
winrt::WinUI_OpenCV::implementation::MainWindow::Enlarge2Times_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
// 面積を2倍に拡大(1辺を √2 倍)
co_await ResizeAndDisplayImage(std::sqrt(2.0));
}
winrt::Windows::Foundation::IAsyncAction
winrt::WinUI_OpenCV::implementation::MainWindow::Reduce2Times_Click(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
{
// 面積を1/2に縮小(1辺を 1/√2 倍)
co_await ResizeAndDisplayImage(1.0 / std::sqrt(2.0));
}
・確かに良い提案をしてくれていますね。
・素晴らしいと褒めてもくれてるし(単純なのでそれだけでも嬉しい)
・全面的にこの提案を受け入れて、もう少し良いコードへ変更しましょう。
MainWindow.xaml.h
#pragma once
#include "MainWindow.g.h"
//6-2で追加。OpenCVのヘッダーをインクルード
#include <opencv2/opencv.hpp>
namespace winrt::WinUI_OpenCV::implementation
{
struct MainWindow : MainWindowT<MainWindow>
{
MainWindow()
{
//6-1で追加。タイトル変更
Title(L"WinUI_OpenCV");
}
//6-1で追加。ウインドウハンドルの取得
HWND GetHWND();
//6-2で追加。wstringからstringへの変換
std::string WStringToString(const std::wstring& utf16);
//6-2で追加。stringからwstringへの変換
std::wstring StringToWString(const std::string& jisstr);
//6-3で追加。MatからBitmapImageへの変換
winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage> MatToBitmapAsync(const cv::Mat& mat);
//6-3で追加。ウインドウサイズを画像サイズに合わせる
void ResizeWindowToImage(const winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage& bitmap);
//6-4で追加。画像サイズを変更
void ResizeImage(const double scale);
//4-2-2で追加。ウインドウサイズ変更の共通処理
winrt::Windows::Foundation::IAsyncAction ResizeAndDisplayImage(double scale);
//イベントハンドラ
//6-1で変更。非同期化
winrt::Windows::Foundation::IAsyncAction Open_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
void Exit_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
//6-4で変更。非同期化
winrt::Windows::Foundation::IAsyncAction Enlarge2Times_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
//6-4で変更。非同期化
winrt::Windows::Foundation::IAsyncAction Reduce2Times_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
private:
//6-2で追加。画像データ保存メンバ
cv::Mat _loadImg;
};
}
namespace winrt::WinUI_OpenCV::factory_implementation
{
struct MainWindow : MainWindowT<MainWindow, implementation::MainWindow>
{
};
}
MainWindow.xaml.cpp
#include "pch.h"
#include "MainWindow.xaml.h"
#if __has_include("MainWindow.g.cpp")
#include "MainWindow.g.cpp"
#endif
using namespace winrt;
using namespace Microsoft::UI::Xaml;
//6-1で追加。::IWindowNativeと::IInitializeWithWindowを使うのにmicrosoft.ui.xaml.window.h、Shobjidl.hが必要
#include <microsoft.ui.xaml.window.h>
#include <Shobjidl.h>
//6-1で追加。FilePickerを使用するのにwinrt/Windows.Storage.Pickers.hが必要
#include <winrt/Windows.Storage.Pickers.h>
//6-3で追加。InMemoryRandomAccessStreamを使用するのにwinrt/Windows.Storage.Streams.hが必要
#include <winrt/Windows.Storage.Streams.h>
//6-3で追加。BitmapImageを使用するのにwinrt/Microsoft.UI.Xaml.Media.Imaging.hが必要
#include <winrt/Microsoft.UI.Xaml.Media.Imaging.h>
//6-3で追加GetWindowIdFromWindowを使用するのにwinrt/Microsoft.ui.interop.hとwinrt/Microsoft.UI.Windowing.hが必要
#include <winrt/Microsoft.UI.Interop.h>
#include <winrt/Microsoft.UI.Windowing.h>
//6-3で追加。Windows.Graphics.SizeInt32を使用するのにwinrt/Windows.Graphics.hが必要
#include <winrt/Windows.Gaming.Input.h>
namespace winrt::WinUI_OpenCV::implementation
{
}
//6-1で追加。ウインドウハンドルの取得
HWND winrt::WinUI_OpenCV::implementation::MainWindow::GetHWND()
{
auto windowNative{ this->m_inner.as<::IWindowNative>() };
HWND hWnd{ 0 };
windowNative->get_WindowHandle(&hWnd);
return hWnd;
}
//6-2で追加。wstringからstringへの変換
std::string winrt::WinUI_OpenCV::implementation::MainWindow::WStringToString(const std::wstring& utf16)
{
std::string jisstr;
int jissize = WideCharToMultiByte(CP_ACP, 0, utf16.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (jissize > 0)
{
jisstr.resize(jissize);
WideCharToMultiByte(CP_ACP, 0, utf16.c_str(), -1, jisstr.data(), jissize, nullptr, nullptr);
}
return jisstr;
}
//6-2で追加。stringからwstringへの変換
std::wstring winrt::WinUI_OpenCV::implementation::MainWindow::StringToWString(const std::string& jisstr)
{
std::wstring utf16;
int utf16size = MultiByteToWideChar(CP_ACP, 0, jisstr.c_str(), -1, nullptr, 0);
if (utf16size > 0)
{
utf16.resize(utf16size);
MultiByteToWideChar(CP_ACP, 0, jisstr.c_str(), -1, utf16.data(), utf16size);
}
return utf16;
}
//6-3で追加。MatからBitmapImageへの変換
//4-2-2で変更。エラーハンドリングを追加
winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage> winrt::WinUI_OpenCV::implementation::MainWindow::MatToBitmapAsync(const cv::Mat& mat)
{
if (mat.empty())
{
co_return nullptr;
}
try
{
// ① imencode を使用してバイト配列へエンコード
std::vector<uchar> imagebuf;
if (!cv::imencode(".bmp", mat, imagebuf))
{
co_return nullptr;
}
// ② InMemoryRandomAccessStream を作成
winrt::Windows::Storage::Streams::InMemoryRandomAccessStream rcstream;
// ③ DataWriter を使用してストリームへデータを書き込む
{
auto outputstream = rcstream.GetOutputStreamAt(0);
winrt::Windows::Storage::Streams::DataWriter writer(outputstream);
writer.WriteBytes(winrt::array_view<const uint8_t>(imagebuf));
co_await writer.StoreAsync();
co_await outputstream.FlushAsync();
// リソースを解放
writer.Close();
outputstream.Close();
}
// ④ ストリームの位置を先頭に戻す
rcstream.Seek(0);
// ⑤ BitmapImage を作成
winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage bitmap;
co_await bitmap.SetSourceAsync(rcstream);
// ストリームを解放
rcstream.Close();
co_return bitmap;
}
catch (const std::exception& ex)
{
// エラーのログ出力(必要に応じて)
OutputDebugStringA(ex.what());
co_return nullptr;
}
}
//6-3で追加。ウインドウサイズを画像サイズに合わせる
void winrt::WinUI_OpenCV::implementation::MainWindow::ResizeWindowToImage(const winrt::Microsoft::UI::Xaml::Media::Imaging::BitmapImage& bitmap)
{
//①ウインドウIDからAppWindowを作成。
auto wndid = Microsoft::UI::GetWindowIdFromWindow(GetHWND());
auto appwnd = Microsoft::UI::Windowing::AppWindow::GetFromWindowId(wndid);
//②Bitmapのサイズを取得+高さにはmenuの高さ
auto menuheight = _menuBar().ActualHeight();
winrt::Windows::Graphics::SizeInt32 imgsize;
imgsize.Width = bitmap.PixelWidth();
imgsize.Height = bitmap.PixelHeight() + menuheight;
//③AppWindowのResize()を使用してウインドウサイズを変更
appwnd.Resize(imgsize);
}
void winrt::WinUI_OpenCV::implementation::MainWindow::ResizeImage(const double scale)
{
if (_loadImg.empty())
return;
//画像を拡大縮小. cv::resize()を使用
cv::Mat resizedImg;
cv::resize(_loadImg, resizedImg, cv::Size(), scale, scale, cv::INTER_CUBIC);
//_loadImgをresizedImgで上書き
_loadImg = resizedImg.clone();
}
//4-2-2で追加。ウインドウサイズ変更の共通処理
winrt::Windows::Foundation::IAsyncAction winrt::WinUI_OpenCV::implementation::MainWindow::ResizeAndDisplayImage(double scale)
{
if (_loadImg.empty())
{
co_return;
}
// 画像のリサイズ
ResizeImage(scale);
// 画像を BitmapImage に変換
auto bitmap = co_await MatToBitmapAsync(_loadImg);
if (!bitmap)
{
co_return;
}
// ウィンドウサイズを画像サイズに合わせる
ResizeWindowToImage(bitmap);
// 画像を表示
_showImage().Source(bitmap);
}
winrt::Windows::Foundation::IAsyncAction winrt::WinUI_OpenCV::implementation::MainWindow::Open_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
//6-1で追加。FileOpenPickerを使用してファイルを開く
winrt::Windows::Storage::Pickers::FileOpenPicker imgpicker;
imgpicker.as<::IInitializeWithWindow>()->Initialize(this->GetHWND());
imgpicker.FileTypeFilter().Append(L".jpg");
imgpicker.FileTypeFilter().Append(L".png");
imgpicker.FileTypeFilter().Append(L".bmp");
imgpicker.FileTypeFilter().Append(L".gif");
imgpicker.SuggestedStartLocation(winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
auto&& imgfile = co_await imgpicker.PickSingleFileAsync();
//6-1で追加。ファイルが選択されなかった場合はco_returnで関数を終了
if (!imgfile)
{
co_return;
}
//6-1で追加。ファイルパスの取得
std::wstring imgpath = imgfile.Path().c_str();
//6-2で追加。wstringからstringへの変換
std::string utf8path = WStringToString(imgpath);
//6-2で追加。OpenCVで画像を読み込む
_loadImg = cv::imread(utf8path);
//6-2で追加。画像データを表示 6-4でコメントアウト
//cv::imshow("Load Image", _loadImg);
//4-2-2で変更。共通処理化。倍率は変更しないので1倍1
co_await ResizeAndDisplayImage(1.0);
}
void winrt::WinUI_OpenCV::implementation::MainWindow::Exit_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
//6-1で追加。アプリの終了
Application::Current().Exit();
}
//6-4で変更。非同期化
//4-2-2で変更。共通処理化
winrt::Windows::Foundation::IAsyncAction winrt::WinUI_OpenCV::implementation::MainWindow::Enlarge2Times_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
// 面積を2倍に拡大(1辺を √2 倍)
co_await ResizeAndDisplayImage(std::sqrt(2.0));
}
//6-4で変更。非同期化
//4-2-2で変更。共通処理化
winrt::Windows::Foundation::IAsyncAction winrt::WinUI_OpenCV::implementation::MainWindow::Reduce2Times_Click(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e)
{
// 面積を1/2に縮小(1辺を 1/√2 倍)
co_await ResizeAndDisplayImage(1.0 / std::sqrt(2.0));
}
・この変更はコミットしませんが、最終的にこうなりました。
・こんな感じでGitHub Copilotはコードレビューもしてくれる良いツールです。
・使っていると色々鬱陶しい部分もありますが…
・こんなに使えるツールが$10/月とお買い得になっています。
・実際にwstringとstringの変換部分はcopilotが提案してくれたままですし、お値段以上の価値がある気がしています。