|
|
|
|
|
|
|
|
import android.content.Context; |
|
|
import android.content.Context; |
|
|
import android.graphics.ImageFormat; |
|
|
import android.graphics.ImageFormat; |
|
|
import android.media.Image; |
|
|
import android.media.Image; |
|
|
|
|
|
import android.os.Handler; |
|
|
import android.util.Size; |
|
|
import android.util.Size; |
|
|
|
|
|
|
|
|
import androidx.camera.core.CameraSelector; |
|
|
import androidx.camera.core.CameraSelector; |
|
|
|
|
|
|
|
|
private final MutableLiveData<Boolean> mVideoAlarmDetected = new MutableLiveData<>(); |
|
|
private final MutableLiveData<Boolean> mVideoAlarmDetected = new MutableLiveData<>(); |
|
|
private static CameraSensor cameraSensorInstance; |
|
|
private static CameraSensor cameraSensorInstance; |
|
|
private boolean videoAlarmDetected; |
|
|
private boolean videoAlarmDetected; |
|
|
|
|
|
private boolean isMotionDetected; |
|
|
private ByteBuffer previousBuffer; |
|
|
private ByteBuffer previousBuffer; |
|
|
private int previousWidth; |
|
|
private int previousWidth; |
|
|
private int previousHeight; |
|
|
private int previousHeight; |
|
|
|
|
|
private final int threshold = 50; |
|
|
|
|
|
private int startX; |
|
|
|
|
|
private int startY; |
|
|
|
|
|
private int endX; |
|
|
|
|
|
private int endY; |
|
|
|
|
|
|
|
|
|
|
|
private static final long ALARM_RESET_DELAY = 5000; |
|
|
|
|
|
private Runnable alarmResetRunnable; |
|
|
|
|
|
private final Handler alarmResetHandler = new Handler(); |
|
|
|
|
|
|
|
|
private CameraSensor() { |
|
|
private CameraSensor() { |
|
|
videoAlarmDetected = false; |
|
|
videoAlarmDetected = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (previousHeight != 0) { |
|
|
if (previousHeight != 0) { |
|
|
assert currentImage != null; |
|
|
assert currentImage != null; |
|
|
videoAlarmDetected = compareFrames(currentImage); |
|
|
|
|
|
|
|
|
isMotionDetected = compareFrames(currentImage); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
assert currentImage != null; |
|
|
assert currentImage != null; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
currentImage.close(); |
|
|
currentImage.close(); |
|
|
|
|
|
|
|
|
|
|
|
if (isMotionDetected) { |
|
|
|
|
|
|
|
|
|
|
|
videoAlarmDetected = true; |
|
|
|
|
|
|
|
|
|
|
|
if(alarmResetRunnable != null) { |
|
|
|
|
|
alarmResetHandler.removeCallbacks(alarmResetRunnable); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
alarmResetRunnable = this::resetAlarmStatus; |
|
|
|
|
|
alarmResetHandler.postDelayed(alarmResetRunnable, ALARM_RESET_DELAY); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
checkAlarmCondition(); |
|
|
checkAlarmCondition(); |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
private boolean compareFrames(Image currentImage) { |
|
|
private boolean compareFrames(Image currentImage) { |
|
|
|
|
|
|
|
|
ByteBuffer currentBuffer = currentImage.getPlanes()[0].getBuffer(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Image.Plane[] planes = currentImage.getPlanes(); |
|
|
|
|
|
ByteBuffer currentBuffer = planes[0].getBuffer(); |
|
|
int currentWidth = currentImage.getWidth(); |
|
|
int currentWidth = currentImage.getWidth(); |
|
|
int currentHeight = currentImage.getHeight(); |
|
|
int currentHeight = currentImage.getHeight(); |
|
|
|
|
|
int yRowStride = planes[0].getRowStride(); |
|
|
|
|
|
int yPixelStride = planes[0].getPixelStride(); |
|
|
|
|
|
|
|
|
if (previousWidth != currentWidth || previousHeight != currentHeight) { |
|
|
if (previousWidth != currentWidth || previousHeight != currentHeight) { |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
for (int row = 0; row < previousHeight; row++) { |
|
|
|
|
|
for (int col = 0; col < previousWidth; col++) { |
|
|
|
|
|
|
|
|
int blockSize = kleinstesQuadrat(previousHeight, previousWidth) / 8; |
|
|
|
|
|
|
|
|
|
|
|
int numBlocksX = currentWidth / blockSize; |
|
|
|
|
|
int numBlocksY = currentHeight / blockSize; |
|
|
|
|
|
|
|
|
int previousIndex = row * previousWidth + col; |
|
|
|
|
|
int currentIndex = row * currentWidth + col; |
|
|
|
|
|
|
|
|
for (int blockY = 0; blockY < numBlocksY; blockY++) { |
|
|
|
|
|
for (int blockX = 0; blockX < numBlocksX; blockX++) { |
|
|
|
|
|
startX = blockX * blockSize; |
|
|
|
|
|
startY = blockY * blockSize; |
|
|
|
|
|
endX = startX + blockSize; |
|
|
|
|
|
endY = startY + blockSize; |
|
|
|
|
|
|
|
|
int previousPixel = previousBuffer.get(previousIndex) & 0xFF; |
|
|
|
|
|
int currentPixel = currentBuffer.get(currentIndex) & 0xFF; |
|
|
|
|
|
|
|
|
float currentLuminance = berechneMittlereLuminanz(currentBuffer, yRowStride, yPixelStride); |
|
|
|
|
|
float previousLuminance = berechneMittlereLuminanz(previousBuffer, yRowStride, yPixelStride); |
|
|
|
|
|
|
|
|
int pixelDifference = Math.abs(previousPixel - currentPixel); |
|
|
|
|
|
int threshold = 120; |
|
|
|
|
|
|
|
|
int pixelDifference = Math.abs((int) previousLuminance - (int) currentLuminance); |
|
|
|
|
|
|
|
|
if (pixelDifference > threshold) { |
|
|
if (pixelDifference > threshold) { |
|
|
|
|
|
System.out.println(pixelDifference); |
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private float berechneMittlereLuminanz(ByteBuffer buffer, int rowStride, int pixelStride) { |
|
|
|
|
|
int sumLuminance = 0; |
|
|
|
|
|
int countPixels = 0; |
|
|
|
|
|
|
|
|
|
|
|
for (int row = startY; row < endY; row++) { |
|
|
|
|
|
for (int col = startX; col < endX; col++) { |
|
|
|
|
|
int bufferIndex = row * rowStride + col * pixelStride; |
|
|
|
|
|
int currentPixel = buffer.get(bufferIndex) & 0xFF; |
|
|
|
|
|
|
|
|
|
|
|
sumLuminance += currentPixel; |
|
|
|
|
|
countPixels++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return (float) sumLuminance / countPixels; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private int kleinstesQuadrat(int height, int width) { |
|
|
|
|
|
if (height == width) { |
|
|
|
|
|
return height; |
|
|
|
|
|
} else if (height > width) { |
|
|
|
|
|
return kleinstesQuadrat(height - width, width); |
|
|
|
|
|
} else { |
|
|
|
|
|
return kleinstesQuadrat(height, width - height); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private void resetAlarmStatus() { |
|
|
|
|
|
videoAlarmDetected = false; |
|
|
|
|
|
alarmResetRunnable = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
public void checkAlarmCondition() { |
|
|
public void checkAlarmCondition() { |
|
|
if (videoAlarmDetected && !mVideoAlarmDetected.getValue()) { |
|
|
if (videoAlarmDetected && !mVideoAlarmDetected.getValue()) { |
|
|
setMutableLiveDataVideoMovementDetected(); |
|
|
setMutableLiveDataVideoMovementDetected(); |