Visualizzazione post con etichetta Opencv. Mostra tutti i post
Visualizzazione post con etichetta Opencv. Mostra tutti i post

giovedì 24 marzo 2022

Stima distanze con Aruco Tags ed OpenCV

Ho voluto provare questo progetto

https://github.com/GSNCodes/ArUCo-Markers-Pose-Estimation-Generation-Python

ho provato ad usare la webcam del portatile per provare a stimare le distanze mediante i tag Aruco (sono concettualmente simili a QrCode ma piu' robusti per il riconoscimento a scapito del numero di dati inseriti...di fatto solo un codice ID)..altri progetti usano gli AprilTag

Attenzione : e' necessario Python3

Per generare i tag si usa il codice sottostante

python3 generate_aruco_tags.py --id 24 --type DICT_4X4_50 --output tags/ -s 230

id = codice inserito nel tag
type = formato del tag (ne esistono molti...si puo' vedere l'elenco nel file utils.py)
output = e' il folder dove sono salvati i file png dei tag
s = dimensioni del tag

Esempio di tag

Il passo successivo e' quello di crea i file di calibrazione della camera. Si usa una scacchiera (checkerboard)...ho fatto alcune prove ed alcune immagini non funzionano ...ho usato quella proposta da OpenCv in formato 9x6
Si devono fare fotografie in cui si vede la scacchiera posizionata a diversa distanza e con diverse angolazioni e si mettono in una sottodirectory 

Checkerboard

Una delle immagini di calibrazione

Dopo aver raccolto una ventina di immagini di calibrazione (nel mio caso salvato nel folder pose) si lancia

python3 calibration.py --dir './pose/' -s 0.023  -w 9 -t 6

se tutto va a buon fine saranno create i file della matrice di calibrazione e dei coefficienti di distorsione


A questo punto si puo' procedere con la stima di distanza

python3 pose_estimation.py --K_Matrix calibration_matrix.npy --D_Coeff distortion_coefficients.npy --type DICT_4X4_50



il codice e' leggermente differente dal progetto originario perche' di fatto in origine vengono calcolate le matrici rvec e tvec ma non viene effettuato il calcolo della distanza. Le modifiche possono essere ritrovate al mio GitHub https://github.com/c1p81/Aruco_distance




giovedì 15 febbraio 2018

Esperimenti con filtri Canny ed Hough Lines con OpenCV







----------------------------------------------------------------------------------------------





----------------------------------------------------------------------------------------------
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('casa.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# parametri automatici Canny
#ret,th = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
#edges = cv2.Canny(img,ret,ret*0.5)

v = np.median(gray)
sigma = 0.3
lower = int(max(0,(1.0-sigma)*v))
upper = int(max(0,(1.0+sigma)*v))

print lower
print upper

edges = cv2.Canny(img,lower,upper)

cv2.imwrite('canny_casa.jpg',edges)


lines = cv2.HoughLines(edges,1.5,np.pi/180,370)
for rho,theta in lines[0]:
    a = np.cos(theta)
    b = np.sin(theta)
    x0 = a*rho
    y0 = b*rho
    x1 = int(x0 + 1000*(-b))
    y1 = int(y0 + 1000*(a))
    x2 = int(x0 - 1000*(-b))
    y2 = int(y0 - 1000*(a))

    cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)

cv2.imwrite('risultato_casa.jpg',img)



lunedì 12 febbraio 2018

Realtime Hough Lines con OpenCV su Android

L'idea e' quella di utilizzare OpenCV per estrarre le linee in realtime dalle immagini riprese dalla fotocamera del telefono



Dopo avere impostato un nuovo progetto OpenCV su Android come visto qui ho modificato il secondo esempo (mixed_sample)

Un po' di commenti
1) il flusso di lavoro prevede di prendere l'immagine dalla fotocamera, convertirla in scala di grigi, applicare il filtro Canny, applicare l'algoritmo delle Hough Lines,  selezionare solo le linee maggiore di un valore limite (altrimenti l'algoritmo diventa rumoroso), calcolare il coefficiente angolare di ogni linea per creare 5 classi angolari (-90°/-60°,-60°/-30°,-30°/0°,0°/30°,30°/60°,60°/90°),  popolare una textbox in altro a sinistra con i valori delle classi,sovrapporre le Hough Lines all'immagine RGB (Img.procline) e mostrare il tutto sullo schermo

2) per poter modificare la textbox si deve procedere mediante RunonUIThread




uno dei problemini che si hanno usando i filtri con i parametri cosi' come sono impostati e' che
in scene reali complesse, il filtro canny puo' comportarsi in questo modo


rendendo totalmente inutile la ricerca delle houghlines. Per risolvere questo problema possono essere calcolati i valori di threshold in modo automatico dalla deviazione media dell'immagine come segue

MatOfDouble mu = new MatOfDouble();
MatOfDouble sigma = new MatOfDouble();

Core.meanStdDev(inputFrame.gray(),mu,sigma);

Mat lines = new Mat();
Imgproc.Canny(inputFrame.gray(), mIntermediateMat, mu.get(0,0)[0] - sigma.get(0,0)[0], mu.get(0,0)[0] - sigma.get(0,0)[0]); 

per fare le cose in modo piu' pulito possono essere usate le funzioni di OpenCV come il filtro adattativo

Imgproc.adaptiveThreshold(inputFrame.gray(),result,255,
Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_OTSU,15,4)


Layout
---------------------------------
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    
xmlns:tools="http://schemas.android.com/tools"   
 xmlns:opencv="http://schemas.android.com/apk/res-auto"  
  android:layout_width="match_parent"    
android:layout_height="match_parent" >

    <org.opencv.android.JavaCameraView        
android:layout_width="fill_parent"        
android:layout_height="fill_parent"        
android:visibility="gone"        
android:id="@+id/tutorial1_activity_java_surface_view"        
opencv:show_fps="true"        
opencv:camera_id="any" />

    <LinearLayout        
android:layout_width="match_parent"        
android:layout_height="match_parent"        
android:orientation = "vertical" >

        <TextView            
android:layout_width="wrap_content"            
android:layout_height="wrap_content"            
android:id="@+id/fps_text_view"            
android:textSize="12sp"
            android:text="FPS:" />

    </LinearLayout>



</FrameLayout>
---------------------------------
Codice
---------------------------------

import android.app.Activity;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.widget.TextView;

import org.opencv.android.OpenCVLoader;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

import java.util.function.DoubleToLongFunction;

public class MainActivity extends Activity implements CvCameraViewListener2 {

    private static final String    TAG = "OCVSample::Activity";

    private static final int       VIEW_MODE_RGBA     = 0;
    private static final int       VIEW_MODE_GRAY     = 1;
    private static final int       VIEW_MODE_CANNY    = 2;
    private static final int       VIEW_MODE_FEATURES = 5;
    private static final int       VIEW_MODE_HOUGH = 3;


    private int                    mViewMode;
    private Mat                    mRgba;
    private Mat                    mIntermediateMat;
    private Mat                    mGray;

    private MenuItem               mItemPreviewRGBA;
    private MenuItem               mItemPreviewGray;
    private MenuItem               mItemPreviewCanny;
    private MenuItem               mItemPreviewFeatures;

    private CameraBridgeViewBase   mOpenCvCameraView;

    int [] classi = new int[10];


    int threshold = 50;
    int minLineSize = 20;
    int lineGap = 10;


    private BaseLoaderCallback  mLoaderCallback = new BaseLoaderCallback(this) {
        @Override        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS:
                {
                    Log.i(TAG, "OpenCV loaded successfully");

                    mOpenCvCameraView.enableView();
                } break;
                default:
                {
                    super.onManagerConnected(status);
                } break;
            }
        }
    };
    private TextView testo;


    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {
        Log.i(TAG, "called onCreate");
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        testo = (TextView) findViewById(R.id.fps_text_view);
        testo.setTextColor(Color.RED);


        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial1_activity_java_surface_view);
        mOpenCvCameraView.setVisibility(CameraBridgeViewBase.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
        mViewMode = VIEW_MODE_HOUGH;
    }

    @Override    public boolean onCreateOptionsMenu(Menu menu) {
        Log.i(TAG, "called onCreateOptionsMenu");
        mItemPreviewRGBA = menu.add("Preview RGBA");
        mItemPreviewGray = menu.add("Preview GRAY");
        mItemPreviewCanny = menu.add("Canny");
        mItemPreviewFeatures = menu.add("Find features");
        return true;
    }

    @Override    public void onPause()
    {
        super.onPause();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    @Override    public void onResume()
    {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
        mRgba = new Mat(height, width, CvType.CV_8UC4);
        mIntermediateMat = new Mat(height, width, CvType.CV_8UC4);
        mGray = new Mat(height, width, CvType.CV_8UC1);
    }

    public void onCameraViewStopped() {
        mRgba.release();
        mGray.release();
        mIntermediateMat.release();
    }

    public static boolean isBetween(double x, double lower, double upper) {
        return lower <= x && x <= upper;
    }


    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        final int viewMode = mViewMode;
        switch (viewMode) {
            case VIEW_MODE_GRAY:
                // input frame has gray scale format                
               Imgproc.cvtColor(inputFrame.gray(), mRgba, Imgproc.COLOR_GRAY2RGBA, 4);
                break;
            case VIEW_MODE_RGBA:
                // input frame has RBGA format                
               mRgba = inputFrame.rgba();
                break;
            case VIEW_MODE_CANNY:
                // input frame has gray scale format                mRgba = inputFrame.rgba();
                Imgproc.Canny(inputFrame.gray(), mIntermediateMat, 80, 100);
                Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2RGBA, 4);


                break;
            case VIEW_MODE_HOUGH:

                mRgba = inputFrame.rgba();

                Mat lines = new Mat();
                Imgproc.Canny(inputFrame.gray(), mIntermediateMat, 80, 100);
                Imgproc.HoughLinesP(mIntermediateMat,lines, 1, Math.PI/180, threshold,minLineSize, lineGap);


                Log.d("Linee", Integer.toString(lines.rows()));



                for (int x = 0; x < lines.rows(); x++) {
                    double[] vec = lines.get(x, 0);
                    double x1 = vec[0],
                            y1 = vec[1],
                            x2 = vec[2],
                            y2 = vec[3];
                    Point start = new Point(x1, y1);
                    Point end = new Point(x2, y2);
                    double dx = x1 - x2;
                    double dy = y1 - y2;

                    double m = Math.toDegrees(Math.atan(dy / dx));
                    Log.d("Angolo", Double.toString(m));

                    double dist = Math.sqrt(dx * dx + dy * dy);
                    Log.d("Distanza", Double.toString(dist));


                    //calcola le classi in base all'angolo del coefficiente angolare
                    if (dist > 50.d)                    {
                                if (isBetween(m, -90.0, -60.0)) {
                                    classi[0] = classi[0] + 1;
                                }

                                if (isBetween(m, -60.0, -30.0)) {
                                    classi[1] = classi[1] + 1;
                                }


                                if (isBetween(m, -30.0, 0.0)) {
                                    classi[2] = classi[2] + 1;
                                }

                                if (isBetween(m, 0.0, 30.0)) {
                                    classi[3] = classi[3] + 1;
                                }

                                if (isBetween(m, 30.0, 60.0)) {
                                    classi[4] = classi[4] + 1;
                                }

                                if (isBetween(m, 60.0, 90.0)) {
                                    classi[5] = classi[5] + 1;
                                }
                        //sovrappone le Hough Lines
                        Imgproc.line(mRgba, start, end, new Scalar(255,0, 0, 1),2);
                }


                 

                }



                break;
            case VIEW_MODE_FEATURES:
                // input frame has RGBA format                mRgba = inputFrame.rgba();
                mGray = inputFrame.gray();
                break;
        }


        runOnUiThread(new Runnable() {
            @Override            public void run() {
                testo.setText(" -90/-60 "+Integer.toString(classi[0])+"\n" +
                              " -60/-30 "+Integer.toString(classi[1])+"\n" +
                              " -30/0   "+Integer.toString(classi[2])+"\n" +
                              "   0/30  "+Integer.toString(classi[3])+"\n" +
                              "  30/60  "+Integer.toString(classi[4])+"\n" +
                              "  60/90  "+Integer.toString(classi[5])+"\n"
                );
            }
        });

        return mRgba;
    }

    public boolean onOptionsItemSelected(MenuItem item) {
        Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);

        if (item == mItemPreviewRGBA) {
            mViewMode = VIEW_MODE_RGBA;
        } else if (item == mItemPreviewGray) {
            mViewMode = VIEW_MODE_GRAY;
        } else if (item == mItemPreviewCanny) {
            mViewMode = VIEW_MODE_CANNY;
        } else if (item == mItemPreviewFeatures) {
            mViewMode = VIEW_MODE_FEATURES;
        }

        return true;
    }


}
---------------------------------

Opencv Android SDK su Android Studio

per utilizzare OpenCV su Android mediante l'SDK si procede prima scaricando il voluminoso pacchetto (sono oltre 300 Mb ma sono contenuti gli apk di tutte le applicazioni demo, la documentazione e le librerie compilate per differenti piattaforme hardware)

A questo punto da Android Studio (devono esserer gia' installati NDK e CMake) si crea un normale progetto ... poi /File/New/Import Module e si cerca la directory nell'SDK opencv/sdk/java

Si deve poi editare il file opencv/build.gradle

CompileSDKVersion 23
buildToolsVersion "23.0.2"
TargetSDKVersion 23

fatto cio' si mette la visualizzazione dell'albero di destra (in Project Files) si seleziona Android/App/tasto destro/Open Module Settings/Dependecies/tasto +/Module Dependencies/ e si seleziona OpenCV Library


si va poi in App/New/Folder/JNIFolder/Change Folder Location e si seleziona /src/main/jniLibs

si apre il folder e si copiano tutte le sottodirectories dall'SDK opncv/sdk/native/libs/ cancellando poi cio' che non e' *.so

Per provare se se le cose funzionavano ho provato ad usare il secondo esempio dell'SDK (quello che implementa il filtro Canny in realtime) ed il programma di esempio si ostinava a non funzionare. Dopo un po' di tempo perso a pigiare tasti ho scoperto che commentando la riga

System.loadLibrary("mixed_sample")

tutto funziona correttamente


venerdì 20 maggio 2016

Track Object con OpenCv ed IPCamera PTZ in realtime

L'idea alla base di questo post e' quella di usare OpenCV per riconoscere un oggetto in stream video da una IP camera dotata di movimento PTZ in modo che l'oggetto rimanga sempre al centro dell'inquadratura

Come camera ho ripreso la Maygion (gia' vista qui, un modello piuttosto vecchiotto da 0.3MPx) e l'oggetto da tracciare era la scatola a sinistra

Tramite OpenCV viene catturato lo stream video della camera IP, l'immagine viene elaborato alla ricerca del colore verde calcolando il centroide, a questo punto si calcola la posizione del centroide rispetto al centro dell'immagine e si pilota la camera per posizionare il centroide piu' vicino possibile al centro dell'immagine.


Questo e' il video della prova

Per rendere le cose piu' semplici, in questo primo esempio viene mostrato come catturato il flusso derivante dalla camera Ip mediante OpenCV. Di fatto il sistema e' praticamente a quello che si applica ad una webcam con la sola differenza che si deve passare l'url dello stream (variabile da produttore a produttore e da modello a modello)
---------------------------------------------
import numpy as np
import cv2

vcap = cv2.VideoCapture("http://192.168.43.207:81/videostream.cgi?stream=0&usr=admin&pwd=admin")


if vcap.isOpened():

rval,frame = vcap.read()
print "acquisito"
else:
rval = False
print "non acquisito"

while rval:

cv2.imshow("preview",frame)
rval,frame = vcap.read()
key = cv2.waitKey(20)
if key == 27:
break
cv2.destroyWindow("preview")
---------------------------------------------

Questo e' invece il codice per pilotare il movimento della camera (in pratica si mandano comandi via stringa http)
---------------------------------------------
import time
try:
    from urllib.request import urlopen
except ImportError:
    from urllib2 import urlopen

ur = "http://192.168.43.207:81/"


direzione = "up"



html=urlopen(ur+"moveptz.xml?dir="+direzione+"&user=admin&password=admin")

time.sleep(0.1)
html=urlopen(ur+"moveptz.xml?dir=stop&user=admin&password=admin")
---------------------------------------------

Per tracciare il movimento della sagoma verde ho ripreso, modificandolo leggermente, questo esempio di Conan Zhao and Simon D. Levy
---------------------------------------------
import cv2
import numpy as np
import time

try:

    from urllib.request import urlopen
except ImportError:
    from urllib2 import urlopen

ur = "http://192.168.43.207:81/"



# For OpenCV2 image display

WINDOW_NAME = 'GreenBallTracker' 

def track(image):

    # Blur the image to reduce noise
    blur = cv2.GaussianBlur(image, (5,5),0)

    # Convert BGR to HSV

    hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)

    # Threshold the HSV image for only green colors

    lower_green = np.array([40,70,70])
    upper_green = np.array([80,200,200])

    # Threshold the HSV image to get only green colors

    mask = cv2.inRange(hsv, lower_green, upper_green)
    
    # Blur the mask
    bmask = cv2.GaussianBlur(mask, (5,5),0)

    # Take the moments to get the centroid

    moments = cv2.moments(bmask)
    m00 = moments['m00']
    centroid_x, centroid_y = None, None
    if m00 != 0:
        centroid_x = int(moments['m10']/m00)
        centroid_y = int(moments['m01']/m00)

    # Assume no centroid

    ctr = (-1,-1)

    # Use centroid if it exists

    if centroid_x != None and centroid_y != None:

        ctr = (centroid_x, centroid_y)


        # Put black circle in at centroid in image

        cv2.circle(image, ctr, 4, (255,0,0))
x_schermo = centroid_x-320
y_schermo = centroid_x-240
print "X="+str(x_schermo) + " ; Y=" + str(y_schermo)
tempo = 0.02
if (x_schermo > 0): 
html=urlopen(ur+"moveptz.xml?dir=left&user=admin&password=admin")
time.sleep(tempo)
html=urlopen(ur+"moveptz.xml?dir=stop&user=admin&password=admin")
else: 
html=urlopen(ur+"moveptz.xml?dir=right&user=admin&password=admin")
time.sleep(tempo)
html=urlopen(ur+"moveptz.xml?dir=stop&user=admin&password=admin")


    # Display full-color image

    cv2.imshow(WINDOW_NAME, image)

    # Force image display, setting centroid to None on ESC key input

    if cv2.waitKey(1) & 0xFF == 27:
        ctr = None
    
    # Return coordinates of centroid
    return ctr

# Test with input from camera

if __name__ == '__main__':

    capture = cv2.VideoCapture(ur+"videostream.cgi?stream=0&usr=admin&pwd=admin")


    while True:


        okay, image = capture.read()


        if okay:


            if not track(image):

                break
          
            if cv2.waitKey(1) & 0xFF == 27:
                break

        else:


           print('Capture failed')

           break

---------------------------------------------

Opencv camera calibration in cpp

Oltre che con uno script Python come visto qui la calibrazione della camera si puo' fare anche con il programma in CPP Questo il proce...