koshian0129
@koshian0129

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

エッジ検出による三角構図の検出をしようと考えていますがうまくいきません

ダウンロード.png
[0, 0, 0, ... , 0, 0, 0, 1080.0, 1080.0, 1080.0, ... , 1080.0, 1080.0, 1080.0]
↑出力結果

前提

初心者ですのでわからない部分もありますが、ご容赦ください。
Pythonで山岳風景動画を入力後、(1フレームではなく)1秒ごとに切り取って全てを画像にした後、一枚一枚画像にエッジ検出をかけてその中から各画像ごとに三角構図を作り出すプログラムを書きたいです。
具体的な質問をする前にイメージを持ってもらいたいので検出方法を説明させてください。私の想定では上のようなエッジ検出された画像の中で、山の頂上(エッジの中で一番y軸が高いところとして判定)、画像一番左端にくるエッジの中で一番y軸が高いところ(画像で言えば大体 (0,320)くらいのところ)、画像一番右端にくるエッジの中で一番y軸が高いところ(画像で言えば大体 (1850,300)くらいのところ)、の三点を結んで三角形を作りたいです。
添付している画像は複数枚出力される結果のうち一番最初に出てきた画像です。

解決したいこと

出力にかける時間を減らすのと、きちんとしたy座標が描画されるようにするにはどこにどう修正を加えれば良いでしょうか?
下のコードで実行すると出力に大幅に時間がかかるほか、出力されたどの結果も各y座標がすべて同じような高さになってしまいます。
エラーはありませんが、実行結果がおかしいです。三角形を作るはずが一直線になってしまいました。エッジ検出は添付画像の通りうまく作動していると思います。

発生している問題・エラー

出力結果は添付画像とセットで上においています(処理が遅く、一枚目の結果のみになります)

...の部分は省略しました

該当するソースコード

from numpy.ma.core import sqrt
import cv2
import sys
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

class Vector2:
  x = 0
  y = 0

class Triangle:
  ver = Vector2()
  r = Vector2()
  l = Vector2()

def dacor(yRec, x):
  if abs(yRec[x] - yRec[x - 1]) > 200:
   return True;
  else:
   return False;

def area(ver, lMax, rMax):
  dl = sqrt(pow(ver.x - lMax.x , 2) + pow(ver.y - lMax.y , 2))
  dr = sqrt(pow(ver.x - rMax.x , 2) + pow(ver.y - rMax.y , 2))
  db = sqrt(pow(rMax.x - lMax.x , 2) + pow(rMax.y - lMax.y , 2))
  s = 1 / 2 * (dl + db + dr)
  st = sqrt(s * (s - dl) * (s - db) * (s - dr))
  return st;

def saturation(pixel):
  return (pixel.max() - pixel.min()) / pixel.max();

def middle(yRec):
  for item in yRec:
    if item > min(yRec) and item < max(yRec):
      return item;
  return 0;

def functionKOZUESC(frame, width, height, nowframe):

  plt.imshow()    
  plt.show()

  return 0;



def functionKOZU(frame, width, height, nowframe):
  x = 0
  y = 0
  sum = 0
  average = 0
  flag = False
  edgeFlag = False
  yRec = [height] * int(width)
  brack = 255
  tri = Triangle()

  while y < height:
    while x < width :
      sum += saturation(frame[y][x])
      x += 1
    y += 1

  average = sum / (width * height)

  if average < 30:
    highTh = 400
    lowTh = 50
  else:
    highTh = 530
    lowTh = 200

  img_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  edge_img = cv2.Canny(img_gray,  highTh, lowTh)

  flag = False
  x = 0
  y = 0

  for x,i in enumerate(edge_img): 
    for y, j in enumerate(i):
      if j == brack:
        yRec[x] = y
        flag = True
      if x -1 >= 0:
        if not flag:
          yRec[x] = yRec[x - 1]
        if dacor(yRec, x):
          yRec[x] = yRec[x - 1]
      else:
        if not flag:
          yRec[x] = middle(yRec)
    flag = False

  print(yRec)

  x = 0
  edgeFlag = False
  rMax = Vector2()
  lMax = Vector2()
  ver = Vector2()

  while x < width:
    if yRec[x] == min(yRec):
      ver.y = yRec[x]
      ver.x = x
      edgeFlag = True
    if not edgeFlag:
      if yRec[x] > rMax.y:
        lMax.y = yRec[x]
        lMax.x = x
    else:
      if yRec[x] > lMax.y:
        rMax.y = yRec[x]
        rMax.x = x
    x += 1

  plt.imshow(edge_img)    
  plt.show()
  s = area(ver, lMax, rMax)

  if s > 0:
    tri.ver = ver
    tri.r = rMax
    tri.l = lMax


  return tri

cap = cv2.VideoCapture('drive/MyDrive/サンプル動画/sample_video2.mp4')

if not (cap.isOpened()):
  sys.exit('Can not Open VideoFile. Pleas Chack flie path.')

width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
fps = cap.get(cv2.CAP_PROP_FPS)
fcount = cap.get(cv2.CAP_PROP_FRAME_COUNT)
nowframe = 0
kozu = np.array([Triangle()])


while cap.get(cv2.CAP_PROP_POS_FRAMES) < fcount:
  ret, frame = cap.read()
  if not ret and cap.get(cv2.CAP_PROP_POS_FRAMES) <= fcount:
   sys.exit('Error, Can Not Read Frame' + str(nowframe))
  kozu = np.append(kozu ,functionKOZU(frame, width, height, nowframe))
  nowframe += fps
  cap.set(cv2.CAP_PROP_POS_FRAMES, nowframe)

#for item in kozu:
#    print(item.ver.y)
#    print(item.l.y)
#    print(item.r.y)

自分で試したこと

 for x,i in enumerate(edge_img): 
    for y, j in enumerate(i):
      if j == brack:
        yRec[x] = y
        flag = True
      if x -1 >= 0:
        if not flag:
          yRec[x] = yRec[x - 1]
        if dacor(yRec, x):
          yRec[x] = yRec[x - 1]
      else:
        if not flag:
          yRec[x] = middle(yRec)
    flag = False

この部分を

 while x < width:
    while y < height:
        if not flag:
          if edge_img[y][x] == brack:
            yRec[x] = y
            flag = True
       y += 1
    if x -1 >= 0:
      if not flag:
         yRec[x] = yRec[x - 1]
      if dacor(yRec, x):
         yRec[x] = yRec[x - 1]
    else:
      if not flag:
        yRec[x] = middle(yRec)
  x += 1
  flag = False

に置き換えてやってみましたが、ループが終わらないなどうまくいきませんでした。

0

1Answer

edge_imgは(0,1)の画像でしょうか?
山が画像の左端、右端まで続き、その上には信号がないものと仮定しています。
np.nonzeroで配列内の0でない最初のindexを取得できます。

import numpy as np
# 画像左端の一番上の部分のindex
left_edge = edge_img[:, 0]
left_idx = np.nonzero(left_edge)[0][0]
# 画像右端の一番上の部分のindex
right_edge = edge_img[:, -1]
right_idx = np.nonzero(right_edge)[0][0]
# 山頂のindex
# 画像を左右方向に圧縮し一次元化してから山頂のy-indexを取得
collapsed_x = np.sum(edge_img, axis=1) 
summit_idx_y = np.nonzero(collapsed_x)[0][0]
# 山頂のy-indexを通る横線を取得してから山頂のx-indexを取得
summit_y = edge_img[summit_idx_y, :]
summit_idx_x = np.nonzero(summit_y)[0][0]

0Like

Your answer might help someone💌