반응형

텍스트를 음성으로 합성해 주는 TTS(Text To Speech)기능을 사용해 보자.

 

레이아웃에 에디트텍스트와 버튼을 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.example.myapplication;
 
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
 
import androidx.appcompat.app.AppCompatActivity;
 
import java.util.Locale;
 
public class MainActivity extends AppCompatActivity {
 
    TextToSpeech tts;
    EditText editText;
    Button button;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        tts = new TextToSpeech(thisnew TextToSpeech.OnInitListener() {
            @Override
            public void onInit(int status) {
                if (status == TextToSpeech.SUCCESS) {
                    int result = tts.setLanguage(Locale.ENGLISH);
                    // 영어로 설정해도 한글을 읽을 수 있고 영어 발음이 한국어로 설정하는것 보다 낫다.
                    if (result == TextToSpeech.LANG_NOT_SUPPORTED || result == TextToSpeech.LANG_MISSING_DATA) {
                        Log.e("TTS""Language not supported.");
                    } else {
                        button.setText("Ready To Speak");
                    }
                } else {
                    Log.e("TTS""Initialization failed.");
                }
            }
        });
        editText = findViewById(R.id.editText);
        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                CharSequence text = editText.getText();
                tts.setPitch((float)1.0); // Sets the speech pitch for the TextToSpeech engine.
                tts.setSpeechRate((float)1.0); // Sets the speech rate.
 
                tts.speak(text, TextToSpeech.QUEUE_FLUSH, null"uid");
                // QUEUE_ADD - Queue mode where the new entry is added at the end of the playback queue.
                // QUEUE_FLUSH - Queue mode where all entries in the playback queue (media to be played
                // and text to be synthesized) are dropped and replaced by the new entry.
            }
        });
    }
 
    @Override
    protected void onDestroy() {
        if (tts != null) {
            tts.stop();
            // Interrupts the current utterance (whether played or rendered to file) and
            // discards other utterances in the queue.
            tts.shutdown();
            // Releases the resources used by the TextToSpeech engine.
        }
        super.onDestroy();
    }
}
 

 

소스를 입력하고 빌드한다.

 

텍스트를 입력하고 버튼을 터치하면 음성이 출력된다.

※ 참고

TextToSpeech

 

반응형
Posted by J-sean
:
반응형

Message, Handler, Looper를 이용해 메인 스레드에서 다른 스레드로 메세지(데이터)를 보내자.

 

레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            handler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(@NonNull Message msg) {
                    textView.setText(msg.obj + " from My Thread.");
                    // 메인 스레드의 메인 루퍼를 사용하기 때문에
                    // textView()에 바로 접근 가능하다.
                }
            };
        }
    }
}
 

 

메인 루퍼를 이용하는 소스를 작성하고 빌드한다.

 

에디트텍스트에 메세지를 입력하고 버튼을 터치하면 텍스트뷰에 표시된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.handler.sendMessage(message);
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        Handler handler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            handler = new Handler(Looper.myLooper()) {
                // 메인 스레드가 아닌 새로 만든 이 스레드의 루퍼 사용
                @Override
                public void handleMessage(@NonNull Message msg) {
                    String message = msg.obj.toString();
                    // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                    // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                    textView.post(new Runnable() {
                        @Override
                        public void run() {
                            textView.setText(message + " from My Thread");
                        }
                    });
                }
            };
 
            Looper.loop();
        }
    }
}
 

 

메인 루퍼가 아닌 새로 만든 스레드의 루퍼를 생성하고 메세지를 처리하는 소스. 결과는 동일하다.

 

 

(루퍼 종료용)버튼을 하나 더 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText;
    TextView textView;
 
    MyThread myThread;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText = findViewById(R.id.editText);
        textView = findViewById(R.id.textView);
        
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String input = editText.getText().toString();
                Message message = Message.obtain();
                message.obj = input;
 
                myThread.myHandler.sendMessage(message);
            }
        });
 
        Button button2 = findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                myThread.myHandler.StopLooper();
                // 새로 만든 스레드의 루퍼 종료.
            }
        });
 
        myThread = new MyThread();
        myThread.start();
    }
 
    class MyThread extends Thread {
        MyHandler myHandler;
 
        @Override
        public void run() {
            Looper.prepare();
 
            myHandler = new MyHandler();
 
            Looper.loop();
        }
 
        class MyHandler extends Handler {
            MyHandler() {
                super(Looper.myLooper());
            }
 
            @Override
            public void handleMessage(@NonNull Message msg) {
                String message = msg.obj.toString();
                // 새로 만든 이 스레드로 넘어온 메세지 데이터를 미리 처리.
                // 아래 setText()에서 msg.obj를 사용하면 null 값이 대입된다.
 
                textView.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText(message + " from My Thread");
                    }
                });
            }
 
            void StopLooper() {
                myHandler.getLooper().quitSafely();
                // 새로 만든 스레드의 루퍼 종료.
            }
        }
    }
}
 

 

새 스레드의 루퍼를 종료하는 함수가 포함된 파생 Handler 클래스를 사용하는 소스.

Looper.XXX() 호출 시 MyThread 클래스 내부라도 어디서 루퍼를 참조 하느냐에 따라 메인 루퍼가 참조 될 수도 있고 새 스레드의 루퍼가 참조 될 수 있다. (MyThread.Run()에서만 새 스레드의 루퍼가 참조된다)

 

스레드 루퍼 종료 버튼 터치 후 다시 스레드로 보내기 버튼을 터치하면 아래와 같은 경고 발생.

 

새 스레드의 루퍼가 종료(별 다른 작업이 없다면 동시에 새 스레드도 종료) 된 후 핸들링할 메세지를 보내기 때문에 경고가 발생한다. (예외 처리 필요)

 

※ 참고

Looper

 

반응형
Posted by J-sean
:
반응형

안드로이드 UI는 메인 스레드나 UI 스레드 외 다른 스레드에서 조작 할 수 없다. 다른 스레드에서 UI 조작 시 별다른 에러 없이 Virtual Device에서 잘 작동 되더라도 실제 기기에서는 종료되어 버린다. 아니면 테스트 시 Logcat에 아래와 같은 에러가 표시된다.

 

CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

 

텍스트뷰와 버튼을 적당히 배치한다.

 

다른 스레드에서 텍스트뷰에 접근하는 코드를 작성한다.

 

위 예제의 경우 AVD에서는 별 이상없이 동작 하지만 실제 기기에서 버튼을 터치하면 바로 종료되어 버린다. 메인스레드나 UI 스레드가 아닌 다른 스레드에서 텍스트뷰에 접근하기 때문이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity {
 
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                MyThread thread = new MyThread();
                thread.start();
            }
        });
    }
 
    class MyThread extends Thread {
        @Override
        public void run() {
            textView.post(new Runnable() {
                @Override
                public void run() {
                    textView.setText("My Thread.");
                }
            });
        }
    }
}
 

 

위와 같이 소스를 수정하고 빌드한다.

 

문제없이 잘 실행된다.

 

 

1
2
3
4
5
6
7
8
9
10
11
class MyThread extends Thread {
    @Override
    public void run() {
        textView.postDelayed(new Runnable() {
            @Override
            public void run() {
                textView.setText("My Thread");
            }
        }, 5000);
    }
}
 

 

.post()가 아닌 .postDelayed()를 사용하면 Thread.sleep()를 사용하지 않고 일정시간 후 실행할 수 있다. 위 예제는 버튼 터치 5초 후 텍스트를 바꾼다.

 

다른 스레드에서 UI 스레드에 접근 하는 방법은 여러가지가 있다. 아래 링크를 참고하자.

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

프로세스 및 스레드 개요

 

반응형
Posted by J-sean
:
반응형

구글 안드로이드 맵 SDK를 사용해 보자.

 

Google Play services가 설치되어 있지 않다면 설치한다.

 

play-services-maps를 추가한다.

 

AndroidManifest.xml에 API KEY와 GMS(Google Mobile Services) 버전 확인하는 메타 데이터를 추가한다.

 

레이아웃에 fragment를 추가한다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.example.myapplication;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
 
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
 
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                .findFragmentById(R.id.map);
        mapFragment.getMapAsync(this);
    }
 
    @Override
    public void onMapReady(@NonNull GoogleMap googleMap) {
        LatLng curPosition = new LatLng(37.3850143127.1234308);
 
        googleMap.addMarker(new MarkerOptions().position(curPosition).title("Marker"));
        googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(curPosition, 16));
    }
}
 

 

소스를 입력하고 빌드한다.

 

지정한 위치에 마커가 표시되고 카메라가 이동한다.

※ 참고

Android용 Maps SDK 개요

 

반응형
Posted by J-sean
:
반응형

C# WinForm 으로 구글 클라우드 비전 API를 이용해 Detect localized objects in a single image 를 진행해 보자. 아래 링크를 참고해 기본 준비를 한다.

2022.02.11 - [C#] - C# Google Cloud Vision API - 구글 클라우드 비전 API 2

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using Google.Cloud.Vision.V1;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        ImageAnnotatorClient client;
 
        public Form1()
        {
            InitializeComponent();
 
            try
            {
                client = new ImageAnnotatorClientBuilder
                {
                    CredentialsPath = "your_credentials.json"
                }.Build();
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
                Close();
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                System.Drawing.Image systemImage;
 
                OpenFileDialog dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    systemImage = System.Drawing.Image.FromFile(dlg.FileName);
                    pictureBox1.Image = systemImage;
 
                    pictureBox1.Refresh();
 
                    Graphics graphics = pictureBox1.CreateGraphics();
 
                    Google.Cloud.Vision.V1.Image visionImage = Google.Cloud.Vision.V1.Image.FromFile(dlg.FileName);
 
                    IReadOnlyList<LocalizedObjectAnnotation> annotations = client.DetectLocalizedObjects(visionImage);
                    foreach (LocalizedObjectAnnotation annotation in annotations)
                    {
                        Google.Protobuf.Collections.RepeatedField<NormalizedVertex> normalizedVertices =
                            annotation.BoundingPoly.NormalizedVertices;
 
                        int imageWidth = systemImage.Width;
                        int imageHeight = systemImage.Height;
                        // Google.Cloud.Vision.V1.Image 는 Width, Height 필드가 없다.
 
                        Rectangle rectangle = new Rectangle(
                            (int)(normalizedVertices[0].X * imageWidth),
                            (int)(normalizedVertices[0].Y * imageHeight),
                            (int)((normalizedVertices[2].X - normalizedVertices[0].X) * imageWidth),
                            (int)((normalizedVertices[2].Y - normalizedVertices[0].Y) * imageHeight)
                            );
                        // BoundingPoly.NormalizedVertices 는 0~1의 범위를 가지는 값이다.
 
                        StringFormat stringFormat = new StringFormat
                        {
                            Alignment = StringAlignment.Center,
                            LineAlignment = StringAlignment.Center
                        };
 
                        graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
                        graphics.DrawString($"Name: {annotation.Name}\n ID: {annotation.Mid}\n Score: {annotation.Score}",
                            Font, Brushes.Red, rectangle, stringFormat);
                    }
 
                    graphics.Dispose();
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

 

 

 

 

 

 

 

반응형
Posted by J-sean
:
반응형

C# WinForm 으로 구글 클라우드 비전 API 결과를 확인해 보자. 아래 링크를 참고해 기본적인 구글 클라우드 비전 API 사용 준비를 한다.

2022.02.11 - [C#] - C# Google Cloud Vision API - 구글 클라우드 비전 API 1

 

Visual Studio - NuGet Package Manager에서 Google.Cloud.Vision.V1을 설치한다.

 

PictureBox, Button을 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
using System.IO;
using Google.Cloud.Vision.V1;
 
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        ImageAnnotatorClient client;
 
        public Form1()
        {
            InitializeComponent();
 
            try
            {                
                client = new ImageAnnotatorClientBuilder
                {
                    CredentialsPath = "./values-324913-58dd97f8b8ec.json"                    
                }.Build();
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
                Close();
            }
        }
 
        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                System.Drawing.Image systemImage;
 
                OpenFileDialog dlg = new OpenFileDialog();
                if (dlg.ShowDialog() == DialogResult.OK)
                {
                    systemImage = System.Drawing.Image.FromFile(dlg.FileName);
                    pictureBox1.Image = systemImage;
 
                    pictureBox1.Refresh();
                    //Forces the control to invalidate its client area and immediately redraw itself and any child controls.
                    // 미리 Refresh()를 호출하지 않으면 아래 얼굴 영역 사각형, 신뢰도 텍스트 출력이 보이지 않는다.
 
                    Graphics graphics = pictureBox1.CreateGraphics();
 
                    Google.Cloud.Vision.V1.Image visionImage = Google.Cloud.Vision.V1.Image.FromFile(dlg.FileName);
                    //MemoryStream memoryStream = new MemoryStream();
                    //pictureBox1.Image.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Png);
                    //Google.Cloud.Vision.V1.Image visionImage = Google.Cloud.Vision.V1.Image.FromBytes(memoryStream.ToArray());                    
                    // Google.Cloud.Vision.V1.Image와 System.Drawing.Image는 서로 호환되지 않는다.
                    
                    IReadOnlyList<FaceAnnotation> result = client.DetectFaces(visionImage);
                    foreach (FaceAnnotation face in result)
                    {
                        Google.Protobuf.Collections.RepeatedField<Vertex> vertices = face.BoundingPoly.Vertices;
                        
                        Rectangle rectangle = new Rectangle(vertices[0].X, vertices[0].Y, vertices[2].X - vertices[0].X,
                            vertices[2].Y - vertices[0].Y);
                        // 얼굴 영역 (vertices[0]: 좌상단, vertices[1]:우상단, vertices[2]:우하단, vertices[3]:좌하단)
                        StringFormat stringFormat = new StringFormat
                        {
                            Alignment = StringAlignment.Center,
                            LineAlignment = StringAlignment.Center
                        };
                        // 신뢰도 문자열 센터 정렬
 
                        graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
                        graphics.DrawString($"Confidence: {(int)(face.DetectionConfidence * 100)}%", Font,
                            Brushes.Red, rectangle, stringFormat);
                    }
 
                    graphics.Dispose();
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message);
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

버튼을 클릭하고 이미지 파일을 불러온다.

 

 

불러온 이미지의 얼굴 영역에 사각형과 신뢰도가 표시된다.

 

얼굴 이미지가 잘리거나 명확하지 않으면 제대로 검출되지 않는다.

 

반응형
Posted by J-sean
:
반응형

C#에서 구글 클라우드 비전 API를 사용해 보자.

 

Google Could Platform - APIs&amp;amp;amp;Services 에서 Cloud Vision API를 활성화(Enable)한다.

 

Service account key를 생성하고 JSON파일로 다운로드한다.

 

Visual Studio - NuGet Package Manager에서 Google.Cloud.Vision.V1을 설치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.IO;
using Google.Cloud.Vision.V1;
 
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Image image = Image.FromFile("./barbara.png");
                string credentialsString = File.ReadAllText("./your_jsoncredentials.json");
 
                ImageAnnotatorClient client = new ImageAnnotatorClientBuilder
                {
                    JsonCredentials = credentialsString
                    //CredentialsPath = "./your_jsoncredentials.json"
                    // 간단히 CredentialsPath에 서비스 계정 키(json) 파일을 직접 대입해도 된다.
                }.Build();
 
                IReadOnlyList<FaceAnnotation> result = client.DetectFaces(image);
                foreach (FaceAnnotation face in result)
                {
                    string poly = string.Join(" - ", face.BoundingPoly.Vertices.Select(v => $"({v.X}, {v.Y})"));
                    Console.WriteLine($"Confidence: {(int)(face.DetectionConfidence * 100)}%; BoundingPoly: {poly}");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

 

분석 이미지

 

분석 이미지의 얼굴 좌표와 신뢰도가 함께 표시된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using Google.Cloud.Vision.V1;
using Newtonsoft.Json;
 
namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Image image = Image.FromFile("./barbara.png");
                Credentials credentials = new Credentials
                {
                    type = "your_type",
                    project_id = "your_project_id",
                    private_key_id = "your_key_id",
                    private_key = "-----BEGIN PRIVATE KEY-----your_key-----END PRIVATE KEY-----\n",
                    client_email = "your_email",
                    client_id = "your_client_id",
                    auth_uri = "your_auth_uri",
                    token_uri = "your_token_uri",
                    auth_provider_x509_cert_url = "your_auth_cert_url",
                    client_x509_cert_url = "your_client_cert_url"
                };
 
                ImageAnnotatorClient client = new ImageAnnotatorClientBuilder
                {
                    JsonCredentials = JsonConvert.SerializeObject(credentials)
 
                }.Build();
 
                IReadOnlyList<FaceAnnotation> result = client.DetectFaces(image);
                foreach (FaceAnnotation face in result)
                {
                    string poly = string.Join(" - ", face.BoundingPoly.Vertices.Select(v => $"({v.X}, {v.Y})"));
                    Console.WriteLine($"Confidence: {(int)(face.DetectionConfidence * 100)}%; BoundingPoly: {poly}");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
 
    class Credentials
    {
        public string type;
        public string project_id;
        public string private_key_id;
        public string private_key;
        public string client_email;
        public string client_id;
        public string auth_uri;
        public string token_uri;
        public string auth_provider_x509_cert_url;
        public string client_x509_cert_url;
    }
}
 

 

위 소스처럼 서비스 계정 키(json)의 내용을 코드에 직접 삽입 할 수도 있다.

 

※ 참고

Google.Cloud.Vision.V1

 

반응형
Posted by J-sean
:
반응형

안드로이드 앱을 위한 리눅스(우분투) 데이터베이스(MySQL/MariaDB) 서버를 만들어 보자.

 

app/res/xml/network_security_config.xml 파일을 작성한다.

 

AndroidManifest.xml 에 인터넷 권한과 networkSecurityConfig를 추가한다.

 

리니어 레이아웃에 에디트텍스트, 버튼, 텍스트뷰를 적당히 배치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.example.myapplication;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
 
public class MainActivity extends AppCompatActivity {
 
    EditText editText1;
    EditText editText2;
    TextView textView;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        editText1 = findViewById(R.id.editText1);
        editText2 = findViewById(R.id.editText2);
        textView = findViewById(R.id.textView);
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String id = editText1.getText().toString();
                String name = editText2.getText().toString();
 
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        RequestData(id, name);
                    }
                }).start();
            }
        });
    }
 
    public void RequestData(String id, String name) {
        try {
            BufferedReader bufferedReader;
            StringBuilder stringBuilder = new StringBuilder();
 
            String urlString = "http://192.168.171.200/data_check.php";
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            if (conn != null) {
                conn.setConnectTimeout(5000);
                conn.setReadTimeout(5000);
                conn.setRequestMethod("POST");
                conn.setDoInput(true);
                conn.setDoOutput(true);
                conn.connect();
 
                String parameter = "id=" + id + "&name=" + name;
                OutputStream outputStream = conn.getOutputStream();
                outputStream.write(parameter.getBytes(StandardCharsets.UTF_8));
                outputStream.flush();
                outputStream.close();
 
                int responseCode = conn.getResponseCode();
 
                if (responseCode == HttpURLConnection.HTTP_OK) {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                } else {
                    bufferedReader = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
                }
 
                String line = null;
                while ((line = bufferedReader.readLine()) != null) {
                    stringBuilder.append(line + "\n");
                }
 
                textView.setText(stringBuilder);
 
                bufferedReader.close();
                conn.disconnect();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

 

서버에 데이터를 요청하고 표시하는 소스를 작성한다. (MainActivity.java)

 

 

리눅스(우분투) 서버 데이터베이스에 위와 같은 테이블과 데이터를 생성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
    $mysql_host = "localhost";
    $mysql_user = "root";
    $mysql_password = "1234";
    $mysql_db = "member_db";
 
    $conn = mysqli_connect($mysql_host$mysql_user$mysql_password$mysql_db);
 
    if (!$conn) {
        die("Database Connect Error: " . mysqli_connect_error());
    }
 
    $android = strpos($_SERVER['HTTP_USER_AGENT'], "Android");
 
    if ((($_SERVER['REQUEST_METHOD'== 'POST'&& isset($_POST['submit'])) || $android)
    {
        $post_id = trim($_POST['id']);
        $post_name = trim($_POST['name']);
 
        $sql = "SELECT * FROM member WHERE id='$post_id' AND name='$post_name'";
        $result = mysqli_query($conn$sql);
 
        if (mysqli_num_rows($result== 1 ) {
            $member = mysqli_fetch_assoc($result);
            echo "ID: " . $member['id'] . " Name: " . $member['name'] . " Age: " . $member['age'];
        } else {
            echo "No Data.";
            mysqli_close($conn);
            exit;
        }
    } else {
        echo "No Android Device Detected.";
    }
 
    mysqli_close($conn);
?>
 

 

아파치 서버 디렉토리에 안드로이드 앱 요청을 처리할 PHP 파일을 작성한다. (/var/www/html/data_check.php)

 

앱을 실행하고 정보를 요청하면 Age 데이터가 표시된다.

 

잘못된 정보 요청.

 

 

 

안드로이드가 아닌 다른 운영체제로 서버에 접속.

※ 참고

SELinux 유효성 검사

 

반응형
Posted by J-sean
: