I am making an app which compare camera captured image with computer generated images and these images are stored in database.
I am using Opencv for image comparison but it only compare computer generated images correctly.When i capture same image with camera and compare that image with computer generated image then it does not match them acuurately.Is there any way where i can compare camera images also;
Here is my code
public static void main(String[] args) {
// Set image path
String path = System.getProperty("user.dir");
String filename1 = path+"/images/266.jpg";
String filename2 = path+"/images/20160805_121529.jpg";
int ret;
ret = compareFeature(filename1, filename2);
if (ret > 0) {
System.out.println("Two images are same.");
} else {
System.out.println("Two images are different.");
}
}
/**
* Compare that two images is similar using feature mapping
* #author minikim
* #param filename1 - the first image
* #param filename2 - the second image
* #return integer - count that has the similarity within images
*/
public static int compareFeature(String filename1, String filename2) {
int retVal = 0;
long startTime = System.currentTimeMillis();
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// Load images to compare
Mat img1 = Imgcodecs.imread(filename1, Imgcodecs.CV_LOAD_IMAGE_COLOR);
Mat img2 = Imgcodecs.imread(filename2, Imgcodecs.CV_LOAD_IMAGE_COLOR);
// Declare key point of images
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
Mat descriptors1 = new Mat();
Mat descriptors2 = new Mat();
// Definition of ORB key point detector and descriptor extractors
FeatureDetector detector = FeatureDetector.create(FeatureDetector.ORB);
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.ORB);
// Detect key points
detector.detect(img1, keypoints1);
detector.detect(img2, keypoints2);
// Extract descriptors
extractor.compute(img1, keypoints1, descriptors1);
extractor.compute(img2, keypoints2, descriptors2);
// Definition of descriptor matcher
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
// Match points of two images
MatOfDMatch matches = new MatOfDMatch();
// System.out.println("Type of Image1= " + descriptors1.type() + ", Type of Image2= " + descriptors2.type());
// System.out.println("Cols of Image1= " + descriptors1.cols() + ", Cols of Image2= " + descriptors2.cols());
// Avoid to assertion failed
// Assertion failed (type == src2.type() && src1.cols == src2.cols && (type == CV_32F || type == CV_8U)
if (descriptors2.cols() == descriptors1.cols()) {
matcher.match(descriptors1, descriptors2 ,matches);
// Check matches of key points
DMatch[] match = matches.toArray();
double max_dist = 0; double min_dist = 100;
for (int i = 0; i < descriptors1.rows(); i++) {
double dist = match[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
System.out.println("max_dist=" + max_dist + ", min_dist=" + min_dist);
// Extract good images (distances are under 10)
for (int i = 0; i < descriptors1.rows(); i++) {
if (match[i].distance <= 10) {
retVal++;
}
}
System.out.println("matching count=" + retVal);
}
long estimatedTime = System.currentTimeMillis() - startTime;
System.out.println("estimatedTime=" + estimatedTime + "ms");
return retVal;
}
Here is pictures that i am comparing
And here is camera captured image
Thanks in Advance
Related
I have a series of mp4 files saved on the device that need to be merged together to make a single mp4 file.
video_p1.mp4 video_p2.mp4 video_p3.mp4 > video.mp4
The solutions I have researched such as the mp4parser framework use deprecated code.
The best solution I could find is using a MediaMuxer and MediaExtractor.
The code runs but my videos are not merged (only the content in video_p1.mp4 is displayed and it is in landscape orientation, not portrait).
Can anyone help me sort this out?
public static boolean concatenateFiles(File dst, File... sources) {
if ((sources == null) || (sources.length == 0)) {
return false;
}
boolean result;
MediaExtractor extractor = null;
MediaMuxer muxer = null;
try {
// Set up MediaMuxer for the destination.
muxer = new MediaMuxer(dst.getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// Copy the samples from MediaExtractor to MediaMuxer.
boolean sawEOS = false;
//int bufferSize = MAX_SAMPLE_SIZE;
int bufferSize = 1 * 1024 * 1024;
int frameCount = 0;
int offset = 100;
ByteBuffer dstBuf = ByteBuffer.allocate(bufferSize);
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
long timeOffsetUs = 0;
int dstTrackIndex = -1;
for (int fileIndex = 0; fileIndex < sources.length; fileIndex++) {
int numberOfSamplesInSource = getNumberOfSamples(sources[fileIndex]);
// Set up MediaExtractor to read from the source.
extractor = new MediaExtractor();
extractor.setDataSource(sources[fileIndex].getPath());
// Set up the tracks.
SparseIntArray indexMap = new SparseIntArray(extractor.getTrackCount());
for (int i = 0; i < extractor.getTrackCount(); i++) {
extractor.selectTrack(i);
MediaFormat format = extractor.getTrackFormat(i);
if (dstTrackIndex < 0) {
dstTrackIndex = muxer.addTrack(format);
muxer.start();
}
indexMap.put(i, dstTrackIndex);
}
long lastPresentationTimeUs = 0;
int currentSample = 0;
while (!sawEOS) {
bufferInfo.offset = offset;
bufferInfo.size = extractor.readSampleData(dstBuf, offset);
if (bufferInfo.size < 0) {
sawEOS = true;
bufferInfo.size = 0;
timeOffsetUs += (lastPresentationTimeUs + 0);
}
else {
lastPresentationTimeUs = extractor.getSampleTime();
bufferInfo.presentationTimeUs = extractor.getSampleTime() + timeOffsetUs;
bufferInfo.flags = extractor.getSampleFlags();
int trackIndex = extractor.getSampleTrackIndex();
if ((currentSample < numberOfSamplesInSource) || (fileIndex == sources.length - 1)) {
muxer.writeSampleData(indexMap.get(trackIndex), dstBuf, bufferInfo);
}
extractor.advance();
frameCount++;
currentSample++;
Log.d("tag2", "Frame (" + frameCount + ") " +
"PresentationTimeUs:" + bufferInfo.presentationTimeUs +
" Flags:" + bufferInfo.flags +
" TrackIndex:" + trackIndex +
" Size(KB) " + bufferInfo.size / 1024);
}
}
extractor.release();
extractor = null;
}
result = true;
}
catch (IOException e) {
result = false;
}
finally {
if (extractor != null) {
extractor.release();
}
if (muxer != null) {
muxer.stop();
muxer.release();
}
}
return result;
}
public static int getNumberOfSamples(File src) {
MediaExtractor extractor = new MediaExtractor();
int result;
try {
extractor.setDataSource(src.getPath());
extractor.selectTrack(0);
result = 0;
while (extractor.advance()) {
result ++;
}
}
catch(IOException e) {
result = -1;
}
finally {
extractor.release();
}
return result;
}
I'm using this library for muxing videos: ffmpeg-android-java
gradle dependency:
implementation 'com.writingminds:FFmpegAndroid:0.3.2'
Here's how I use it in my project to mux video and audio in kotlin: VideoAudioMuxer
So basically it works like the ffmpeg in terminal but you're inputing your command to a method as an array of strings along with a listener.
fmpeg.execute(arrayOf("-i", videoPath, "-i", audioPath, "$targetPath.mp4"), object : ExecuteBinaryResponseHandler() {
You'll have to search how to merge videos in ffmpeg and convert the commands into array of strings for the argument you need.
You could probably do almost anything, since ffmpeg is a very powerful tool.
I am running template matching using openCV 3.4.7 Android SDK (java).
The code work almost perfectly; when the template is match, it draws a rectangle on the matching area. The problem is that even when there is no match, it draws a random rectangle. I think that happens because the threshold is not set correctly. If so, can someone please help me out?
Here's the code:
public static void run(String inFile, String templateFile, String outFile,
int match_method) {
Mat img = Imgcodecs.imread(inFile);
Mat templ = Imgcodecs.imread(templateFile);
// / Create the result matrix
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
// / Do the Matching and Normalize
Imgproc.matchTemplate(img, templ, result, match_method);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
// / Localizing the best match with minMaxLoc
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc;
if (match_method == Imgproc.TM_SQDIFF
|| match_method == Imgproc.TM_SQDIFF_NORMED) {
matchLoc = mmr.minLoc;
} else {
matchLoc = mmr.maxLoc;
}
// / Show me what you got
Imgproc.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 0, 128));
// Save the visualized detection.
System.out.println("Writing " + outFile);
Imgcodecs.imwrite(outFile, img);
}
You can use Imgproc.TM_CCOEFF_NORMED or Imgproc.TM_CCORR_NORMED and mmr.maxVal >= 0.8. It should take care of most of your false positives.
Sample Code:
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.io.File;
import java.nio.file.Files;
public class templateMatchingTester {
private static String str = null;
static {
if (str == null) {
str = "initialised";
nu.pattern.OpenCV.loadShared();
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
}
private static Mat createMatrixFromImage(String imagePath) {
Mat imageMatrix = Imgcodecs.imread(imagePath);
Mat greyImage = new Mat();
Imgproc.cvtColor(imageMatrix, greyImage, Imgproc.COLOR_BGR2GRAY);
return greyImage;
}
private static boolean matchTemplate(String pathToInputImage,String pathToTemplate){
Mat inputImage = createMatrixFromImage(pathToInputImage);
Mat templateImage = createMatrixFromImage(pathToTemplate);
// Create the result matrix
int result_cols = inputImage.cols() - templateImage.cols() + 1;
int result_rows = inputImage.rows() - templateImage.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_8UC1);
int match_method;
match_method = Imgproc.TM_CCOEFF_NORMED;//Imgproc.TM_CCORR_NORMED;
Imgproc.matchTemplate(inputImage, templateImage, result, match_method);
Core.MinMaxLocResult mmr = Core.minMaxLoc(result);
double minMatchQuality = 0.85;
System.out.println(mmr.maxVal);
if (mmr.maxVal >= minMatchQuality){
return true;
} else
return false;
}
public static void main(String args[]) {
String template = "path/to/your/templateImage";
final File folder = new File("path/to/your/testImagesFolder/");
int matchCount = 0;
for (final File fileEntry : folder.listFiles()){
if (matchTemplate(fileEntry.getPath(),template)){
matchCount+=1;
}else
System.out.println(fileEntry.getPath());
}
System.out.println(matchCount);
}
}
Use a normed match method to ensure your match value is [0..1].
Replace this line
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
with a thresholding operation. Otherwise a best match of 0.9 would become 1 by the second normalization and you would lose the actual match "quality" information.
Normalizing the result of the template matching will always result in your best match being 1 making it impossible to discard a bad match.
i wrote an app that would take a screenshot of the game overwatch and attempt to tell who is on each team. using template matching and open cv. project need to iterate over the result image and check values.
OpenCVUtils.getPointsFromMatAboveThreshold(result,
0.90f)
public static void scaleAndCheckAll(String guid){
Mat source = imread(IMG_PROC_PATH + guid); //load the source image
Mat scaledSrc = new Mat(defaultScreenshotSize, source.type());
resize(source, scaledSrc, defaultScreenshotSize);
Mat sourceGrey = new Mat(scaledSrc.size(), CV_8UC1);
cvtColor(scaledSrc, sourceGrey, COLOR_BGR2GRAY);
for (String hero : getCharacters()) {
Mat template = OpenCVUtils.matFromJar(TEMPLATES_FOLDER + hero + ".png", 0); //load a template
Size size = new Size(sourceGrey.cols()-template.cols()+1, sourceGrey.rows()-template.rows()+1);
Mat result = new Mat(size, CV_32FC1);
matchTemplate(sourceGrey, template, result, TM_CCORR_NORMED);// get results
Scalar color = OpenCVUtils.randColor();
List<Point> points = OpenCVUtils.getPointsFromMatAboveThreshold(result,
0.90f);
for (Point point : points) {
//rectangle(scaledSrc, new Rect(point.x(),point.y(),template.cols(),template.rows()), color, -2, 0, 0);
putText(scaledSrc, hero, point, FONT_HERSHEY_PLAIN, 2, color);
}
}
String withExt = IMG_PROC_PATH + guid +".png";
imwrite(withExt, scaledSrc);
File noExt = new File(IMG_PROC_PATH + guid);
File ext = new File(withExt);
noExt.delete();
ext.renameTo(noExt);
}
the other method.
public static List<Point> getPointsFromMatAboveThreshold(Mat m, float t){
List<Point> matches = new ArrayList<Point>();
FloatIndexer indexer = m.createIndexer();
for (int y = 0; y < m.rows(); y++) {
for (int x = 0; x < m.cols(); x++) {
if (indexer.get(y,x)>t) {
System.out.println("(" + x + "," + y +") = "+ indexer.get(y,x));
matches.add(new Point(x, y));
}
}
}
return matches;
}
you can just get the first from the list or see how close they are if you expect multiple matches.
I'm trying to perform feature matching on images in Java. The code example for this I found is this one. I fixed the FeatureDetector deprecation via this answer.
However, when I use this code, it does not detect a sub image being part of the bigger image when they clearly are. Here is my adapted code example:
public static boolean performFeatureMatching(BufferedImage largeBufferedImage, BufferedImage smallBufferedImage) throws IOException
{
FastFeatureDetector fd = FastFeatureDetector.create();
final MatOfKeyPoint keyPointsLarge = new MatOfKeyPoint();
final MatOfKeyPoint keyPointsSmall = new MatOfKeyPoint();
Mat largeImage = bufferedImage2Mat(largeBufferedImage);
Mat smallImage = bufferedImage2Mat(smallBufferedImage);
fd.detect(largeImage, keyPointsLarge);
fd.detect(smallImage, keyPointsSmall);
System.out.println("keyPoints.size() : " + keyPointsLarge.size());
System.out.println("keyPoints2.size() : " + keyPointsSmall.size());
Mat descriptorsLarge = new Mat();
Mat descriptorsSmall = new Mat();
DescriptorExtractor extractor = DescriptorExtractor.create(DescriptorExtractor.BRISK);
extractor.compute(largeImage, keyPointsLarge, descriptorsLarge);
extractor.compute(smallImage, keyPointsSmall, descriptorsSmall);
System.out.println("descriptorsA.size() : " + descriptorsLarge.size());
System.out.println("descriptorsB.size() : " + descriptorsSmall.size());
MatOfDMatch matches = new MatOfDMatch();
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT);
matcher.match(descriptorsLarge, descriptorsSmall, matches);
System.out.println("matches.size() : " + matches.size());
MatOfDMatch matchesFiltered = new MatOfDMatch();
List<DMatch> matchesList = matches.toList();
List<DMatch> bestMatches = new ArrayList<>();
Double max_dist = 0.0;
Double min_dist = 100.0;
for (DMatch aMatchesList : matchesList)
{
Double dist = (double) aMatchesList.distance;
if (dist < min_dist && dist != 0)
{
min_dist = dist;
}
if (dist > max_dist)
{
max_dist = dist;
}
}
System.out.println("max_dist : " + max_dist);
System.out.println("min_dist : " + min_dist);
if (min_dist > 50)
{
System.out.println("No match found");
System.out.println("Just return ");
return false;
}
double threshold = 3 * min_dist;
double threshold2 = 2 * min_dist;
if (threshold > 75)
{
threshold = 75;
} else if (threshold2 >= max_dist)
{
threshold = min_dist * 1.1;
} else if (threshold >= max_dist)
{
threshold = threshold2 * 1.4;
}
System.out.println("Threshold : " + threshold);
for (int i = 0; i < matchesList.size(); i++)
{
double dist = (double) matchesList.get(i).distance;
if (dist < threshold)
{
bestMatches.add(matches.toList().get(i));
//System.out.println(String.format(i + " best match added : %s", dist));
}
}
matchesFiltered.fromList(bestMatches);
System.out.println("matchesFiltered.size() : " + matchesFiltered.size());
if (matchesFiltered.rows() >= 4)
{
System.out.println("match found");
return true;
} else
{
return false;
}
}
My test is the following:
#Test
public void testFeatureMatching() throws IOException
{
BufferedImage completeImage = getBufferedImageFromClasspath("lena.png");
BufferedImage subImage = getBufferedImageFromClasspath("lips.png");
boolean matches = performFeatureMatching(completeImage, subImage);
assertTrue(matches);
}
The example images are the following:
Since the lower image is cut out of the upper one it should definitely be found but the match returns false.
Furthermore there are still deprecations left in the code (related question):
Warning:(7, 29) java: org.opencv.features2d.DescriptorExtractor in org.opencv.features2d has been deprecated
Warning:(37, 17) java: org.opencv.features2d.DescriptorExtractor in org.opencv.features2d has been deprecated
Warning:(37, 76) java: org.opencv.features2d.DescriptorExtractor in org.opencv.features2d has been deprecated
Warning:(37, 49) java: org.opencv.features2d.DescriptorExtractor in org.opencv.features2d has been deprecated
Can someone fix the deprecations and/or the general code itself to make feature detection work properly because I can't get it to detect anything correctly?
I'm a beginner to OpenCV.
I'm trying to do template matching with multiple tempates.
for single template i get the source code from here
OpenCV Template Matching example in Android
I searched in the internet and I couldn't find a proper android or java code which satisfy my requirement.
I have C++ code but I dont know how to translate it.
Can you please help me to find a proper java or android code. Or else please help me with translate this C++ code into java, which I can use inside android application.
Thank you in advance.
C++ code
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
bool FindTemplate(Mat Img_Scene_Bgr,Mat Img_Template_Bgr,Point &Point_TemplateLocation)
{
// `Img_Scene_Bgr` and `Img_Template_Bgr` are the reference and template image
cv::Mat Img_Result_Float(Img_Scene_Bgr.rows-Img_Template_Bgr.rows+1, Img_Scene_Bgr.cols-Img_Template_Bgr.cols+1, CV_32FC1);
cv::matchTemplate(Img_Template_Bgr, Img_Scene_Bgr, Img_Result_Float, CV_TM_CCOEFF_NORMED);
normalisasi normalize( Img_Result_Float, Img_Result_Float, 0, 1, NORM_MINMAX, -1, Mat() );
double minval, maxval, threshold = 0.7;
cv::Point minloc, maxloc;
cv::minMaxLoc(Img_Result_Float, &minval, &maxval, &minloc, &maxloc);
if (maxval >= threshold)
{
Point_TemplateLocation= maxloc;
return true;
}
else
{
return false;
}
}
int main( int argc, char** argv )
{
Mat Img_Scene;
Mat Img_Template_1;
Mat Img_Template_2;
Mat Img_Result;
char* image_window = "Source Image";
char* result_window = "Result window";
/// Load image and template
Img_Scene = imread("SceneImage.png", 1 );
Img_Template_1 = imread( "Templ1.png", 1 );
Img_Template_2 = imread( "Templ2.png", 1 );
if(Img_Scene.data== NULL||Img_Template_1.data==NULL||Img_Template_2.data==NULL)
{
cout<<"Image Not Found";
return 0;
}
Img_Result= Img_Scene.clone();
Vector<Mat> List_Template_Img;
List_Template_Img.push_back(Img_Template_1);//Otherwise Get some folder & add the Files in it
List_Template_Img.push_back(Img_Template_2);
Point Point_TemplateLocation;
for (int i = 0; i < List_Template_Img.size(); i++)
{
if(!FindTemplate(Img_Scene,List_Template_Img[i],Point_TemplateLocation))
{
cout<<"No Match Found";
}
/// Show me what you got
rectangle( Img_Result, Point_TemplateLocation, Point( Point_TemplateLocation.x + Img_Template_1.cols , Point_TemplateLocation.y + Img_Template_1.rows ), Scalar(0,0,255), 2, 8, 0 );
putText( Img_Result, format("Object %d ",i),Point( Point_TemplateLocation.x + Img_Template_1.cols/4 , Point_TemplateLocation.y + Img_Template_1.rows/2 ),1,1,Scalar(255,0,0),1,-1);
}
/// Create windows
namedWindow( image_window, CV_WINDOW_AUTOSIZE );
namedWindow( result_window, CV_WINDOW_AUTOSIZE );
imshow( image_window, Img_Template_1);
imshow( image_window, Img_Template_2);
imshow( result_window, Img_Result );
waitKey(0);
return 0;
}
I tried to write a script like this, but I am confused in the calculation process and bring the results of calculations
public void matching() {
String img_eq = Environment.getExternalStorageDirectory().getAbsolutePath() + "/img_eq/img_eq.jpg";
String template = Environment.getExternalStorageDirectory().getAbsolutePath() + "/img_template/img_template.jpg";
String template2 = Environment.getExternalStorageDirectory().getAbsolutePath() + "/img_template/img_template2.jpg";
Mat img = Highgui.imread(img_eq, CvType.CV_8SC3);
Mat templ = Highgui.imread(template, CvType.CV_8SC3);
Mat templ_2 = Highgui.imread(template2, CvType.CV_8SC3);
int match_method = Imgproc.TM_SQDIFF_NORMED;
int result_cols = img.cols() - templ.cols() + 1;
int result_rows = img.rows() - templ.rows() + 1;
Mat result = new Mat(result_rows, result_cols, CvType.CV_8SC3);
Imgproc.matchTemplate(img, templ, result, match_method);
Core.normalize(result, result, 0, 1, Core.NORM_MINMAX, -1, new Mat());
// / Localizing the best match with minMaxLoc
MinMaxLocResult mmr = Core.minMaxLoc(result);
Point matchLoc = new Point();
//for (int i = 0; i < List_template_image.size(); i++)
{
if (match_method == Imgproc.TM_SQDIFF || match_method == Imgproc.TM_SQDIFF_NORMED) {
matchLoc = mmr.minLoc;
} else {
matchLoc = mmr.maxLoc;
}
// / Show me what you got
Core.rectangle(img, matchLoc, new Point(matchLoc.x + templ.cols(),
matchLoc.y + templ.rows()), new Scalar(0, 255, 0));
// Save the visualized detection.
Highgui.imwrite("/mnt/sdcard/img_result/img_result.jpg", img);
Mat image = Highgui.imread("/mnt/sdcard/img_result/img_result.jpg");
Mat android_image = Mat.zeros(image.cols(), image.rows(), CvType.CV_8SC3);
Imgproc.cvtColor(image, android_image, Imgproc.COLOR_BGR2RGB);
Bitmap bm = Bitmap.createBitmap(android_image.cols(),android_image.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(android_image, bm);
ImageView iv = (ImageView) findViewById(R.id.image);
iv.setImageBitmap(bm);
}
NOTE: I have updated this since originally asking the question to reflect some of what I have learned about loading live camera images into the ffmpeg libraries.
I am using ffmpeg from javacv compiled for Android to encode/decode video for my application. (Note that originally, I was trying to use ffmpeg-java, but it has some incompatible libraries)
Original problem: The problem that I've run into is that I am currently getting each frame as a Bitmap (just a plain android.graphics.Bitmap) and I can't figure out how to stuff that into the encoder.
Solution in javacv's ffmpeg: Use avpicture_fill(), the format from Android is supposedly YUV420P, though I can't verify this until my encoder issues (below) are fixed.
avcodec.avpicture_fill((AVPicture)mFrame, picPointer, avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT)
Problem Now: The line that is supposed to actually encode the data crashes the thread. I get a big native code stack trace that I'm unable to understand. Does anybody have a suggestion?
Here is the code that I am using to instantiate all the ffmpeg libraries:
avcodec.avcodec_register_all();
avcodec.avcodec_init();
avformat.av_register_all();
mCodec = avcodec.avcodec_find_encoder(avcodec.CODEC_ID_H263);
if (mCodec == null)
{
Logging.Log("Unable to find encoder.");
return;
}
Logging.Log("Found encoder.");
mCodecCtx = avcodec.avcodec_alloc_context();
mCodecCtx.bit_rate(300000);
mCodecCtx.codec(mCodec);
mCodecCtx.width(VIDEO_WIDTH);
mCodecCtx.height(VIDEO_HEIGHT);
mCodecCtx.pix_fmt(avutil.PIX_FMT_YUV420P);
mCodecCtx.codec_id(avcodec.CODEC_ID_H263);
mCodecCtx.codec_type(avutil.AVMEDIA_TYPE_VIDEO);
AVRational ratio = new AVRational();
ratio.num(1);
ratio.den(30);
mCodecCtx.time_base(ratio);
mCodecCtx.coder_type(1);
mCodecCtx.flags(mCodecCtx.flags() | avcodec.CODEC_FLAG_LOOP_FILTER);
mCodecCtx.me_cmp(avcodec.FF_LOSS_CHROMA);
mCodecCtx.me_method(avcodec.ME_HEX);
mCodecCtx.me_subpel_quality(6);
mCodecCtx.me_range(16);
mCodecCtx.gop_size(30);
mCodecCtx.keyint_min(10);
mCodecCtx.scenechange_threshold(40);
mCodecCtx.i_quant_factor((float) 0.71);
mCodecCtx.b_frame_strategy(1);
mCodecCtx.qcompress((float) 0.6);
mCodecCtx.qmin(10);
mCodecCtx.qmax(51);
mCodecCtx.max_qdiff(4);
mCodecCtx.max_b_frames(1);
mCodecCtx.refs(2);
mCodecCtx.directpred(3);
mCodecCtx.trellis(1);
mCodecCtx.flags2(mCodecCtx.flags2() | avcodec.CODEC_FLAG2_BPYRAMID | avcodec.CODEC_FLAG2_WPRED | avcodec.CODEC_FLAG2_8X8DCT | avcodec.CODEC_FLAG2_FASTPSKIP);
if (avcodec.avcodec_open(mCodecCtx, mCodec) == 0)
{
Logging.Log("Unable to open encoder.");
return;
}
Logging.Log("Encoder opened.");
mFrameSize = avcodec.avpicture_get_size(avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT);
Logging.Log("Frame size - '" + mFrameSize + "'.");
//mPic = new AVPicture(mPicSize);
mFrame = avcodec.avcodec_alloc_frame();
if (mFrame == null)
{
Logging.Log("Unable to alloc frame.");
}
This is what I want to be able to execute next:
BytePointer picPointer = new BytePointer(data);
int bBuffSize = mFrameSize;
BytePointer bBuffer = new BytePointer(bBuffSize);
int picSize = 0;
if ((picSize = avcodec.avpicture_fill((AVPicture)mFrame, picPointer, avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT)) <= 0)
{
Logging.Log("Couldn't convert preview to AVPicture (" + picSize + ")");
return;
}
Logging.Log("Converted preview to AVPicture (" + picSize + ")");
VCAP_Package vPackage = new VCAP_Package();
if (mCodecCtx.isNull())
{
Logging.Log("Codec Context is null!");
}
//encode the image
int size = avcodec.avcodec_encode_video(mCodecCtx, bBuffer, bBuffSize, mFrame);
int totalSize = 0;
while (size >= 0)
{
totalSize += size;
Logging.Log("Encoded '" + size + "' bytes.");
//Get any delayed frames
size = avcodec.avcodec_encode_video(mCodecCtx, bBuffer, bBuffSize, null);
}
Logging.Log("Finished encoding. (" + totalSize + ")");
But, as of now, I don't know how to put the Bitmap into the right piece or if I have that setup correctly.
A few notes about the code:
- VIDEO_WIDTH = 352
- VIDEO_HEIGHT = 288
- VIDEO_FPS = 30;
After a lot of searching, I figured out that you have to load the pointers in a fairly strict and awkward manner. This is how I got everything working:
Codec setup:
avcodec.avcodec_register_all();
avcodec.avcodec_init();
avformat.av_register_all();
/* find the H263 video encoder */
mCodec = avcodec.avcodec_find_encoder(avcodec.CODEC_ID_H263);
if (mCodec == null) {
Log.d("TEST_VIDEO", "avcodec_find_encoder() run fail.");
}
mCodecCtx = avcodec.avcodec_alloc_context();
picture = avcodec.avcodec_alloc_frame();
/* put sample parameters */
mCodecCtx.bit_rate(400000);
/* resolution must be a multiple of two */
mCodecCtx.width(VIDEO_WIDTH);
mCodecCtx.height(VIDEO_HEIGHT);
/* frames per second */
AVRational avFPS = new AVRational();
avFPS.num(1);
avFPS.den(VIDEO_FPS);
mCodecCtx.time_base(avFPS);
mCodecCtx.pix_fmt(avutil.PIX_FMT_YUV420P);
mCodecCtx.codec_id(avcodec.CODEC_ID_H263);
mCodecCtx.codec_type(avutil.AVMEDIA_TYPE_VIDEO);
/* open it */
if (avcodec.avcodec_open(mCodecCtx, mCodec) < 0) {
Log.d("TEST_VIDEO", "avcodec_open() run fail.");
}
/* alloc image and output buffer */
output_buffer_size = 100000;
output_buffer = avutil.av_malloc(output_buffer_size);
size = mCodecCtx.width() * mCodecCtx.height();
picture_buffer = avutil.av_malloc((size * 3) / 2); /* size for YUV 420 */
picture.data(0, new BytePointer(picture_buffer));
picture.data(1, picture.data(0).position(size));
picture.data(2, picture.data(1).position(size / 4));
picture.linesize(0, mCodecCtx.width());
picture.linesize(1, mCodecCtx.width() / 2);
picture.linesize(2, mCodecCtx.width() / 2);
Handling the preview data:
//(1)Convert byte[] first
byte[] data420 = new byte[data.length];
convert_yuv422_to_yuv420(data, data420, VIDEO_WIDTH, VIDEO_HEIGHT);
//(2) Fill picture buffer
int data1_offset = VIDEO_HEIGHT * VIDEO_WIDTH;
int data2_offset = data1_offset * 5 / 4;
int pic_linesize_0 = picture.linesize(0);
int pic_linesize_1 = picture.linesize(1);
int pic_linesize_2 = picture.linesize(2);
//Y
for(y = 0; y < VIDEO_HEIGHT; y++)
{
for(x = 0; x < VIDEO_WIDTH; x++)
{
picture.data(0).put((y * pic_linesize_0 + x), data420[y * VIDEO_WIDTH + x]);
}
}
//Cb and Cr
for(y = 0; y < VIDEO_HEIGHT / 2; y++) {
for(x = 0; x < VIDEO_WIDTH / 2; x++) {
picture.data(1).put((y * pic_linesize_1 + x), data420[data1_offset + y * VIDEO_WIDTH / 2 + x]);
picture.data(2).put((y * pic_linesize_2 + x), data420[data2_offset + y * VIDEO_WIDTH / 2 + x]);
}
}
//(2)Encode
//Encode the image into output_buffer
out_size = avcodec.avcodec_encode_video(mCodecCtx, new BytePointer(output_buffer), output_buffer_size, picture);
Log.d("TEST_VIDEO", "Encoded '" + out_size + "' bytes");
//Delayed frames
for(; out_size > 0; i++) {
out_size = avcodec.avcodec_encode_video(mCodecCtx, new BytePointer(output_buffer), output_buffer_size, null);
Log.d("TEST_VIDEO", "Encoded '" + out_size + "' bytes");
//fwrite(output_buffer, 1, out_size, file);
}
I am still working to packetize the data, but the ongoing test project can be found here # http://code.google.com/p/test-video-encode/
Does android graphics library support the YUV format:
codecCtx.pix_fmt = AVCodecLibrary.PIX_FMT_YUV420P;
See if you can set it to ARGB or RGB32. I know the android graphics library supports this pixel format.
PS: I don't know anything about ffmpeg