OpenCV USBカメラをキャプチャして動画を作成する(C++)

C++でUSBカラメをキャプチャしてAVIを作成する。
OpenCV 3.4.1

#include <opencv2/opencv.hpp>

int main()
{
    using std::cout;
    using std::endl;

    int camera_id = 0;
    double fps = 30.0;
    double width = 640.0;
    double height = 480.0;

    // DirectShowでキャプチャ
    cv::VideoCapture cap(cv::CAP_DSHOW + camera_id);

    if (!cap.isOpened()) {
        cout << "camera open error" << endl;
        return;
    }

    if (!cap.set(cv::CAP_PROP_FPS, fps)) cout << "camera set fps error" << endl;
    if (!cap.set(cv::CAP_PROP_FRAME_WIDTH, width)) cout << "camera set width error" << endl;
    if (!cap.set(cv::CAP_PROP_FRAME_HEIGHT, height)) cout << "camera set height error" << endl;

    // モーションJPEGで保存する
    int fourcc = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
    bool isColor = true;

    cv::VideoWriter writer("test.avi", fourcc, fps, cv::Size(width, height), isColor);
    cv::namedWindow("framewin", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);
    cv::Mat frame;

    std::vector<uchar> jpeg_buff;
    std::vector<int> param(2);
    param[0] = CV_IMWRITE_JPEG_QUALITY;
    param[1] = 95;

    while (true) {
        // 1フレームキャプチャする
        cap >> frame;
        if (frame.empty()) break;

        // 水平反転
        cv::flip(frame, frame, 1);

        // JPEG変換
        // libjpeg-turboを使っているので(不要?)
        cv::imencode(".jpg", frame, jpeg_buff, param);
        cv::Mat jpeg_image = cv::imdecode(cv::Mat(jpeg_buff), CV_LOAD_IMAGE_COLOR);

        // AVIに書き込み
        writer << jpeg_image;

        cv::imshow("framewin", jpeg_image);

        if (cv::waitKey(1) >= 0) break;
    }

    cap.release();
    writer.release();

    cv::destroyAllWindows();

    return 0;
}

C++/CLIでString^(UTF-16)とchar*(UTF-8)の相互変換を調べたのでメモ。

久し振りに仕事でちょっとしたフレームワーク作ってます。
昔を思い出すこの感じ。嫌いじゃないです。
※Shift-JISは一切考慮してません

#include "msclr/marshal.h"
#include "msclr/marshal_windows.h"
#include "msclr/marshal_cppstd.h"
#include "msclr/marshal_atl.h"

#include <cstdint>
#include <iostream>
#include <string>   // string, wstring
#include <codecvt>  // wstring_convert, codecvt_utf8_utf16
#include <memory>

using namespace System;

namespace convutil {

inline std::wstring utf8_to_utf16(const std::string& from) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
    return conv.from_bytes(from);
}

std::string utf16_to_utf8(const std::wstring& from) {
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
    return conv.to_bytes(from);
}

inline String^ ToCliString(const std::string& from) {
    return msclr::interop::marshal_as<String^>(utf8_to_utf16(from));
}

inline String^ ToCliString(const char* from) {
    return msclr::interop::marshal_as<String^>(utf8_to_utf16(from));
}

inline std::string ToStlString(String^ from) {
    return msclr::interop::marshal_as<std::string>(from);
}

template<size_t toSize>
inline const char* ToCharArray(String^ from, char(&to)[toSize]) {
    auto context = gcnew msclr::interop::marshal_context();
    try {
        // 一度 std::wstring に変換してから std::string(utf-8)に変換
        // ムダがあるような気もする
        auto temp = context->marshal_as<std::wstring>(from);
        auto temp2 = utf16_to_utf8(temp);
        strncpy_s(to, toSize, temp2.c_str(), _TRUNCATE);
        return to;
    }
    finally{
        // 確実に削除。ぶっちゃけ例外でない気がするのでムダtryかも。
        delete context;
    }
}

} // namespace convutil

// Native
struct Employee {
    int32_t id;
    char name[64];
    char address[256];
};

// Managed
ref struct ST_Employee {
    Int32 id;
    String^ name;
    String^ address;
};

namespace msclr {
namespace interop {

// Native → Managed 変換
template<>
inline ST_Employee^ marshal_as<ST_Employee^, Employee>(const Employee& from) {
    auto to = gcnew ST_Employee();
    to->id = from.id;
    to->name = convutil::ToCliString(from.name);
    to->address = convutil::ToCliString(from.address);
    return to;
}

// Managed → Native 変換
template<>
ref class context_node<Employee*, ST_Employee^> : public context_node_base {
public:
    context_node(Employee*& to_obj, ST_Employee^ from_obj) {
        if (toPtr_ != nullptr) {
            delete toPtr_;
        }

        // toPtr_ に値をコピーする
        toPtr_ = new Employee();
        toPtr_->id = from_obj->id;
        convutil::ToCharArray(from_obj->name, toPtr_->name);
        convutil::ToCharArray(from_obj->address, toPtr_->address);

        to_obj = toPtr_;
    }

    ~context_node() {
        this->!context_node();
    }

protected:
    !context_node() {
        // メモリはちゃんと開放しましょう
        if (toPtr_ != nullptr) {
            delete toPtr_;
            toPtr_ = nullptr;
        }
    }

private:
    Employee * toPtr_;
    marshal_context context_;
};
} // namespace interop
} // namespace msclr

using namespace msclr::interop;

int main(array<System::String ^> ^args)
{
    // native to managed
    {
        Employee native_emp;
        native_emp.id = 1;
        strcpy_s(native_emp.name, u8"私です!!");
        strcpy_s(native_emp.address, "nihon no dokokadayo");

        auto managed_emp = marshal_as<ST_Employee^>(native_emp);
        Console::WriteLine("managed id = {0}", managed_emp->id);
        Console::WriteLine("managed name = {0}", managed_emp->name);
        Console::WriteLine("managed address = {0}", managed_emp->address);
    }

    // managed to native
    {
        auto managed_emp = gcnew ST_Employee();
        managed_emp->id = 100;
        managed_emp->name = "私はだれ??";
        managed_emp->address = "iega naidesu...";

        marshal_context context;
        auto native_emp = context.marshal_as<Employee*>(managed_emp);

        using std::cout;
        using std::endl;
        cout << "native id = " << native_emp->id << endl;
        cout << "native name = " << native_emp->name << endl;  // UTF-8だから文字化けするよ!
        cout << "native address = " << native_emp->address << endl;
    }

    return 0;
}

雑記

間もなく2017年度も終わりますね。
この一年は自分的には色々と激動の年でした。。

最近は要件がどうとか企画がどうとかが中心で、
余りプログラムを自分で書くことができていないのが残念です。

来年度は今更ながらPythonでも真面目に勉強してみようかなーとか思ってます。
そして、勉強成果をここにでもメモれたらいいな、とか。

話題は変わりますが、最近職場で英会話を学ぶ人が増えてきました。
私は英会話が全く出来ないのですが、
「エンジニアなら英会話を勉強するよりも、リアルタイム自動翻訳アプリを開発しようぜ!」
と思ってしまうのは、ある意味で逃げなんでしょうかね^^;

雑記でした。

Google Chrome のキャッシュ格納場所を変更する

最近会社のPCがSSD+HDDになったので、一時ファイル関係はHDD側に格納するように変更した。
その時に Google Chrome のキャッシュ格納場所を移動したのでやり方をメモ。

1.移動先のキャッシュフォルダを作成する
例:E:\Cache

2.現在のキャッシュフォルダを確認する
C:\Users\ユーザー名\AppData\Local\Google\Chrome\User Data\Default\Cache
※DefaultはProfile1、Profile2…などになっている場合がある
 一度chromeで適当なサイトを開いてみて、更新されたフォルダがどれかで判断する

3.上記で見つけたキャッシュフォルダを削除する

4.次のコマンドを実行する
mklink /d "C:\Users\ユーザー名\AppData\Local\Google\Chrome\User Data\Default\Cache" "E:\Cache"

これでシンボリックリンクが作成され、キャッシュの格納場所が変更される

ListのTがクラスか構造体かでメモリ使用量が大きく異なる?

そんなことはないだろうと思ったが、気になったので試してみた。
ListにAddし続けてOutOfMemoryがでるタイミングを見てみたがやっぱり大きな差はなかった。

[ListにクラスをAddし続けた場合]
・開始時点
 プライベートメモリ使用量: 30MB
 物理メモリ使用量: 41MB
 仮想メモリ使用量: 621MB

・終了時点
 プライベートメモリ使用量: 6,297MB
 物理メモリ使用量: 882MB
 仮想メモリ使用量: 6,870MB

[Listに構造体をAddし続けた場合]
・開始時点
 プライベートメモリ使用量: 30MB
 物理メモリ使用量: 41MB
 仮想メモリ使用量: 613MB

・終了時点
 プライベートメモリ使用量: 6,289MB
 物理メモリ使用量: 955MB
 仮想メモリ使用量: 6,862MB

以下、テストで使用したコード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MemoryCheck
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            const string title = "ListにクラスをAddし続けた場合";

            // 自プロセスを取得
            var process = System.Diagnostics.Process.GetCurrentProcess();
            var list = new List<TestClass>();

            using (var sw = new System.IO.StreamWriter(process.ProcessName + "_" + title + ".txt"))
            {
                try
                {
                    sw.WriteLine(title);
                    sw.WriteLine("プロセスID: {0}", process.Id);
                    sw.WriteLine("プロセス名: {0}", process.ProcessName);

                    int count = 1;
                    while (true)
                    {
                        sw.WriteLine("Count = {0}", count++);
                        WriteMemoryLog(process, sw);
                        var data = new TestClass();
                        data.Init();
                        list.Add(data);
                        sw.WriteLine();
                    }
                }
                catch (Exception ex)
                {
                    sw.WriteLine();
                    sw.WriteLine(ex.ToString());
                    this.Close();
                }
            }
            
        }

        private void button2_Click(object sender, EventArgs e)
        {
            const string title = "Listに構造体をAddし続けた場合";

            // 自プロセスを取得
            var process = System.Diagnostics.Process.GetCurrentProcess();
            var list = new List<TestStruct>();

            using (var sw = new System.IO.StreamWriter(process.ProcessName + "_" + title + ".txt"))
            {
                try
                {
                    sw.WriteLine(title);
                    sw.WriteLine("プロセスID: {0}", process.Id);
                    sw.WriteLine("プロセス名: {0}", process.ProcessName);

                    int count = 1;
                    while (true)
                    {
                        sw.WriteLine("Count = {0}", count++);
                        WriteMemoryLog(process, sw);
                        var data = new TestStruct();
                        data.Init();
                        list.Add(data);
                        sw.WriteLine();
                    }
                }
                catch (Exception ex)
                {
                    sw.WriteLine();
                    sw.WriteLine(ex.ToString());
                    this.Close();
                }
            }
        }

        private void WriteMemoryLog(System.Diagnostics.Process process, System.IO.StreamWriter sw)
        {
            // リフレッシュしないとプロセスの各種情報が最新情報に更新されない
            process.Refresh();

            // 各種プロセス情報を出力する
            const int mb = 1024 * 1024;
            sw.WriteLine("プライベートメモリ使用量: {0:#,0}MB", process.PrivateMemorySize64 / mb);
            sw.WriteLine("物理メモリ使用量:         {0:#,0}MB", process.WorkingSet64 / mb);
            sw.WriteLine("仮想メモリ使用量:         {0:#,0}MB", process.VirtualMemorySize64 / mb);
            sw.Flush();
        }
    }

    [StructLayout(LayoutKind.Sequential, Size = 4)]
    public struct TestStruct
    {
        // 1MBの配列
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024 * 1024)]
        public byte[] array01;

        public void Init()
        {
            array01 = new byte[1024 * 1024];
        }
    }

    public class TestClass
    {
        // 1MBの配列
        public byte[] array01;

        public void Init()
        {
            array01 = new byte[1024 * 1024];
        }
    }
}

C#で簡単な画像のトリミングをやってみた

引数で指定された画像をトリミングする

using System.Drawing;

namespace TrimImage
{
    class Program
    {
        static void Main(string[] args)
        {
            string srcFile = args[0];
            int x = int.Parse(args[1]);
            int y = int.Parse(args[2]);
            int width = int.Parse(args[3]);
            int height = int.Parse(args[4]);

            using (var srcImage = new Bitmap(srcFile))
            using (var desImage = new Bitmap(width, height, srcImage.PixelFormat))
            using (var g = Graphics.FromImage(desImage))
            {
                var srcRect = new Rectangle(x, y, width, height);
                var desRect = new Rectangle(0, 0, desImage.Width, desImage.Height);

                g.DrawImage(srcImage, desRect, srcRect, GraphicsUnit.Pixel);

                string desFile = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(srcFile), "trim_" + System.IO.Path.GetFileName(srcFile));

                desImage.Save(desFile, System.Drawing.Imaging.ImageFormat.Png);
            }
        }
    }
}

C++でファイルサイズ取得

C++でファイルサイズを取得する

// for win32api
#include <Windows.h>

// for ifstream
#include <fstream>

// for stat
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char* argv[])
{
    const char* filepath = "C:\\Windows\\Media\\Windows Shutdown.wav";

    // for win32api
    HANDLE hFile = CreateFile(filepath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    DWORD size1 = GetFileSize(hFile, NULL);
    CloseHandle(hFile);
    printf("GetFileSize :%ld\n", size1);

    // for ifstream
    std::ifstream ifs(filepath, std::ios::binary);
    ifs.seekg(0, std::ios::end);
    auto eofpos = ifs.tellg();
    ifs.clear();
    ifs.seekg(0, std::ios::beg);
    auto begpos = ifs.tellg();
    auto size2 = eofpos - begpos;
    printf("ifstream :%ld\n", size2);
    ifs.close();

    // for stat
    struct _stat buf;
    int result = _stat(filepath, &buf);
    if (result == 0)
    {
        printf("stat :%ld\n", buf.st_size);
    }

    return 0;
}

結果
GetFileSize :169444
ifstream :169444
stat :169444