히스토그램이란?

이미지에서 픽셀들이 가지는 값들의 출현빈도를 히스토그램이라한다.

GRAY이미지에서는 0~255값을 가진다.

이미지의 크기를 400 x 400이라고 한다면, 총 160,000개의 픽셀들을 0 ~ 255값에 따라 분류하여

각 개별값을 갖는 픽셀들이 몇개인지 알아낸 것이 히스토그램이다.

calcHist()

void calcHist(

const Mat* images

int nimages,

const int* channels

InputArray mask

OutputArray hist

int dims

const int* histSize

const float** ranges

bool uniform=true, 

bool accumulate=false 

);


images : 대상 이미지

nimages :  배열에 포함된 이미지의 개수

channels : Histogram을 계산할 채널 번호의 배열. 

예를 들어 아래 그림과 같이 BGR이미지 2장에 대해, img1은 B채널, img2는 G채널에 대해서   히스토그램을 구하고자 한다면 {0,4}를 배열에 넣어 전달해야한다.



mask : 히스토그램을 계산할 영역을 지정할 수 있다. Mat()을 전달하면 아무것도 하지않는다.

hist : 히스토그램을 계산한 결과를 저장한다.

dims : 히스토그램 계산 결과를 저장한 hist의 차원

histSize : 빈도수를 분류할 칸의 개수

ranges : 각 차원의 분류 bin의 최소, 최대값을 의미한다.




minMaxLoc()

배열에서 가장 큰값과 작은 값을 찾는 함수

C++ : 
void minMaxLoc(
    InputArray src,
    double* minVal,
    double* maxVal=0,
    Point* minLoc=0,
    Point* maxLoc=0,
)



코드

#include<iostream>

#include<opencv2\core.hpp>

#include<opencv\cv.h>

#include<opencv2\highgui.hpp>

#include<opencv2\imgproc.hpp>


using namespace cv;



int histSize[1];

float hranges[2];

const float* ranges[1];

int channels[1]; 



MatND getHistogram(const Mat &image) {


histSize[0] = 256;

hranges[0] = 0.0; //히스토그램 최소 값

hranges[1] = 255.0; //히스토그램 최대 값

ranges[0] = hranges;

channels[0] = 0;


MatND hist;

calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges);

return hist;

}





Mat getHistogramImage(const Mat &image) {


histSize[0] = 256;

hranges[0] = 0.0;

hranges[1] = 255.0;

ranges[0] = hranges;

channels[0] = 0;

// 만약 BGR값을 위한 히스토 그램을 만들고 싶다면, 

// channels[0] = {0}; //blue

// channels[0] = {1}; //green

// channels[0] = {2}; //red 를 해주면된다.


MatND hist = getHistogram(image); // 히스토그램 계산


double maxVal = 0; // 최대 빈도수

double minVal = 0; // 최소 빈도수

minMaxLoc(hist, &minVal, &maxVal, 0, 0);


Mat histImg(histSize[0], histSize[0], CV_8U, Scalar(255));


int hpt = static_cast<int>(0.9*histSize[0]);


//그래프 그리기

for (int h = 0; h < histSize[0]; h++) {

float binVal = hist.at<float>(h);

int intensity = static_cast<int> (binVal*hpt / maxVal);

line(histImg, Point(h, histSize[0]), Point(h, histSize[0] - intensity), Scalar::all(0));

}

return histImg;

}


int main() {


Mat image = imread("image/humming_bird.jpg", 0);

Mat thresholded;

Mat eqHist;


if (!image.data)

{

std::cout << "open failed" << std::endl;

return 0;

}


//원본

namedWindow("Image");

imshow("Image", image);


//원본 히스토그램

MatND histo = getHistogram(image);

namedWindow("Histogram");

imshow("Histogram", getHistogramImage(image));



//히스토그램 이퀄라이제이션한 영상

equalizeHist(image, eqHist);

imshow("Image_EQ", eqHist);


//imwrite시에 영상의 퀄리티 정도를 지정해줄수있다.

imwrite("Image_Histogram EQ.jpg", eqHist, { CV_IMWRITE_JPEG_QUALITY , 50 });



//히스토그램 이퀄라이제이션 히스토그램 출력

imshow("Histogram_EQ", getHistogramImage(eqHist));



//영상 이진화

threshold(image, thresholded, 100, 255, THRESH_BINARY);

namedWindow("Binary Image");

imshow("Binary Image", thresholded);


imwrite("Image_Histogram threshold.jpg", thresholded, { CV_IMWRITE_JPEG_QUALITY , 50});




waitKey(0);


return 0;

}



결과 영상



'Programming > openCV' 카테고리의 다른 글

6. 디졸브와 트랙바  (0) 2017.04.04
5. 영상 디졸브  (1) 2017.04.04
4. Threshold 와 트랙바  (0) 2017.04.04
2. 컬러이미지 그레이 이미지로 변환하기  (0) 2017.04.04
1. 사진에 글자적기 / 도형 그리기  (0) 2017.04.04

#include <iostream>

#include <opencv2/opencv.hpp>


#include <stdlib.h>

#include <stdio.h>


using namespace std;

using namespace cv;



int main()

{

Mat image, image_gray;

image = cv::imread("image/humming_bird.jpg", CV_LOAD_IMAGE_COLOR);


if (!image.data) { return -1; }


cvtColor(image, image_gray, CV_RGB2GRAY);


namedWindow("Image Grayed", CV_WINDOW_AUTOSIZE);

imshow("Image Grayed", image_gray);

waitKey();


return 0;

}


#include <iostream>

#include <opencv2/opencv.hpp>


#include <stdlib.h>

#include <stdio.h>


using namespace std;

using namespace cv;


int main(void) {


char* path = "image/humming_bird.jpg";

Mat img = imread(path, CV_LOAD_IMAGE_COLOR);


//텍스트 입력

putText(img, "Hello Bird!", cvPoint(150, 50), CV_FONT_BLACK, 1, Scalar(255,255,255));

//사각형 입력

rectangle(img, cvPoint(320, 70), cvPoint(150,10), Scalar(0, 0, 255),1);


namedWindow("Image", CV_WINDOW_AUTOSIZE);

imshow("Image", img);


waitKey();


return 0;

}







 핸드폰이 흔들리는 것을 감지하려면, 가속도 센서를 이용합니다. 가속도 센서는 핸드폰을 기준으로 x, y, z 방향으로의 가속도만 측정이 가능합니다. 즉, 이 방향성을 가지는 가속도를 가지고 제대로된 움직임을 감지해야하는 것이죠. 센서를 사용하기 위해 SensorListener를 상속받는 엑티비티를 생성합니다. 



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
public class ShakeActivity extends Activity implements SensorEventListener {
 
    private long lastTime;
    private float speed;
    private float lastX;
    private float lastY;
    private float lastZ;
    private float x, y, z;
 
    private static final int SHAKE_THRESHOLD = 800;
    private static final int DATA_X = SensorManager.DATA_X;
    private static final int DATA_Y = SensorManager.DATA_Y;
    private static final int DATA_Z = SensorManager.DATA_Z;
 
    private SensorManager sensorManager;
    private Sensor accelerormeterSensor;
 
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accelerormeterSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    }
 
    @Override
    public void onStart() {
        super.onStart();
        if (accelerormeterSensor != null)
            sensorManager.registerListener(this, accelerormeterSensor,
            SensorManager.SENSOR_DELAY_GAME);
    }
 
    @Override
    public void onStop() {
        super.onStop();
        if (sensorManager != null)
            sensorManager.unregisterListener(this);
    }
 
    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
 
    }
 
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            long currentTime = System.currentTimeMillis();
            long gabOfTime = (currentTime - lastTime);
            if (gabOfTime > 100) {
                lastTime = currentTime;
                x = event.values[SensorManager.DATA_X];
                y = event.values[SensorManager.DATA_Y];
                z = event.values[SensorManager.DATA_Z];
 
                speed = Math.abs(x + y + z - lastX - lastY - lastZ) / gabOfTime * 10000;
 
                if (speed > SHAKE_THRESHOLD) {
                    // doSomething
                }
 
                lastX = event.values[DATA_X];
                lastY = event.values[DATA_Y];
                lastZ = event.values[DATA_Z];
            }
 
        }
 
    }
}
cs


 onCreate 부분에서는 센서를 사용하기 위해서 시스템 서비스를 가져와서 SensorManager 타입으로 저장해둡니다.

onAccuracyChanged와 onSensorChanged를 오버라이딩 해줘야 합니다. 

함수 이름에서 보시듯 정확도와 센서 정보가 변하면 발생하는 함수입니다. 

흔드는 것을 감지하는 핵심적인 소스는 onSensorChanged 함수에 있습니다. 센서가 변함에 따라 이 함수가 실행됩니다. 센서 종류가 가속도 센서일 때, 이벤트를 처리해줘야합니다. 최근 측정한 시간과 현재 시간을 비교하여 0.1초 이상되었을 때, 흔듬을 감지합니다. 이 흔듬을 감지하는 함수는 조금씩 수정이 가능합니다. 자신의 프로그램에 맞게 설정할 해줄 수 있습니다. 기본적으로는 위의 소스코드에서 보이는 것처럼, 가속도의 벡터값을 이용하여 대략적으로 측정합니다. 정확도면에서는 약간 떨어지겠죠. SHAKE_THRESHOLD 또한 잘 설정을 해줘야 합니다. 속도가 얼마 이상일 때, 흔듬을 감지하겠다는 것을 설정해주는 부분입니다.

'Programming > Android' 카테고리의 다른 글

DialogFragment  (0) 2016.09.24
ListView에 각각 Seekbar 넣기  (1) 2016.09.24
Custom Listview  (0) 2016.09.24
ListFragment  (0) 2016.09.24
ScrollView  (0) 2016.09.15

다이얼로그를 프래그먼트로 사용하기 위해서는, 다이얼로그 창도 새로 만들어 줘야한다.

이 방법을 공부하는 과정 역식 정말 신기하고 재미있었다. 안드로이드는 공부할수록 신기한 것 같다.


xml

우선 xml에 사용하고 싶은 다이얼로그의 형태를 정의한다. 그리고 여기서 중요한것은 부모 layout의 width와 height를 wrap_content로 만들어 줘야하는것이다. 이것이 여타 다른 프래그먼트를 만들때 match_parent를 하는것과 차이점이 되겠다. 위의 방법만 유의해서 xml파일을 만들어 주면 된다.


다음은 DialogFragment를 상속받은 class파일을 하나 만들어보자


DialogFragment.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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
 
public class DialogFragment extends android.support.v4.app.DialogFragment {
 
  ...
 
    public static DialogFragment newInstance(String param1, String param2) {
        DialogFragment fragment = new DialogFragment();
        Bundle arg = new Bundle();
 
        arg.putString("param1", param1);
        arg.putString("param2", param2);
 
        fragment.setArguments(arg);
 
        return fragment;
    }
 
    public DialogFragment(){}
 
 
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        ...
 
    }
 
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {      
 
        ...
 
         AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
 
        builder.setView(view)
            .setPositiveButton("Yes"new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        // Yes 버튼을 눌렀을때 발생하는 이벤트
                    }
        }).setNegativeButton("No"new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                 // No 버튼을 눌렀을때 발생하는 이벤트
            });
 
        return builder.create();
    }
}

cs


DialogFragment의 재미있는 점은 onCreateDialog 메소드가 있다는 점이다. 이를통해 다이얼로그가 띄워졌을때에 대한 화면과 이벤트를 정의하는데, 35행 이하와 같이 builer를 정의해서 그 builder에 PositiveButton과 NegativeButton을 정의해 주는것이다. 이것이 우리가 흔히 알고있는 예/아니오 버튼인 것이다. 위를 통해 우리는 xml파일에 굳이 예/아니오 버튼을 만들 필요가 없으며 간단하게 java파일에서 만들수 있다.


'Programming > Android' 카테고리의 다른 글

가속도 센서를 이용하여 흔들림 감지  (0) 2016.10.12
ListView에 각각 Seekbar 넣기  (1) 2016.09.24
Custom Listview  (0) 2016.09.24
ListFragment  (0) 2016.09.24
ScrollView  (0) 2016.09.15

ListView에 각각 seekbar를 넣어서 따로 제어하게 하는 부분을 구현하고 싶었는데, 잘 되지 않았다. 그래서 이틀을 삽질을하면서 구글링한 결과 다음과 같은 답을 얻어 내었다.



이번 프로젝트를 하면서 가장 어려웠던 부분인데, 스택오버플로우에서 찾아보니 간단하게 해결 되었다...;;

http://stackoverflow.com/questions/6297791/refresh-seekbar-textview-in-a-listview 원문은 이곳이다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 sbProgress.setTag(position);
        sbProgress.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
 
            View view = (View) seekBar.getParent();
            if(view != null){
                TextView tvProgress = (TextView)view
                    .findViewById(R.id.tv_porcentaje_numerico_obtenido);
                tvProgress.setText(progress + "%");
                MyAdapter.this.getItem((Integer)seekBar.getTag()).setProgres(progress);
            }
        }
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {}
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {}
 
        });
cs



11행에 있는 코드와같이 한줄이면 해결된다... 하지만 필자는 11행이 잘 되지 않아서 아래와 같이 변경했다.


1
listAdepter.this.getItem((Integer)seekBar.getTag());
cs


필자는 위와 같은 방법을 사용해서 문제를 해결 하였다.

'Programming > Android' 카테고리의 다른 글

가속도 센서를 이용하여 흔들림 감지  (0) 2016.10.12
DialogFragment  (0) 2016.09.24
Custom Listview  (0) 2016.09.24
ListFragment  (0) 2016.09.24
ScrollView  (0) 2016.09.15

SUMMARY

커스텀 리스트뷰를 만들기위해서는 

Item xml을 정하고

리스트 뷰를 띄울 fragment를 만들고

BaseAdepter을 상속받은 클래스를 통하여 이 둘을 연결시켜 준다.

또한 BaseAdepte에서는 getView getCount getItemId를 각각 overriding해서 정의 해주어야 한다.

getView는 화면에 띄울 아이템의 메소드를 정의하고,

getCount에서는 아이템의 개수를

getItemId에서는 아이템의 position을 리턴해주게 만들면 된다.

그리고 이 클래스 안에 InnerClass를 정의하여 이를 ArrayList로 만들어서 사용하면 편리하다.



사용법

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
public class Control_listAdepter extends BaseAdapter {
 
    public class ListViewItem {
 
        ...
    }
 
    private ArrayList<ListViewItem> listViewItemList = new ArrayList<ListViewItem>() ;
 
    @Override
    public int getCount() {
        return listViewItemList.size() ;
    }
 
    @Override
    public View getView(int position, View view, ViewGroup parent) {
        final Context context = parent.getContext();
 
        if (view == null) {
            LayoutInflater inflater = (LayoutInflater) context
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.control_item, parent, false);
        }
 
 
        /*화면에 표시할 부분을 입력해 주는 .*/
 
        return view;
    }
 
    @Override
    public long getItemId(int position) {
        return position ;
    }
 
    @Override
    public Object getItem(int position) {
        return listViewItemList.get(position) ;
    }
}
cs


'Programming > Android' 카테고리의 다른 글

DialogFragment  (0) 2016.09.24
ListView에 각각 Seekbar 넣기  (1) 2016.09.24
ListFragment  (0) 2016.09.24
ScrollView  (0) 2016.09.15
xml 경계선  (1) 2016.09.15

리스트 뷰를  프래그먼트를 이용하여 사용하기 위해서는  ListFragment를 상속받는 것이 좋다.

onListItemClick이라는 함수를 이용하여 아이템 클릭 메소드를 사용하면 되기때문이다.


1
2
3
4
5
  @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        ListViewItem item = (ListViewItem) l.getItemAtPosition(position) ;
        String title = item.getTitle() ;
    }
cs


위와 같은 방법으로 Click 메소드를 손쉽게 구현 할 수 있다.

'Programming > Android' 카테고리의 다른 글

ListView에 각각 Seekbar 넣기  (1) 2016.09.24
Custom Listview  (0) 2016.09.24
ScrollView  (0) 2016.09.15
xml 경계선  (1) 2016.09.15
xml include  (0) 2016.09.15

+ Recent posts