Image Rotation : Perspective Transformation or Affline Transformation? - opencv

I want to rotate following image by 20 degree at center.
I can achieve this in OpenCV by two different ways:
1. Perspective Transformation
2. Affine Transformation
public void perspectiveXformation(String imgPath, List<Point> sourceCorners,
List<Point> targetCorners) {
// Load image in gray-scale format
Mat matIncomingImg = Highgui.imread(imgPath, 0);
// Check if size of list, process only if there are four points in list.
if (sourceCorners.size() == 4) {
// Convert vector points into Mat type of object.
Mat sourceCornersMat =
Converters.vector_Point2f_to_Mat(sourceCorners);
Mat targetCornersMat =
Converters.vector_Point2f_to_Mat(targetCorners);
Mat matResultant = new Mat();
// Do the Perspective transformation
Mat matPtransform =
Imgproc.getPerspectiveTransform(sourceCornersMat,
targetCornersMat);
Imgproc.warpPerspective(matIncomingImg, matResultant,
matPtransform,
new Size(targetCorners.get(2).x, targetCorners.get(2).y));
Highgui.imwrite("/tmp/perspectiveXform.png", matResultant);
}
}
public void afflineXformation(String imgPath, Point center) {
Mat selectedMat = Highgui.imread(imgPath, 0);
Mat res = Imgproc.getRotationMatrix2D(center, 20, 1.0);
Mat newMat = new Mat();
Imgproc.warpAffine(selectedMat, newMat, res, selectedMat.size());
Highgui.imwrite("/tmp/afflineXform.png", newMat);
}
Which is the preferred way of rotating image ?

Related

How to improve line detection with OpenCV

I am trying to detect lines in an image using OpenCV (Emgu.Net implementation).
So far, results are not that bad, but it appears to me not be consistent.
For instance, as you can see from the original and the results, some lines are well captured, yet the small top vertical ones are not, despite the fact that they have the same thickness.
Here is my code:
public static void ProcessImage(Mat src, bool createComposedImage)
{
//Convert the image to grayscale
Mat gray = new Mat();
CvInvoke.CvtColor(src, gray, ColorConversion.Bgr2Gray);
//Use Gaussian filtering to remove noise
CvInvoke.GaussianBlur(gray, gray, new Size(3, 3), 1);
double cannyThreshold = 180.0;
Mat result = new Mat();
List<Mat> resultPortions = new List<Mat>();
if (createComposedImage)
{
resultPortions.Add(src);
}
using (Mat cannyEdges = new Mat())
{
double cannyThresholdLinking = 120.0;
CvInvoke.Canny(gray, cannyEdges, cannyThreshold, cannyThresholdLinking);
LineSegment2D[] lines = CvInvoke.HoughLinesP(
image: cannyEdges,
rho: 1, //Distance resolution in pixel-related units
theta: Math.PI / 180.0, //Angle resolution measured in radians.
10, //threshold
30, //min Line width
10); //gap between lines
if (createComposedImage)
{
Mat lineImage = ProcessLinesInImage(gray, lines);
resultPortions.Add(lineImage);
}
if (createComposedImage)
{
try
{
CvInvoke.VConcat(resultPortions.ToArray(), result);
}
catch
{
}
foreach (Mat resultPortion in resultPortions)
{
resultPortion.Dispose();
}
}
}
return;
}
Here are the images

Remove de-focus region on image by opencv

I have an image and 2 regions (focus region and de-focus region). I use Open CV, I want to detect near region.
I apply watershed in OpenCV or Canny detector to detect the object. But the object includes near and far region.
So, I need an idea or help from anyone help me apply OpenCV to detect near region image.
Code for show image, that I attached.
private Mat CalculateMapStrength(Mat inputMat){
Imgproc.cvtColor(inputMat,inputMat, Imgproc.COLOR_RGBA2GRAY);
//Compute dx and dy derivatives
Mat dx = new Mat();
Mat dy = new Mat();
Imgproc.Sobel(inputMat, dx, CV_32F, 1, 0);
Imgproc.Sobel(inputMat, dy, CV_32F, 0, 1);
Core.convertScaleAbs(dx,dx);
Core.convertScaleAbs(dy,dy);
Mat outputMat = new Mat();
Core.addWeighted(dx,0.5,dy,0.5,0,outputMat);
return outputMat;
}
Beside, I get Image segmentation by watershed algorithm in OpenCV. Can I compile 2 result for detect object? How to compile that?
public Mat steptowatershed(Mat img)
{
Mat threeChannel = new Mat();
Imgproc.cvtColor(img, threeChannel, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(threeChannel, threeChannel, 100, 255, Imgproc.THRESH_BINARY);
Mat fg = new Mat(img.size(),CvType.CV_8U);
Imgproc.erode(threeChannel,fg,new Mat());
Mat bg = new Mat(img.size(),CvType.CV_8U);
Imgproc.dilate(threeChannel,bg,new Mat());
Imgproc.threshold(bg,bg,1, 128,Imgproc.THRESH_BINARY_INV);
Mat markers = new Mat(img.size(),CvType.CV_8U, new Scalar(0));
Core.add(fg, bg, markers);
Mat result1=new Mat();
WatershedSegmenter segmenter = new WatershedSegmenter();
segmenter.setMarkers(markers);
Imgproc.cvtColor(img, img, Imgproc.COLOR_RGBA2RGB);
result1 = segmenter.process(img);
return result1;
}
public class WatershedSegmenter
{
public Mat markers=new Mat();
public void setMarkers(Mat markerImage)
{
markerImage.convertTo(markers, CvType.CV_32SC1);
}
public Mat process(Mat image)
{
Imgproc.watershed(image,markers);
markers.convertTo(markers,CvType.CV_8U);
return markers;
}
}

OpenCV sharpen the edges (edges with no holes)

I am trying to detect the biggest/larger rectangular shape and draw bounding box to the detected area.
In my use case, very often (and not always) the object that represent the rectangle shape is in color white and the background is also in color very similar to white.
Before detecting contours, I have preprocessed the image in order to detect perfect edge.
My problem is that I can't detect edges perfectly and i have a lot of noise even after blurring and using 'adaptive threshold' or 'threshold'.
The original image i have used for contours detection
I have tried different way to detect perfect edge in different lighting condition without success.
How can I process image in order to detect perfect edge (edges with no holes) for contour detection ?
Below is the code i am using
public static Mat findRectangleX(Mat original) {
Mat src = original.clone();
Mat gray = new Mat();
Mat binary = new Mat();
MatOfPoint2f approxCurve;
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
if (original.type() != CvType.CV_8U) {
Imgproc.cvtColor(original, gray, Imgproc.COLOR_BGR2GRAY);
} else {
original.copyTo(gray);
}
Imgproc.GaussianBlur(gray, gray, new Size(5,5),0);
Imgproc.adaptiveThreshold(gray, binary, 255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,11, 1);
//Imgproc.threshold(gray, binary,0,255,Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
double maxArea = 0;
Imgproc.findContours(binary, contours, new Mat(),Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
for (int i = 0; i<contours.size();i++) {
MatOfPoint contour = contours.get(i);
MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());
double area = Imgproc.contourArea(contour);
approxCurve = new MatOfPoint2f();
Imgproc.approxPolyDP(temp, approxCurve, Imgproc.arcLength(temp, true) * 0.03, true);
if (approxCurve.total() == 4 ) {
Rect rect = Imgproc.boundingRect(contours.get(i));
Imgproc.rectangle(src, rect.tl(), rect.br(), new Scalar(255, 0, 0, .8), 4);
if(maxArea < area)
maxArea = area;
}
}
Log.v(TAG, "Total contours found : " + contours.size());
Log.v(TAG, "Max area :" + maxArea);
return src;
}
I've search similar problems on stackoverflow and try code sample but any of them worked for me. The difficulty i think is the white objet on white background.
How can I process image in order to sharpen the edges for contour detection ?
How can I detect the biggest/larger rectangle shape and draw rectangle line to the detected shape ?
//Updated at : 20/02/2017
i have tried the solution suggested by #Nejc in the post below. The segmentation is better but i still have holes in contour and findcontours fails in detecting the larger contour.
Below is the code provided by #Nejc and translated to java.
public static Mat process(Mat original){
Mat src = original.clone();
Mat hsvMat = new Mat();
Mat saturation = new Mat();
Mat sobx = new Mat();
Mat soby = new Mat();
Mat grad_abs_val_approx = new Mat();
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
List<Mat> hsv_channels = new ArrayList<Mat>(3);
Core.split(hsvMat, hsv_channels);
Mat hue = hsv_channels.get( 0 );
Mat sat = hsv_channels.get( 1 );
Mat val = hsv_channels.get( 2 );
Imgproc.GaussianBlur(sat, saturation, new Size(9, 9), 2, 2);
Mat imf = new Mat();
saturation.convertTo(imf, CV_32FC1, 0.5f, 0.5f);
Imgproc.Sobel(imf, sobx, -1, 1, 0);
Imgproc.Sobel(imf, soby, -1, 0, 1);
sobx = sobx.mul(sobx);
soby = soby.mul(soby);
Mat abs_x = new Mat();
Core.convertScaleAbs(sobx,abs_x);
Mat abs_y = new Mat();
Core.convertScaleAbs(soby,abs_y);
Core.addWeighted(abs_x, 1, abs_y, 1, 0, grad_abs_val_approx);
sobx.release();
soby.release();
Mat filtered = new Mat();
Imgproc.GaussianBlur(grad_abs_val_approx, filtered, new Size(9, 9), 2, 2);
final MatOfDouble mean = new MatOfDouble();
final MatOfDouble stdev = new MatOfDouble();
Core.meanStdDev(filtered, mean, stdev);
Mat thresholded = new Mat();
Imgproc.threshold(filtered, thresholded, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_TOZERO);
/*
Mat thresholded_bin = new Mat();
Imgproc.threshold(filtered, thresholded_bin, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_BINARY);
Mat converted = new Mat();
thresholded_bin.convertTo(converted, CV_8UC1);
*/
return thresholded;
}
Here is the image that i have got after running the code above
Image after using #Nejc solution
1) Why my translated code does not output the same image like #Nejc ?
The same code applied to same image should produce the same output ?
2) did i miss something when translating ?
3) For my understanding, why did we multiply the image by itself in this instruction sobx = sobx.mul(sobx); ?
I managed to obtain a pretty nice image of the edge by computing an approximation of the absolute value of gradient of the input image.
EDIT: Before I started working, I resized the input image to 5x smaller size. Click here to see it!. If you use my code on that image, the results will be good. If you want to make my code work well with the image of the original size, then either:
multiply Gaussian kernel sizes and sigmas by 5, or
downsample the image by factor 5, execute the algorithm and then upsample the result by factor 5 (this should work much faster than the first option)
This is the result I got:
My procedure relies on two key features. The first is a conversion to appropriate color space. As Jeru Luke stated in his answer , the saturation channel in HSV color space is the good choice here. The second important thing is the estimation of absolute value of gradient. I used sobel operators and some arithmetics for that purpose. I can provide additional explanations if someone requests them.
This is the code I used to obtain the first image.
using namespace std;
using namespace cv;
Mat img_rgb = imread("letter.jpg");
Mat img_hsv;
cvtColor(img_rgb, img_hsv, CV_BGR2HSV);
vector<Mat> channels_hsv;
split(img_hsv, channels_hsv);
Mat channel_s = channels_hsv[1];
GaussianBlur(channel_s, channel_s, Size(9, 9), 2, 2);
Mat imf;
channel_s.convertTo(imf, CV_32FC1, 0.5f, 0.5f);
Mat sobx, soby;
Sobel(imf, sobx, -1, 1, 0);
Sobel(imf, soby, -1, 0, 1);
sobx = sobx.mul(sobx);
soby = soby.mul(soby);
Mat grad_abs_val_approx;
cv::pow(sobx + soby, 0.5, grad_abs_val_approx);
Mat filtered;
GaussianBlur(grad_abs_val_approx, filtered, Size(9, 9), 2, 2);
Scalar mean, stdev;
meanStdDev(filtered, mean, stdev);
Mat thresholded;
cv::threshold(filtered, thresholded, mean.val[0] + stdev.val[0], 1.0, CV_THRESH_TOZERO);
// I scale the image at this point so that it is displayed properly
imshow("image", thresholded/50);
And this is how I computed the second image:
Mat thresholded_bin;
cv::threshold(filtered, thresholded_bin, mean.val[0] + stdev.val[0], 1.0, CV_THRESH_BINARY);
Mat converted;
thresholded_bin.convertTo(converted, CV_8UC1);
vector<vector<Point>> contours;
findContours(converted, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
Mat contour_img = Mat::zeros(converted.size(), CV_8UC1);
drawContours(contour_img, contours, -1, 255);
imshow("contours", contour_img);
Thanks for yours comments and suggestion.
The code provided by #NEJC works perfectly and cover 80% of my use case.
Nevertheless, it does not works with similar case like this
case not solved by the current code
and i don't know why.
Perhaps someone have an idea/clue/solution ?
I continue to improve the code and try to find a more generic solution that can cover more case. I will post it if i ever i find.
In any case, below is the working code based on #NEJC solution and notes.
public static Mat process(Mat original){
Mat src = original.clone();
Mat hsvMat = new Mat();
Mat saturation = new Mat();
Mat sobx = new Mat();
Mat soby = new Mat();
Mat grad_abs_val_approx = new Mat();
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
List<Mat> hsv_channels = new ArrayList<Mat>(3);
Core.split(hsvMat, hsv_channels);
Mat hue = hsv_channels.get( 0 );
Mat sat = hsv_channels.get( 1 );
Mat val = hsv_channels.get( 2 );
Imgproc.GaussianBlur(sat, saturation, new Size(9, 9), 2, 2);
Mat imf = new Mat();
saturation.convertTo(imf, CV_32FC1, 0.5f, 0.5f);
Imgproc.Sobel(imf, sobx, -1, 1, 0);
Imgproc.Sobel(imf, soby, -1, 0, 1);
sobx = sobx.mul(sobx);
soby = soby.mul(soby);
Mat sumxy = new Mat();
Core.add(sobx,soby, sumxy);
Core.pow(sumxy, 0.5, grad_abs_val_approx);
sobx.release();
soby.release();
sumxy.release();;
Mat filtered = new Mat();
Imgproc.GaussianBlur(grad_abs_val_approx, filtered, new Size(9, 9), 2, 2);
final MatOfDouble mean = new MatOfDouble();
final MatOfDouble stdev = new MatOfDouble();
Core.meanStdDev(filtered, mean, stdev);
Mat thresholded = new Mat();
Imgproc.threshold(filtered, thresholded, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_TOZERO);
/*
Mat thresholded_bin = new Mat();
Imgproc.threshold(filtered, thresholded_bin, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_BINARY_INV);
Mat converted = new Mat();
thresholded_bin.convertTo(converted, CV_8UC1);
*/
Mat converted = new Mat();
thresholded.convertTo(converted, CV_8UC1);
return converted;
}

How to perform DoG in opencv with java API

i just want to know which opencv with java API perforems the Difference Of Gaussian DoG?
DOG is just, what the name says, the difference of 2 convolutions with an image.
let's just do it:
//
// grayscale:
//
Mat gray = new Mat();
Imgproc.cvtColor(ocv,gray, Imgproc.COLOR_BGR2GRAY);
//
// make 2 kernels:
//
Mat k1 = Imgproc.getGaussianKernel(5, 0.6);
Mat k2 = Imgproc.getGaussianKernel(5, 4.2);
//
// apply them on your image:
//
Mat f1 = new Mat();
Imgproc.filter2D(gray, f1, CvType.CV_32F, k1);
Mat f2 = new Mat();
Imgproc.filter2D(gray, f2, CvType.CV_32F, k2);
//
// difference:
//
Mat dog = new Mat();
Core.subtract(f1,f2,dog);
[edit:]
this is even a rare case of where you can swap consecutive convolution with different fitlers with applying the diff of the filters,
G(img,fa)-G(img,fb) == G(img, fa-fb)

Retrieving the original coordinates of a pixel taken from a warped Image

I have four corners extracted from a sourceImage:
src_vertices[0] = corners[upperLeft];
src_vertices[1] = corners[upperRight];
src_vertices[2] = corners[downLeft];
src_vertices[3] = corners[downRight];
These four corners are warped to destinationImage like that:
dst_vertices[0] = Point(0,0);
dst_vertices[1] = Point(width, 0);
dst_vertices[2] = Point(0, height);
dst_vertices[3] = Point(width, height);
Mat warpPerspectiveMatrix = getPerspectiveTransform(src_vertices, dst_vertices);
cv::Size size_d = Size(width, height);
cv::Mat DestinationImage(width,height,CV_8UC3);
warpPerspective(sourceImage, destinationImage, warpPerspectiveMatrix, size_d, INTER_LINEAR, BORDER_CONSTANT);
Now my question is:
I have a point p(x,y) taken from the destinationImage how can I retrieve the coordinates of this point in the original sourceImage
In other words I want to use warpPerspectiveMatrix to do the opposite work of getPerspectiveTransform
You want the inverse perspective transform. If your original transform is S->S', you want the transform matrix S'->S
Mat InversewarpPerspectiveMatrix = getPerspectiveTransform(dst_vertices, src_vertices);
Then you make a SPARSE matrix
Mat PerspectiveCoordinates containing the vector x,y.
Finally you want to call
PerspectiveTransform(PerspectiveCoordinates,OriginalCoordinates,InversewarpPerspectiveMatrix)

Resources