kopia lustrzana https://github.com/xdsopl/robot36
Detecting white margin in HF Fax mode and correcting image shift when saving
rodzic
9558080ff8
commit
638484ae78
|
@ -1,4 +1,8 @@
|
||||||
package xdsopl.robot36;
|
package xdsopl.robot36;
|
||||||
|
|
||||||
public abstract class BaseMode implements Mode {
|
public abstract class BaseMode implements Mode {
|
||||||
|
@Override
|
||||||
|
public int getEstimatedHorizontalShift() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
package xdsopl.robot36;
|
package xdsopl.robot36;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
|
||||||
public class HFFax extends BaseMode {
|
public class HFFax extends BaseMode {
|
||||||
private final ExponentialMovingAverage lowPassFilter;
|
private final ExponentialMovingAverage lowPassFilter;
|
||||||
private final int smallPictureMaxSamples;
|
|
||||||
private final int mediumPictureMaxSamples;
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final int sr;
|
private final int sr;
|
||||||
|
|
||||||
|
private final float[] cumulated;
|
||||||
|
private int horizontalShift = 0;
|
||||||
|
|
||||||
HFFax(String name, int sampleRate) {
|
HFFax(String name, int sampleRate) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
smallPictureMaxSamples = (int) Math.round(0.125 * sampleRate);
|
|
||||||
mediumPictureMaxSamples = (int) Math.round(0.175 * sampleRate);
|
|
||||||
lowPassFilter = new ExponentialMovingAverage();
|
lowPassFilter = new ExponentialMovingAverage();
|
||||||
this.sr = sampleRate;
|
this.sr = sampleRate;
|
||||||
|
cumulated = new float[getWidth()];
|
||||||
}
|
}
|
||||||
|
|
||||||
private float freqToLevel(float frequency, float offset) {
|
private float freqToLevel(float frequency, float offset) {
|
||||||
|
@ -55,6 +57,11 @@ public class HFFax extends BaseMode {
|
||||||
return sr / 2;
|
return sr / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getEstimatedHorizontalShift() {
|
||||||
|
return horizontalShift;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
}
|
}
|
||||||
|
@ -63,22 +70,38 @@ public class HFFax extends BaseMode {
|
||||||
public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int scopeBufferWidth, int syncPulseIndex, int scanLineSamples, float frequencyOffset) {
|
public boolean decodeScanLine(PixelBuffer pixelBuffer, float[] scratchBuffer, float[] scanLineBuffer, int scopeBufferWidth, int syncPulseIndex, int scanLineSamples, float frequencyOffset) {
|
||||||
if (syncPulseIndex < 0 || syncPulseIndex + scanLineSamples > scanLineBuffer.length)
|
if (syncPulseIndex < 0 || syncPulseIndex + scanLineSamples > scanLineBuffer.length)
|
||||||
return false;
|
return false;
|
||||||
int horizontalPixels = scopeBufferWidth;
|
int horizontalPixels = getWidth();
|
||||||
if (scanLineSamples < smallPictureMaxSamples)
|
|
||||||
horizontalPixels /= 2;
|
|
||||||
if (scanLineSamples < mediumPictureMaxSamples)
|
|
||||||
horizontalPixels /= 2;
|
|
||||||
lowPassFilter.cutoff(horizontalPixels, 2 * scanLineSamples, 2);
|
lowPassFilter.cutoff(horizontalPixels, 2 * scanLineSamples, 2);
|
||||||
lowPassFilter.reset();
|
lowPassFilter.reset();
|
||||||
for (int i = 0; i < scanLineSamples; ++i)
|
for (int i = 0; i < scanLineSamples; ++i)
|
||||||
scratchBuffer[i] = lowPassFilter.avg(scanLineBuffer[syncPulseIndex + i]);
|
scratchBuffer[i] = lowPassFilter.avg(scanLineBuffer[i]);
|
||||||
lowPassFilter.reset();
|
lowPassFilter.reset();
|
||||||
for (int i = scanLineSamples - 1; i >= 0; --i)
|
for (int i = scanLineSamples - 1; i >= 0; --i)
|
||||||
scratchBuffer[i] = freqToLevel(lowPassFilter.avg(scratchBuffer[i]), frequencyOffset);
|
scratchBuffer[i] = freqToLevel(lowPassFilter.avg(scratchBuffer[i]), frequencyOffset);
|
||||||
for (int i = 0; i < horizontalPixels; ++i) {
|
for (int i = 0; i < horizontalPixels; ++i) {
|
||||||
int position = (i * scanLineSamples) / horizontalPixels;
|
int position = (i * scanLineSamples) / horizontalPixels;
|
||||||
pixelBuffer.pixels[i] = ColorConverter.GRAY(scratchBuffer[position]);
|
int color = ColorConverter.GRAY(scratchBuffer[position]);
|
||||||
|
pixelBuffer.pixels[i] = color;
|
||||||
|
|
||||||
|
cumulated[i] *= 0.99f; //decay old data
|
||||||
|
cumulated[i] += Color.luminance(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//try to detect "sync": thick white margin
|
||||||
|
int bestIndex = 0;
|
||||||
|
float bestValue = 0;
|
||||||
|
for (int x = 0; x < getWidth(); ++x)
|
||||||
|
{
|
||||||
|
float val = cumulated[x];
|
||||||
|
if (val > bestValue)
|
||||||
|
{
|
||||||
|
bestIndex = x;
|
||||||
|
bestValue = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
horizontalShift = bestIndex;
|
||||||
|
|
||||||
pixelBuffer.width = horizontalPixels;
|
pixelBuffer.width = horizontalPixels;
|
||||||
pixelBuffer.height = 1;
|
pixelBuffer.height = 1;
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -15,6 +15,7 @@ import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioFormat;
|
import android.media.AudioFormat;
|
||||||
import android.media.AudioRecord;
|
import android.media.AudioRecord;
|
||||||
|
@ -846,6 +847,19 @@ public class MainActivity extends AppCompatActivity {
|
||||||
Bitmap bmp = Bitmap.createBitmap(scopeBuffer.pixels, offset, stride, width, height, Bitmap.Config.ARGB_8888);
|
Bitmap bmp = Bitmap.createBitmap(scopeBuffer.pixels, offset, stride, width, height, Bitmap.Config.ARGB_8888);
|
||||||
if (currentMode == null || !currentMode.equals("HF Fax")) {
|
if (currentMode == null || !currentMode.equals("HF Fax")) {
|
||||||
bmp = Bitmap.createScaledBitmap(bmp, width / 3, height / 3, true);
|
bmp = Bitmap.createScaledBitmap(bmp, width / 3, height / 3, true);
|
||||||
|
} else {
|
||||||
|
Mode mode = decoder.currentMode;
|
||||||
|
int shift = mode.getEstimatedHorizontalShift();
|
||||||
|
if (shift > 0) {
|
||||||
|
Bitmap part1 = Bitmap.createBitmap(bmp, 0, 0, shift, bmp.getHeight());
|
||||||
|
Bitmap part2 = Bitmap.createBitmap(bmp, shift, 0, mode.getWidth() - shift, bmp.getHeight());
|
||||||
|
|
||||||
|
Bitmap bmpMutable = Bitmap.createBitmap(mode.getWidth(), bmp.getHeight(), Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new android.graphics.Canvas(bmpMutable);
|
||||||
|
canvas.drawBitmap(part2, 0, 1, null);
|
||||||
|
canvas.drawBitmap(part1, mode.getWidth() - shift, 0, null);
|
||||||
|
bmp = bmpMutable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
storeBitmap(bmp);
|
storeBitmap(bmp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,11 @@ public interface Mode {
|
||||||
*/
|
*/
|
||||||
int getScanLineSamples();
|
int getScanLineSamples();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return number of pixels of horizontal shift based on recent data, nonzero for HF Fax
|
||||||
|
*/
|
||||||
|
int getEstimatedHorizontalShift();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset internal state.
|
* Reset internal state.
|
||||||
*/
|
*/
|
||||||
|
|
Ładowanie…
Reference in New Issue