やろうとしていること
Google ARcoreとOpenCVを利用してandroid端末でカメラを起動させ,リアルタイムでカメラが撮影している動画データをOpenCVに渡してOpenCV側で画像処理を行った画像データを画面上に映し出したいです.
できていること
android端末のカメラが取得した画像データを画面上に映し出す.
できていないこと
android端末のカメラが取得した画像データをOpenCVに流して画像処理(例えば,2値化)を行う.
わからないこと
以下のソースコードで試してみたが,アプリを実行した際にアプリがすぐ落ちてしまいます.
原因はおそらく,update関数内でMatの生成をしていることが処理を重くしてアプリが落ちる原因だと考えています.
しかし,update関数の中以外で,どの箇所でMatの生成を行えばリアルタイムで動画を処理できるのでしょうか...
画像処理が分からないわけではなく,現状のコードではなぜアプリが落ちてしまうのか,その改善方法が分からないです.
ソースコード
C#
1using System; 2using UnityEngine; 3 4//GoogleARcore 5using GoogleARCore; 6using GoogleARCore.Examples.ComputerVision; 7 8//OpenCVForUnity 9using OpenCVForUnity; 10using OpenCVForUnity.UnityUtils; 11using OpenCVForUnity.ImgprocModule; 12using OpenCVForUnity.CoreModule; 13 14 15 16public class TextureRenderWrapper : MonoBehaviour 17{ 18 19 //Textureの生成 20 Texture2D new_tex; 21 Texture2D out_tex; 22 23 24 //Matの作成 25 Mat imgMat; 26 Mat imgMat2; 27 Mat hsvMat; 28 Mat blurMat; 29 30 //-------------------------------------- 31 32 /// <summary> 33 /// 取得するTextureのサイズの、カメラ画像に対する割合 34 /// </summary> 35 public float TextureSizeRatio = 1.0f; 36 37 /// <summary> 38 /// カメラ画像のデータ群 39 /// </summary> 40 private TextureReaderApi.ImageFormatType format; 41 private int width; 42 private int height; 43 private IntPtr pixelBuffer; 44 private int bufferSize = 0; 45 46 /// <summary> 47 /// カメラ画像取得用API 48 /// </summary> 49 private TextureReader TextureReader = null; 50 51 52 /// <summary> 53 /// カメラ画像のサイズに合わせてTextureReaderをセットしたかどうかのフラグ 54 /// </summary> 55 private bool setFrameSizeToTextureReader = false; 56 public void Awake() 57 { 58 // // カメラ画像取得時に呼ばれるコールバック関数を定義 59 // TextureReader = GetComponent<TextureReader>(); 60 // TextureReader.OnImageAvailableCallback += OnImageAvailableCallbackFunc; 61 } 62 63 private void OnImageAvailableCallbackFunc(TextureReaderApi.ImageFormatType format, int width, int height, IntPtr pixelBuffer, int bufferSize) 64 { 65 this.format = format; 66 this.width = width; 67 this.height = height; 68 this.pixelBuffer = pixelBuffer; 69 this.bufferSize = bufferSize; 70 } 71 72 73 void Start() 74 { 75 // AwakeからStartへ初期化を移動(TextureRender.csの初期化がStartのため) 76 TextureReader = GetComponent<TextureReader>(); 77 TextureReader.OnImageAvailableCallback += OnImageAvailableCallbackFunc; 78 79 } 80 81 82 void Update() 83 { 84 85 86//----------------------------------------------------------------このあたりの処理をupdateに書いているため処理が重くなっていると考える.しかし,それ以外の方法が分からない. 87 88 new_tex = new Texture2D(height, width, TextureFormat.RGBA32, false); 89 new_tex = FrameTexture;//FrameTexture関数内で取得している_texをnew_texに代入する._texにandroid端末がリアルタイム撮影している画像データが入っている. 90 91 92 //Matを生成する 93 imgMat = new Mat(new_tex.height, new_tex.width, CvType.CV_8UC3); 94 Utils.texture2DToMat(new_tex, imgMat); 95 96 //ガウシアンフィルターによる平滑化 97 imgMat2 = new Mat(new_tex.height, new_tex.width, CvType.CV_8UC3); 98 Imgproc.GaussianBlur(imgMat, imgMat2, new Size(5, 5), 0); 99 100 // BGRからHSVに変換 101 hsvMat = new Mat(new_tex.height, new_tex.width, CvType.CV_8UC3);//CV_8UC3?? 102 Imgproc.cvtColor(imgMat2, hsvMat, Imgproc.COLOR_RGB2HSV); 103 104 //medianBlur化 105 blurMat = new Mat(new_tex.height, new_tex.width, CvType.CV_8UC3);//CV_8CU3?? 106 Imgproc.medianBlur(hsvMat, blurMat, 3); 107 108 //画像の平坦化 109 Imgproc.equalizeHist(blurMat, blurMat); 110 111 out_tex = new Texture2D(imgMat.cols(), imgMat.rows(), TextureFormat.RGBA32, false); 112 Utils.matToTexture2D(blurMat, out_tex); 113 114 gameObject.GetComponent<Renderer>().material.mainTexture = out_tex;//画面上のcubeに貼り付ける 115 116//-------------------------------------------------------------------------------------------- 117 118 119 120 121 // TextureReaderにカメラ画像のサイズをセットする。実行は一回だけ 122 if (!setFrameSizeToTextureReader) 123 { 124 using (var image = Frame.CameraImage.AcquireCameraImageBytes()) 125 { 126 if (!image.IsAvailable) 127 { 128 return; 129 } 130 131 TextureReader.ImageWidth = (int)(image.Width * TextureSizeRatio); 132 TextureReader.ImageHeight = (int)(image.Height * TextureSizeRatio); 133 TextureReader.Apply(); 134 135 setFrameSizeToTextureReader = true; 136 } 137 } 138 } 139 140 public bool isRed(float x, float y) 141 { 142 // グレイスケールが選択されているとき 143 if (format == TextureReaderApi.ImageFormatType.ImageFormatGrayscale) 144 { 145 return false; 146 } 147 148 // TextureReaderが取得した画像データのポインタからデータを取得 149 byte[] data = new byte[bufferSize]; 150 System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, data, 0, bufferSize); 151 // 向きが270回転と反転しているので補正する 152 byte[] correctedData = Rotate90AndFlip(data, width, height, format == TextureReaderApi.ImageFormatType.ImageFormatGrayscale); 153 154 // サイズを合わせる 155 int x_arg = (int)(x / 2.25); 156 int y_arg = (int)(y / 3); 157 int w_arg = height; 158 int h_arg = width; 159 int ch = 4; 160 int th = 50; 161 int idx = (y_arg * w_arg * ch) + (x_arg * ch); 162 163 // 画素値を取得 164 int r = correctedData[idx + 0]; 165 int g = correctedData[idx + 1]; 166 int b = correctedData[idx + 2]; 167 168 // 簡易的に red 検出 169 int diff_rg = r - g; 170 int diff_rb = r - b; 171 if ((diff_rg > th) & (diff_rb > th)) 172 { 173 return true; 174 } 175 return false; 176 } 177 178 public Texture2D FrameTexture 179 { 180 get 181 { 182 if (bufferSize != 0) 183 { 184 // TextureReaderが取得した画像データのポインタからデータを取得 185 byte[] data = new byte[bufferSize]; 186 System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, data, 0, bufferSize); 187 // 向きが270回転と反転しているので補正する 188 byte[] correctedData = Rotate90AndFlip(data, width, height, format == TextureReaderApi.ImageFormatType.ImageFormatGrayscale); 189 // Texture2Dを作成 90度回転させているのでwidth/heightを入れ替える 190 Texture2D _tex = new Texture2D(height, width, TextureFormat.RGBA32, false, false); 191 _tex.LoadRawTextureData(correctedData); 192 _tex.Apply(); 193 194 return _tex; 195 } 196 else 197 { 198 return null; 199 } 200 } 201 } 202 203 private byte[] Rotate90AndFlip(byte[] img, int width, int height, bool isGrayscale) 204 { 205 int srcChannels = isGrayscale ? 1 : 4; 206 int dstChannels = 4; //出力は常にRGBA32にする 207 byte[] newImg = new byte[width * height * dstChannels]; 208 209 for (int i = 0; i < height; i++) 210 { 211 for (int j = 0; j < width; j++) 212 { 213 //imgのindex 214 int p = (i * width + j) * srcChannels; 215 216 //newImgに対するindex. 90度回転と反転を入れている 217 int np = ((width - j - 1) * height + (height - i - 1)) * dstChannels; 218 219 // グレースケールでもRGBで扱えるようにしておく 220 if (isGrayscale) 221 { 222 newImg[np] = img[p]; // R 223 newImg[np + 1] = img[p]; // G 224 newImg[np + 2] = img[p]; // B 225 newImg[np + 3] = 255; // A 226 } 227 else 228 { 229 for (int c = 0; c < dstChannels; c++) 230 { 231 newImg[np + c] = img[p + c]; 232 } 233 } 234 } 235 } 236 237 return newImg; 238 } 239}
吐かれているエラー
- Adreno-GSL: <gsl_memory_alloc_pure:2270>: GSL MEM ERROR: kgsl_sharedmem_alloc ioctl failed.
- Unity : OPENGL NATIVE PLUG-IN ERROR: GL_OUT_OF_MEMORY: Not enough memory left to execute command
- Unity : (Filename: ./Runtime/GfxDevice/opengles/GfxDeviceGLES.cpp Line: 354)
- Unity : Could not allocate memory: System out of memory!
- Unity : Trying to allocate: 1228804B with 16 alignment. MemoryLabel: Texture
- Unity : Allocation happened at: Line:78 in Unity : Memory overview