Android 图像处理与快速傅里叶变换
我正在开发一个实时图像处理的应用程序,以获得每帧之间的y和时间。它们存储在2个双数组中,为了进一步实现,我需要对这些值运行快速傅立叶变换 我在其他问题中见过一些fft算法,例如 我也读过他们建议使用的主题 然而,由于我对FFT的了解非常有限,我不知道如何在代码中实现它 我的主要活动是Android 图像处理与快速傅里叶变换,android,image-processing,fft,Android,Image Processing,Fft,我正在开发一个实时图像处理的应用程序,以获得每帧之间的y和时间。它们存储在2个双数组中,为了进一步实现,我需要对这些值运行快速傅立叶变换 我在其他问题中见过一些fft算法,例如 我也读过他们建议使用的主题 然而,由于我对FFT的了解非常有限,我不知道如何在代码中实现它 我的主要活动是 public class MainActivity extends AppCompatActivity implements CameraView.PreviewReadyCallback { private
public class MainActivity extends AppCompatActivity implements CameraView.PreviewReadyCallback {
private static Camera camera = null;
private CameraView image = null;
Button fftButton;
private LineChart bp_graph;
private int img_Y_Avg, img_U_Avg, img_V_Avg;
private long end = 0, begin = 0;
Handler handler;
private int readingRemaining = 1200;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
bp_graph = (LineChart)findViewById(R.id.graph);
fftButton = (Button)findViewById(R.id.runFFT);
graph_features();
//open camera
try {
camera = Camera.open();
handler = new Handler();
final Runnable runnable = new Runnable() {
@Override
public void run() {
camera.stopPreview();
camera.release();
fftButton.setVisibility(View.VISIBLE);
}
};
handler.postDelayed(runnable, 30000);
} catch (Exception e) {
Log.d("ERROR", "Failed to get camera: " + e.getMessage());
}
if (camera != null) {
image = new CameraView(this, camera);
FrameLayout camera_view = (FrameLayout) findViewById(R.id.camera_view);
camera_view.addView(image);
image.setOnPreviewReady(this);
}
}
@Override
protected void onResume(){
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
public void onPreviewFrame(long startTime, int ySum, int uSum, int vSum, long endTime) {
begin = startTime;
img_Y_Avg = ySum;
img_U_Avg = uSum;
img_V_Avg = vSum;
end = endTime;
showResults(begin, img_Y_Avg, img_U_Avg, img_V_Avg, end);
}
private void showResults(long startTime, int ySum, int uSum, int vSum, long endTime){
//set value of Y on the text view
TextView valueOfY = (TextView)findViewById(R.id.valueY);
//valueY = img_Y_Avg;
valueOfY.setText(String.valueOf(img_Y_Avg));
//start time in milliseconds
long StartDurationInMs = TimeUnit.MILLISECONDS.convert(begin, TimeUnit.MILLISECONDS);
ArrayList<Long> startOfTime = new ArrayList<>();
startOfTime.add(StartDurationInMs);
//store value to array list
ArrayList<Integer> yAverage = new ArrayList<>();
yAverage.add(img_Y_Avg);
ArrayList<Long> getValues = new ArrayList<>();
for(int i = 0; i < yAverage.size(); i++) {
getValues.add(startOfTime.get(i));
getValues.add((long)(yAverage.get(i)));
}
storeCsv(yAverage, getValues);
Log.d("MyEntryData", String.valueOf(getValues));
}
/**
* method to store raw time and y-sum data into CSV file**/
public void storeCsv(ArrayList<Integer>yAverage, ArrayList<Long>getValues){
String filename = "temporary.csv";
//File directoryDownload = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bpReader";
//File logDir = new File (directoryDownload, "bpReader"); //Creates a new folder in DOWNLOAD directory
File logDir = new File(path);
logDir.mkdirs();
File file = new File(logDir, filename);
FileOutputStream outputStream = null;
try {
file.createNewFile();
outputStream = new FileOutputStream(file, true);
//outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
for (int i = 0; i < yAverage.size(); i += 2) {
outputStream.write((getValues.get(i) + ",").getBytes());
outputStream.write((getValues.get(i + 1) + "\n").getBytes());
//outputStream.write((getValues.get(i + 2) + ",").getBytes());
//outputStream.write((getValues.get(i + 3) + "\n").getBytes());
}
outputStream.flush();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//Method for button which appears after the reading is done and the data is saved into csv file
public void readFile(View view){
readCsv();
}
//Method to read the data from the csv and get the time and y-sum value
public void readCsv(){
String getPath = Environment.getExternalStorageDirectory() + "/bpReader";
String csvFile = "temporary.csv";
String path = getPath+ "/" + csvFile;
int length = 500;
double[] xCoords = new double[length];
double[] yCoords = new double[length];
double[] newXcord = new double[length];
CSVReader reader;
try {
File myFile = new File (path);
reader = new CSVReader(new FileReader(myFile));
String[] line;
int i;
for (i = 0; i < xCoords.length; i ++){
if ((line = reader.readNext()) != null){
xCoords[i] = Double.parseDouble(line[0]);
yCoords[i] = Double.parseDouble(line[1]);
}
}
for (i = 0; i < xCoords.length ; i++){
if (xCoords[i]!=0) {
newXcord[i] = xCoords[i] - xCoords[0];
Log.d("read:: ", "Time: " + String.valueOf(newXcord[i]) + " Y-Sum " + String.valueOf(yCoords[i]));
}
}
myFile.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
我的FFT类是从其他堆栈溢出问题中的一些建议中得到的
public class FFT {
/**
* The Fast Fourier Transform (generic version, with NO optimizations).
*
* @param inputReal
* an array of length n, the real part
* @param inputImag
* an array of length n, the imaginary part
* @param DIRECT
* TRUE = direct transform, FALSE = inverse transform
* @return a new array of length 2n
*/
public static double[] fft(final double[] inputReal, double[] inputImag,
boolean DIRECT) {
// - n is the dimension of the problem
// - nu is its logarithm in base e
int n = inputReal.length;
// If n is a power of 2, then ld is an integer (_without_ decimals)
double ld = Math.log(n) / Math.log(2.0);
// Here I check if n is a power of 2. If exist decimals in ld, I quit
// from the function returning null.
if (((int) ld) - ld != 0) {
System.out.println("The number of elements is not a power of 2.");
return null;
}
// Declaration and initialization of the variables
// ld should be an integer, actually, so I don't lose any information in
// the cast
int nu = (int) ld;
int n2 = n / 2;
int nu1 = nu - 1;
double[] xReal = new double[n];
double[] xImag = new double[n];
double tReal, tImag, p, arg, c, s;
// Here I check if I'm going to do the direct transform or the inverse
// transform.
double constant;
if (DIRECT)
constant = -2 * Math.PI;
else
constant = 2 * Math.PI;
// I don't want to overwrite the input arrays, so here I copy them. This
// choice adds \Theta(2n) to the complexity.
for (int i = 0; i < n; i++) {
xReal[i] = inputReal[i];
xImag[i] = inputImag[i];
}
// First phase - calculation
int k = 0;
for (int l = 1; l <= nu; l++) {
while (k < n) {
for (int i = 1; i <= n2; i++) {
p = bitReverseReference(k >> nu1, nu);
// direct FFT or inverse FFT
arg = constant * p / n;
c = Math.cos(arg);
s = Math.sin(arg);
tReal = xReal[k + n2] * c + xImag[k + n2] * s;
tImag = xImag[k + n2] * c - xReal[k + n2] * s;
xReal[k + n2] = xReal[k] - tReal;
xImag[k + n2] = xImag[k] - tImag;
xReal[k] += tReal;
xImag[k] += tImag;
k++;
}
k += n2;
}
k = 0;
nu1--;
n2 /= 2;
}
// Second phase - recombination
k = 0;
int r;
while (k < n) {
r = bitReverseReference(k, nu);
if (r > k) {
tReal = xReal[k];
tImag = xImag[k];
xReal[k] = xReal[r];
xImag[k] = xImag[r];
xReal[r] = tReal;
xImag[r] = tImag;
}
k++;
}
// Here I have to mix xReal and xImag to have an array (yes, it should
// be possible to do this stuff in the earlier parts of the code, but
// it's here to readibility).
double[] newArray = new double[xReal.length * 2];
double radice = 1 / Math.sqrt(n);
for (int i = 0; i < newArray.length; i += 2) {
int i2 = i / 2;
// I used Stephen Wolfram's Mathematica as a reference so I'm going
// to normalize the output while I'm copying the elements.
newArray[i] = xReal[i2] * radice;
newArray[i + 1] = xImag[i2] * radice;
}
return newArray;
}
/**
* The reference bitreverse function.
*/
private static int bitReverseReference(int j, int nu) {
int j2;
int j1 = j;
int k = 0;
for (int i = 1; i <= nu; i++) {
j2 = j1 / 2;
k = 2 * k + j1 - 2 * j2;
j1 = j2;
}
return k;
}
}
公共类FFT{
/**
*快速傅里叶变换(通用版本,无优化)。
*
*@param-inputReal
*长度为n的数组,实数部分
*@param inputImag
*长度为n的数组,虚部
*@param-DIRECT
*真=直接变换,假=逆变换
*@返回长度为2n的新数组
*/
公共静态双[]fft(最终双[]输入,双[]输入,
布尔(直接){
//-n是问题的维数
//-nu是以e为底的对数
int n=inputReal.length;
//如果n是2的幂,那么ld是整数(_不带小数)
双ld=Math.log(n)/Math.log(2.0);
//这里我检查n是否是2的幂。如果ld中存在小数,我退出
//从函数返回null。
如果((int)ld)-ld!=0){
System.out.println(“元素的数量不是2的幂”);
返回null;
}
//变量的声明和初始化
//实际上,ld应该是一个整数,所以我不会丢失任何信息
//演员阵容
int nu=(int)ld;
int n2=n/2;
int nu1=nu-1;
double[]xReal=新的双精度[n];
double[]xImag=新的double[n];
双通道,tImag,p,arg,c,s;
//在这里,我检查我是要做正变换还是逆变换
//转变。
双常数;
如果(直接)
常数=-2*Math.PI;
其他的
常数=2*Math.PI;
//我不想覆盖输入数组,所以我在这里复制它们
//选择增加了\Theta(2n)的复杂性。
对于(int i=0;ik){
tReal=xReal[k];
tImag=xImag[k];
xReal[k]=xReal[r];
xImag[k]=xImag[r];
xReal[r]=tReal;
xImag[r]=tImag;
}
k++;
}
//在这里,我必须混合使用xReal和xImag来创建一个数组(是的,应该是这样的)
//可以在代码的前面部分做这些事情,但是
//这是为了便于阅读)。
double[]newArray=newdouble[xReal.length*2];
双半径=1/数学sqrt(n);
对于(int i=0;ipublic class FFT {
/**
* The Fast Fourier Transform (generic version, with NO optimizations).
*
* @param inputReal
* an array of length n, the real part
* @param inputImag
* an array of length n, the imaginary part
* @param DIRECT
* TRUE = direct transform, FALSE = inverse transform
* @return a new array of length 2n
*/
public static double[] fft(final double[] inputReal, double[] inputImag,
boolean DIRECT) {
// - n is the dimension of the problem
// - nu is its logarithm in base e
int n = inputReal.length;
// If n is a power of 2, then ld is an integer (_without_ decimals)
double ld = Math.log(n) / Math.log(2.0);
// Here I check if n is a power of 2. If exist decimals in ld, I quit
// from the function returning null.
if (((int) ld) - ld != 0) {
System.out.println("The number of elements is not a power of 2.");
return null;
}
// Declaration and initialization of the variables
// ld should be an integer, actually, so I don't lose any information in
// the cast
int nu = (int) ld;
int n2 = n / 2;
int nu1 = nu - 1;
double[] xReal = new double[n];
double[] xImag = new double[n];
double tReal, tImag, p, arg, c, s;
// Here I check if I'm going to do the direct transform or the inverse
// transform.
double constant;
if (DIRECT)
constant = -2 * Math.PI;
else
constant = 2 * Math.PI;
// I don't want to overwrite the input arrays, so here I copy them. This
// choice adds \Theta(2n) to the complexity.
for (int i = 0; i < n; i++) {
xReal[i] = inputReal[i];
xImag[i] = inputImag[i];
}
// First phase - calculation
int k = 0;
for (int l = 1; l <= nu; l++) {
while (k < n) {
for (int i = 1; i <= n2; i++) {
p = bitReverseReference(k >> nu1, nu);
// direct FFT or inverse FFT
arg = constant * p / n;
c = Math.cos(arg);
s = Math.sin(arg);
tReal = xReal[k + n2] * c + xImag[k + n2] * s;
tImag = xImag[k + n2] * c - xReal[k + n2] * s;
xReal[k + n2] = xReal[k] - tReal;
xImag[k + n2] = xImag[k] - tImag;
xReal[k] += tReal;
xImag[k] += tImag;
k++;
}
k += n2;
}
k = 0;
nu1--;
n2 /= 2;
}
// Second phase - recombination
k = 0;
int r;
while (k < n) {
r = bitReverseReference(k, nu);
if (r > k) {
tReal = xReal[k];
tImag = xImag[k];
xReal[k] = xReal[r];
xImag[k] = xImag[r];
xReal[r] = tReal;
xImag[r] = tImag;
}
k++;
}
// Here I have to mix xReal and xImag to have an array (yes, it should
// be possible to do this stuff in the earlier parts of the code, but
// it's here to readibility).
double[] newArray = new double[xReal.length * 2];
double radice = 1 / Math.sqrt(n);
for (int i = 0; i < newArray.length; i += 2) {
int i2 = i / 2;
// I used Stephen Wolfram's Mathematica as a reference so I'm going
// to normalize the output while I'm copying the elements.
newArray[i] = xReal[i2] * radice;
newArray[i + 1] = xImag[i2] * radice;
}
return newArray;
}
/**
* The reference bitreverse function.
*/
private static int bitReverseReference(int j, int nu) {
int j2;
int j1 = j;
int k = 0;
for (int i = 1; i <= nu; i++) {
j2 = j1 / 2;
k = 2 * k + j1 - 2 * j2;
j1 = j2;
}
return k;
}
}