回答編集履歴
3
d
    
        answer	
    CHANGED
    
    | @@ -143,7 +143,8 @@ | |
| 143 143 | 
             
            print(np.allclose(std1, std2))  # True
         | 
| 144 144 | 
             
            ```
         | 
| 145 145 |  | 
| 146 | 
            +
            > 30秒の動画があってR+G+B/3が大きいところほど強度が高いです(赤です)
         | 
| 146 | 
            -
            >
         | 
| 147 | 
            +
            > しかしながら全体的に強度の変化もないので標準偏差は小さくなるはずです(青になる)
         | 
| 147 148 |  | 
| 148 149 | 
             
            ```python
         | 
| 149 150 | 
             
            import matplotlib.pyplot as plt
         | 
| @@ -156,8 +157,5 @@ | |
| 156 157 | 
             
            plt.show()
         | 
| 157 158 | 
             
            ```
         | 
| 158 159 |  | 
| 159 | 
            -
            > 30秒の動画があってR+G+B/3が大きいところほど強度が高いです(赤です)
         | 
| 160 | 
            -
            > しかしながら全体的に強度の変化もないので標準偏差は小さくなるはずです(青になる)
         | 
| 161 | 
            -
             | 
| 162 160 | 
             
            標準偏差が高い = 赤とは限らないです。色は matplotlib で可視化した際のカラーマップをどう設定したかによります。
         | 
| 163 161 | 
             
            例えば、質問の画像だと右側のカラーバーを見る限り、標準偏差が小さいと赤、大きいと青になっています。
         | 
2
d
    
        answer	
    CHANGED
    
    | @@ -88,4 +88,76 @@ | |
| 88 88 | 
             
            ax.imshow(std)
         | 
| 89 89 |  | 
| 90 90 | 
             
            plt.show()
         | 
| 91 | 
            -
            ```
         | 
| 91 | 
            +
            ```
         | 
| 92 | 
            +
             | 
| 93 | 
            +
            ## 追記
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            > しかしながら出力画像を見てみると標準偏差ではないように思えます
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            追記した方法だと計算結果が違うというご指摘でしょうか?
         | 
| 98 | 
            +
            一応、下記方法で最初に回答した方法 (方法1) と追記した方法 (方法2) で計算結果が一致することは確認しています。
         | 
| 99 | 
            +
             | 
| 100 | 
            +
            ```python
         | 
| 101 | 
            +
            import cv2
         | 
| 102 | 
            +
            import numpy as np
         | 
| 103 | 
            +
             | 
| 104 | 
            +
            cap = cv2.VideoCapture("vtest.avi")
         | 
| 105 | 
            +
             | 
| 106 | 
            +
            # 方法1
         | 
| 107 | 
            +
            ##################################
         | 
| 108 | 
            +
            frames = []
         | 
| 109 | 
            +
            while True:
         | 
| 110 | 
            +
                ret, frame = cap.read()
         | 
| 111 | 
            +
                if not ret:
         | 
| 112 | 
            +
                    break
         | 
| 113 | 
            +
                frames.append(frame)
         | 
| 114 | 
            +
            frames = np.array(frames)
         | 
| 115 | 
            +
             | 
| 116 | 
            +
            std1 = frames.std(axis=(0, 3))
         | 
| 117 | 
            +
             | 
| 118 | 
            +
            # 方法2
         | 
| 119 | 
            +
            ##################################
         | 
| 120 | 
            +
            cap = cv2.VideoCapture("vtest.avi")
         | 
| 121 | 
            +
             | 
| 122 | 
            +
            w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # フレームの幅
         | 
| 123 | 
            +
            h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # フレームの高さ
         | 
| 124 | 
            +
            num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # フレーム数
         | 
| 125 | 
            +
             | 
| 126 | 
            +
            frame_sum = np.zeros((h, w))  # 各画素の和を格納する配列
         | 
| 127 | 
            +
            frame_squared_sum = np.zeros((h, w))  # 各画素の平方和を格納する配列
         | 
| 128 | 
            +
             | 
| 129 | 
            +
            while True:
         | 
| 130 | 
            +
                ret, frame = cap.read()
         | 
| 131 | 
            +
                if not ret:
         | 
| 132 | 
            +
                    break
         | 
| 133 | 
            +
                frame = frame.astype(float)  # uint8 -> float (uint8 型で frame**2 すると、overflow するため)
         | 
| 134 | 
            +
                frame_sum += frame.sum(axis=-1)  # 和を計算する。
         | 
| 135 | 
            +
                frame_squared_sum += (frame ** 2).sum(axis=-1)  # 自乗和を計算する。
         | 
| 136 | 
            +
             | 
| 137 | 
            +
            mean = frame_sum / (num_frames * 3)
         | 
| 138 | 
            +
            var = frame_squared_sum / (num_frames * 3) - mean ** 2
         | 
| 139 | 
            +
            std2 = np.sqrt(var)
         | 
| 140 | 
            +
             | 
| 141 | 
            +
            ##################################
         | 
| 142 | 
            +
            # 方法1 と 方法2 の計算結果が一致することを確認
         | 
| 143 | 
            +
            print(np.allclose(std1, std2))  # True
         | 
| 144 | 
            +
            ```
         | 
| 145 | 
            +
             | 
| 146 | 
            +
            >
         | 
| 147 | 
            +
             | 
| 148 | 
            +
            ```python
         | 
| 149 | 
            +
            import matplotlib.pyplot as plt
         | 
| 150 | 
            +
             | 
| 151 | 
            +
            # ヒートマップで描画する。
         | 
| 152 | 
            +
            fig, ax = plt.subplots()
         | 
| 153 | 
            +
            img = ax.imshow(std)
         | 
| 154 | 
            +
            fig.colorbar(img)
         | 
| 155 | 
            +
             | 
| 156 | 
            +
            plt.show()
         | 
| 157 | 
            +
            ```
         | 
| 158 | 
            +
             | 
| 159 | 
            +
            > 30秒の動画があってR+G+B/3が大きいところほど強度が高いです(赤です)
         | 
| 160 | 
            +
            > しかしながら全体的に強度の変化もないので標準偏差は小さくなるはずです(青になる)
         | 
| 161 | 
            +
             | 
| 162 | 
            +
            標準偏差が高い = 赤とは限らないです。色は matplotlib で可視化した際のカラーマップをどう設定したかによります。
         | 
| 163 | 
            +
            例えば、質問の画像だと右側のカラーバーを見る限り、標準偏差が小さいと赤、大きいと青になっています。
         | 
1
d
    
        answer	
    CHANGED
    
    | @@ -43,4 +43,49 @@ | |
| 43 43 | 
             
            plt.show()
         | 
| 44 44 | 
             
            ```
         | 
| 45 45 |  | 
| 46 | 
            -
            
         | 
| 46 | 
            +
            
         | 
| 47 | 
            +
             | 
| 48 | 
            +
            ## 追記
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            > 800×800だとメモリーエラーとなります。
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            標準偏差を計算するのに必要なのは、各画素の和と自乗和だけなので、以下のようにすることでフレームの長さに関わらず、使用するメモリ量を固定できます。
         | 
| 53 | 
            +
             | 
| 54 | 
            +
            標準偏差は分散の平方根なので、分散を以下の分散公式で求めればよいです。
         | 
| 55 | 
            +
            式の導出はネット上に沢山情報があるので省略します。
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            [分散の意味と求め方、分散公式の使い方](https://sci-pursuit.com/math/statistics/variance.html)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
            ```python
         | 
| 60 | 
            +
            import cv2
         | 
| 61 | 
            +
            import matplotlib.pyplot as plt
         | 
| 62 | 
            +
            import numpy as np
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            cap = cv2.VideoCapture("vtest.avi")
         | 
| 65 | 
            +
             | 
| 66 | 
            +
            w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))  # フレームの幅
         | 
| 67 | 
            +
            h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))  # フレームの高さ
         | 
| 68 | 
            +
            num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # フレーム数
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            frame_sum = np.zeros((h, w))  # 各画素の和を格納する配列
         | 
| 71 | 
            +
            frame_squared_sum = np.zeros((h, w))  # 各画素の平方和を格納する配列
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            while True:
         | 
| 74 | 
            +
                ret, frame = cap.read()
         | 
| 75 | 
            +
                if not ret:
         | 
| 76 | 
            +
                    break
         | 
| 77 | 
            +
                
         | 
| 78 | 
            +
                frame = frame.astype(float)  # uint8 -> float (uint8 型で frame**2 すると、overflow するため)
         | 
| 79 | 
            +
                frame_sum += frame.sum(axis=-1)  # 和を計算する。
         | 
| 80 | 
            +
                frame_squared_sum += (frame ** 2).sum(axis=-1)  # 自乗和を計算する。
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            mean = frame_sum / (num_frames * 3)
         | 
| 83 | 
            +
            var = frame_squared_sum / (num_frames * 3) - mean ** 2
         | 
| 84 | 
            +
            std = np.sqrt(var)
         | 
| 85 | 
            +
             | 
| 86 | 
            +
            # ヒートマップで描画する。
         | 
| 87 | 
            +
            fig, ax = plt.subplots()
         | 
| 88 | 
            +
            ax.imshow(std)
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            plt.show()
         | 
| 91 | 
            +
            ```
         | 
