1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

4-2-2. Copilotにコードレビューしてもらおう

Last updated at Posted at 2025-01-01

1.今回やること

・GitHub Copilotを使用してコードレビューをしてもらいました。
・お願いしたことは、「コードレビューをお願いできますか?」


20-1.png

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));
}


20-2.png

・確かに良い提案をしてくれていますね。
・素晴らしいと褒めてもくれてるし(単純なのでそれだけでも嬉しい)
・全面的にこの提案を受け入れて、もう少し良いコードへ変更しましょう。

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が提案してくれたままですし、お値段以上の価値がある気がしています。

もくじへ

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?