When using gocv package it is possible, for example, to perform template matching of a pattern within an image. The package also provide the MinMaxLoc function to retrieve locations of minimums and maximums within the matrix.
However, in below python example, the writer uses numpy.Where to threshold the matrix and get locations of multiple maximums. The python zip function is used to glue values together so they are like a slice [][2]int, the inner slice being xs and ys of the matches found.
The syntax loc[::-1] reverses the array.
The star operator in zip(*loc..) is being used to unpack the slices given to zip.
https://docs.opencv.org/master/d4/dc6/tutorial_py_template_matching.html
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img_rgb = cv.imread('mario.png')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('mario_coin.png',0)
w, h = template.shape[::-1]
res = cv.matchTemplate(img_gray,template,cv.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where( res >= threshold)
for pt in zip(*loc[::-1]):
cv.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)
cv.imwrite('res.png',img_rgb)
How do I implement the same np.where algorithm in Go to get the multiple locations after the threshold is applied?
OpenCV has a built-in (semi-)equivalent function to np.where(), which is findNonZero(). As implied by the name, it finds the non-zero elements in an image, which is what np.where() does when called with a single argument, as the numpy docs state.
And this is available in the golang bindings as well. From the gocv docs on FindNonZero:
func FindNonZero(src Mat, idx *Mat)
FindNonZero returns the list of locations of non-zero pixels.
For further details, please see: https://docs.opencv.org/master/d2/de8/group__core__array.html#gaed7df59a3539b4cc0fe5c9c8d7586190
Note: np.where() returns indexes in array order, that is, (row, col) or (i, j) which is opposite to typical image indexing (x, y). That is why loc is reversed in Python. When using findNonZero() you won't need to do that, since OpenCV always uses (x, y) for points.
For anyone coming across this I hope a full example keeps you from spending days hitting your head against the wall and reading the same google results over and over until something clicks.
package main
import (
"fmt"
"image"
"image/color"
"os"
"gocv.io/x/gocv"
)
func OpenImage(path string) (image.Image, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
img, _, err := image.Decode(f)
return img, err
}
func main() {
src := gocv.IMRead("haystack.png", gocv.IMReadGrayScale)
tgt := gocv.IMRead("needle.png", gocv.IMReadGrayScale)
if src.Empty() {
fmt.Printf("failed to read image")
os.Exit(1)
}
if tgt.Empty() {
fmt.Printf("failed to read image")
os.Exit(1)
}
// Get image size
tgtImg, _ := tgt.ToImage()
iX, iY := tgtImg.Bounds().Size().X, tgtImg.Bounds().Size().Y
// Perform a match template operation
res := gocv.NewMat()
gocv.MatchTemplate(src, tgt, &res, gocv.TmSqdiffNormed, gocv.NewMat())
// Set a thresh hold. Using the `gocv.TmSqdiffNormed` confidence levels are
// reversed. Meaning the lowest value is actually the greatest confidence.
// So here I perform an Inverse Binary Threshold setting all values
// above 0.16 to 1.
thresh := gocv.NewMat()
gocv.Threshold(res, &thresh, 0.16, 1.0, gocv.ThresholdBinaryInv)
// Filter out all the non-zero values.
gocv.FindNonZero(thresh, &res)
// FindNonZero returns a list or vector of locations in the form of a gocv.Mat when using gocv.
// There may be a better way to do this, but I iterate through each found location getting the int vector in value
// at each row. I have to convert the returned int32 values into ints. Then draw a rectangle around each point.
//
// The result of get res.GetVeciAt(i, 0) is just a slice of x, y integers so each value can be accessed by
// using slice/array syntax.
for i := 0; i < res.Rows(); i++ {
x, y := res.GetVeciAt(i, 0)[0], res.GetVeciAt(i, 0)[1]
xi, yi := int(x), int(y)
gocv.Rectangle(&src, image.Rect(xi, yi, xi+iX, yi+iY), color.RGBA{0, 0, 0, 1}, 2)
}
w := gocv.NewWindow("Test")
w.IMShow(src)
if w.WaitKey(0) > 1 {
os.Exit(0)
}
}
Related
I am trying to find contours in grayscale images. My code is based on persistent homology and it is irrelevant here. But the contours I am picking up are coming with some tails.
So, I need to post-process these contours by removing the tails. I came up with a method to do that by flood filling the outside of the contour then removing the contour pixels that is not a boundary of the original loop I am trying to capture.
#####################################
# Post-process the cycles(Get rid of the tails)
#####################################
def fill_mask(self,data, start_coords, fill_value):
"""
Flood fill algorithm
Parameters
----------
data : (M, N) ndarray of uint8 type
Image with flood to be filled. Modified inplace.
start_coords : tuple
Length-2 tuple of ints defining (row, col) start coordinates.
fill_value : int
Value the flooded area will take after the fill.
Returns
-------
None, ``data`` is modified inplace.
"""
xsize, ysize = data.shape
orig_value = data[start_coords[0], start_coords[1]]
stack = set(((start_coords[0], start_coords[1]),))
if fill_value == orig_value:
raise ValueError("Filling region with same value "
"already present is unsupported. "
"Did you already fill this region?")
while stack:
x, y = stack.pop()
if data[x, y] == orig_value:
data[x, y] = fill_value
if x > 0:
stack.add((x - 1, y))
if x < (xsize - 1):
stack.add((x + 1, y))
if y > 0:
stack.add((x, y - 1))
if y < (ysize - 1):
stack.add((x, y + 1))
def remove_non_boundary(self,good_cycles):
#Helper function to remove tails from the contours
#if plot=True, it allows to see individual cycles as a matrix
#we use fill_mask to floodfill everywhere on the mask except the hole bounded by the loop.
#we start floodfilling from (0,0), so we need to use 2 pixels bigger image along left-right and up-down just in case there is a
#cycle whose coordinates go through (0,0)
#"input:cycles with tails to be removed"
#"Returns:coordinates of the clean cycles and the correponding matrix representation 1-pixel bigger than the original image"
#"from all four directions"
good_cycles_cleaned=[]
masks=[]
for k in range(len(good_cycles)):
mask=self.overlay(good_cycles[[k]])
self.fill_mask(mask[:,:,0],(0,0),0.5)
for i in self.cycle2pixel(good_cycles[k]):
if mask[i[0]+2,i[1]+1,0]==0:pass#break
elif mask[i[0]+1,i[1]+2,0]==0:pass#break
elif mask[i[0],i[1]+1,0]==0:pass#break
elif mask[i[0]+1,i[1],0]==0:pass#break
else: mask[i[0]+1,i[1]+1,0]=0.5
if mask[:,:,0].all()==0.5: good_cycles_cleaned.append(good_cycles[k]);mask=self.overlay(good_cycles[[k]]);masks.append(mask)
else: self.fill_mask(mask[:,:,0],(0,0),0); cycle=np.transpose(np.nonzero(mask[:,:,0])) ; good_cycles_cleaned.append(cycle) ; masks.append(mask)
pixels = np.vstack([cycle for cycle in good_cycles_cleaned])
mask_good_clean = np.zeros((self.image.shape[0]+2, self.image.shape[1]+2, 4))
mask_good_clean[pixels[:,0]+1, pixels[:,1]+1,0] = 1
mask_good_clean[pixels[:,0]+1, pixels[:,1]+1,3] = 1
return good_cycles_cleaned,mask_good_clean,masks
However, this method takes ages and I need a quicker method. I've tried to use almost everything in opencv but nothing gives me exactly what I want. cv2.approxPolyDP misdraws the contours and cv2.convexHull traces the tails and gives me a bigger contour than I need. Should be an easy task but what am I missing?
I'm trying to implement a linear regression with gradient descent as explained in this article (https://towardsdatascience.com/linear-regression-using-gradient-descent-97a6c8700931).
I've followed to the letter the implementation, yet my results overflow after a few iterations.
I'm trying to get this result approximately: y = -0.02x + 8499.6.
The code:
package main
import (
"encoding/csv"
"fmt"
"strconv"
"strings"
)
const (
iterations = 1000
learningRate = 0.0001
)
func computePrice(m, x, c float64) float64 {
return m * x + c
}
func computeThetas(data [][]float64, m, c float64) (float64, float64) {
N := float64(len(data))
dm, dc := 0.0, 0.0
for _, dataField := range data {
x := dataField[0]
y := dataField[1]
yPred := computePrice(m, x, c)
dm += (y - yPred) * x
dc += y - yPred
}
dm *= -2/N
dc *= -2/N
return m - learningRate * dm, c - learningRate * dc
}
func main() {
data := readXY()
m, c := 0.0, 0.0
for k := 0; k < iterations; k++ {
m, c = computeThetas(data, m, c)
}
fmt.Printf("%.4fx + %.4f\n", m, c)
}
func readXY() ([][]float64) {
file := strings.NewReader(data)
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
panic(err)
}
records = records[1:]
size := len(records)
data := make([][]float64, size)
for i, v := range records {
val1, err := strconv.ParseFloat(v[0], 64)
if err != nil {
panic(err)
}
val2, err := strconv.ParseFloat(v[1], 64)
if err != nil {
panic(err)
}
data[i] = []float64{val1, val2}
}
return data
}
var data = `km,price
240000,3650
139800,3800
150500,4400
185530,4450
176000,5250
114800,5350
166800,5800
89000,5990
144500,5999
84000,6200
82029,6390
63060,6390
74000,6600
97500,6800
67000,6800
76025,6900
48235,6900
93000,6990
60949,7490
65674,7555
54000,7990
68500,7990
22899,7990
61789,8290`
And here it can be worked on in the GO playground:
https://play.golang.org/p/2CdNbk9_WeY
What do I need to fix to get the correct result ?
Why would a formula work on one data set and not another one?
In addition to sascha's remarks, here's another way to look at problems of this application of gradient descent: The algorithm offers no guarantee that an iteration yields a better result than the previous, so it doesn't necessarily converge to a result, because:
The gradients dm and dc in axes m and c are handled indepently from each other; m is updated in the descending direction according to dm, and c at the same time is updated in the descending direction according to dc — but, with certain curved surfaces z = f(m, c), the gradient in a direction between axes m and c can have the opposite sign compared to m and c on their own, so, while updating any one of m or c would converge, updating both moves away from the optimum.
However, more likely the failure reason in this case of linear regression to a point cloud is the entirely arbitrary magnitude of the update to m and c, determined by the product of an obscure learning rate and the gradient. It is quite possible that such an update oversteps a minimum for the target function, even that this is repeated with higher amplitude in each iteration.
I worked last year with OpenCV and Python. Today I wanted to try OpenCV using Golang with the GOCV package. I just wanted a simple Python example () to evalute but in Golang. I used even the same parameters (except the hiThresh and finalThreshold, i used the default values). Somehow I cannot get it working with GOCV, he only finds one centered result.
Here is my code:
package main
import (
"encoding/json"
"fmt"
"image"
"image/color"
"gocv.io/x/gocv"
)
func main() {
// define default hog descriptor
hog := gocv.NewHOGDescriptor()
defer hog.Close()
hog.SetSVMDetector(gocv.HOGDefaultPeopleDetector())
// color for the rect when faces detected
blue := color.RGBA{0, 0, 255, 0}
// read image
img := gocv.IMRead("images/person_010.bmp", 0)
//resize image
fact := float64(400) / float64(img.Cols())
newY := float64(img.Rows()) * fact
gocv.Resize(img, img, image.Point{X: 400, Y: int(newY)}, 0, 0, 1)
// detect people in image
rects := hog.DetectMultiScaleWithParams(img, 0, image.Point{X: 8, Y: 8}, image.Point{X: 16, Y: 16}, 1.05, 2, false)
// print found points
printStruct(rects)
// draw a rectangle around each face on the original image,
// along with text identifing as "Human"
for _, r := range rects {
gocv.Rectangle(img, r, blue, 3)
size := gocv.GetTextSize("Human", gocv.FontHersheyPlain, 1.2, 2)
pt := image.Pt(r.Min.X+(r.Min.X/2)-(size.X/2), r.Min.Y-2)
gocv.PutText(img, "Human", pt, gocv.FontHersheyPlain, 1.2, blue, 2)
}
if ok := gocv.IMWrite("loool.jpg", img); !ok {
fmt.Println("Error")
}
}
func printStruct(i interface{}) {
b, err := json.Marshal(i)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(b))
}
Here is the input image:
And here is the result:
Actually, I've just run the code you posted with the image you provided—and I've got another resulting image:
I'm running:
gocv version: 0.10.0
opencv lib version: 3.4.1
I started playing with gocv. I'm trying to figure out a simple thing: how to cut out an object from an image which has a background of certain colour. In this case the object is pizza and background colour is blue.
I'm using InRange function (inRange in OpenCV) to define the upper and lower threshold for blue colour to create a mask and then CopyToWithMask function (copyTo in OpenCV) to apply the mask on the original image. I expect the result to be the blue background with the pizza cut out of it.
The code is very simple:
package main
import (
"fmt"
"os"
"gocv.io/x/gocv"
)
func main() {
imgPath := "pizza.png"
// read in an image from filesystem
img := gocv.IMRead(imgPath, gocv.IMReadColor)
if img.Empty() {
fmt.Printf("Could not read image %s\n", imgPath)
os.Exit(1)
}
// Create a copy of an image
hsvImg := img.Clone()
// Convert BGR to HSV image
gocv.CvtColor(img, hsvImg, gocv.ColorBGRToHSV)
lowerBound := gocv.NewMatFromScalar(gocv.NewScalar(110.0, 100.0, 100.0, 0.0), gocv.MatTypeCV8U)
upperBound := gocv.NewMatFromScalar(gocv.NewScalar(130.0, 255.0, 255.0, 0.0), gocv.MatTypeCV8U)
// Blue mask
mask := gocv.NewMat()
gocv.InRange(hsvImg, lowerBound, upperBound, mask)
// maskedImg: output array that has the same size and type as the input arrays.
maskedImg := gocv.NewMatWithSize(hsvImg.Rows(), hsvImg.Cols(), gocv.MatTypeCV8U)
hsvImg.CopyToWithMask(maskedImg, mask)
// save the masked image
newImg := gocv.NewMat()
// Convert back to BGR before saving
gocv.CvtColor(maskedImg, newImg, gocv.ColorHSVToBGR)
gocv.IMWrite("no_pizza.jpeg", newImg)
}
However the resulting image is basically almost completely black except for a slight hint of a pizza edge:
As for the chosen upper and lower bound of blue colours, I followed the guide mentioned in the official documentation:
blue = np.uint8([[[255, 0, 0]]])
hsv_blue = cv2.cvtColor(blue, cv2.COLOR_BGR2HSV)
print(hsv_blue)
[[[120 255 255]]]
Now you take [H-10, 100,100] and [H+10, 255, 255] as lower bound and
upper bound respectively.
I'm sure I'm missing something fundamental, but can't figure out what it is.
So I spent quite some time on this to figure out what I'm missing and finally found the answer to my question in case anyone is interested. It's now clearer to me now why this question hasn't been answered as the solution to it is rather crazy due to gocv API.
Here is the code that I had to write to get the result I'm after:
package main
import (
"fmt"
"os"
"path/filepath"
"gocv.io/x/gocv"
)
func main() {
// read image
pizzaPath := filepath.Join("pizza.png")
pizza := gocv.IMRead(pizzaPath, gocv.IMReadColor)
if pizza.Empty() {
fmt.Printf("Failed to read image: %s\n", pizzaPath)
os.Exit(1)
}
// Convert BGR to HSV image (dont modify the original)
hsvPizza := gocv.NewMat()
gocv.CvtColor(pizza, &hsvPizza, gocv.ColorBGRToHSV)
pizzaChannels, pizzaRows, pizzaCols := hsvPizza.Channels(), hsvPizza.Rows(), hsvPizza.Cols()
// define HSV color upper and lower bound ranges
lower := gocv.NewMatFromScalar(gocv.NewScalar(110.0, 50.0, 50.0, 0.0), gocv.MatTypeCV8UC3)
upper := gocv.NewMatFromScalar(gocv.NewScalar(130.0, 255.0, 255.0, 0.0), gocv.MatTypeCV8UC3)
// split HSV lower bounds into H, S, V channels
lowerChans := gocv.Split(lower)
lowerMask := gocv.NewMatWithSize(pizzaRows, pizzaCols, gocv.MatTypeCV8UC3)
lowerMaskChans := gocv.Split(lowerMask)
// split HSV lower bounds into H, S, V channels
upperChans := gocv.Split(upper)
upperMask := gocv.NewMatWithSize(pizzaRows, pizzaCols, gocv.MatTypeCV8UC3)
upperMaskChans := gocv.Split(upperMask)
// copy HSV values to upper and lower masks
for c := 0; c < pizzaChannels; c++ {
for i := 0; i < pizzaRows; i++ {
for j := 0; j < pizzaCols; j++ {
lowerMaskChans[c].SetUCharAt(i, j, lowerChans[c].GetUCharAt(0, 0))
upperMaskChans[c].SetUCharAt(i, j, upperChans[c].GetUCharAt(0, 0))
}
}
}
gocv.Merge(lowerMaskChans, &lowerMask)
gocv.Merge(upperMaskChans, &upperMask)
// global mask
mask := gocv.NewMat()
gocv.InRange(hsvPizza, lowerMask, upperMask, &mask)
// cut out pizza mask
pizzaMask := gocv.NewMat()
gocv.Merge([]gocv.Mat{mask, mask, mask}, &pizzaMask)
// cut out the pizza and convert back to BGR
gocv.BitwiseAnd(hsvPizza, pizzaMask, &hsvPizza)
gocv.CvtColor(hsvPizza, &hsvPizza, gocv.ColorHSVToBGR)
// write image to filesystem
outPizza := "no_pizza.jpeg"
if ok := gocv.IMWrite(outPizza, hsvPizza); !ok {
fmt.Printf("Failed to write image: %s\n", outPizza)
os.Exit(1)
}
// write pizza mask to filesystem
outPizzaMask := "no_pizza_mask.jpeg"
if ok := gocv.IMWrite(outPizzaMask, mask); !ok {
fmt.Printf("Failed to write image: %s\n", outPizza)
os.Exit(1)
}
}
This code produces the result I was after:
I'm also going to add another picture that shows the im
Now, let's get to code. gocv API function InRange() does not accept Scalar like OpenCV does so you have to do all that crazy image channel splitting and merging dance since you need to pass in Mats as lower and upper bounds to InRange(); these Mat masks have to have the exact number of channels as the image on which you run InRange().
This brings up another important point: when allocating the Scalars in gocv for this task, I originally used gocv.MatTypeCV8U type which represents single channel color - not enough for HSV image which has three channels -- this is fixed by using gocv.MatTypeCV8UC3 type.
If I it were possible pass in gocv.Scalars into gocv.InRange() a lot of the boiler plate code would disappear; so would all the unnecessary gocv.NewMat() allocations for splitting and reassembling the channels which are required to create lower and upper bounds channels.
inRange with the given range runs perfectly for me. I'm not familiar with Go, but here's my python code:
import numpy as py
import cv2
img = cv2.imread("pizza.png")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, (110, 100, 100), (130, 255, 255))
inv_mask = cv2.bitwise_not(mask)
pizza = cv2.bitwise_and(img, img, mask=inv_mask)
cv2.imshow("img", img)
cv2.imshow("mask", mask)
cv2.imshow("pizza", pizza)
cv2.imshow("inv mask", inv_mask)
cv2.waitKey()
A few of notes here:
inRange returns the blue background so we need to invert it to reveal the object's mask (if you need the object).
You don't need to apply mask on hsvImg and convert to BGR, you can apply mask directly on the original image (which is BGR already).
Python does not have CopyToWithMask so I use the equivalent bitwise_and. You may check this function in Go, but I suspect there would be no differences.
Here is what I did with Python because I don't know Go...
Let me explain first.
(1) Image has been turned to gray.
(2) Applied Canny Edge
(3 - 4) Created kernel and used it to do Dilate and Close operations
(5) Found contours
(6) Created and applied mask
(7) Cropped and saved the region
Here is the code:
import cv2
import numpy as np
image = cv2.imread("image.png")
copy = image.copy()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imshow('Gray', gray)
cv2.waitKey(0)
edged = cv2.Canny(gray, 10, 250)
cv2.imshow('Edged', edged)
cv2.waitKey(0)
kernel = np.ones((5, 5), np.uint8)
dilation = cv2.dilate(edged, kernel, iterations=1)
cv2.imshow('Dilation', dilation)
cv2.waitKey(0)
closing = cv2.morphologyEx(dilation, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Closing', closing)
cv2.waitKey(0)
# if using OpenCV 4, remove image variable from below
image, cnts, hiers = cv2.findContours(closing, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cont = cv2.drawContours(copy, cnts, -1, (0, 0, 0), 1, cv2.LINE_AA)
cv2.imshow('Contours', cont)
cv2.waitKey(0)
mask = np.zeros(cont.shape[:2], dtype="uint8") * 255
# Draw the contours on the mask
cv2.drawContours(mask, cnts, -1, (255, 255, 255), -1)
# remove the contours from the image and show the resulting images
img = cv2.bitwise_and(cont, cont, mask=mask)
cv2.imshow("Mask", img)
cv2.waitKey(0)
for c in cnts:
x, y, w, h = cv2.boundingRect(c)
if w > 50 and h > 130:
new_img = img[y:y + h, x:x + w]
cv2.imwrite('Cropped.png', new_img)
cv2.imshow("Cropped", new_img)
cv2.waitKey(0)
Hope will help more than one user.
// Fourier transform of Image<Bgr,byte> orig object.
// output is matrix<float> with 2 channels.
private Matrix<float> fourier()
{
Image<Gray, float> image = orig.Convert<Gray, float>();
IntPtr complexImage = CvInvoke.cvCreateImage(image.Size,Emgu.CV.CvEnum.IPL_DEPTH.IPL_DEPTH_32F, 2);
CvInvoke.cvSetZero(complexImage); // Initialize all elements to Zero
CvInvoke.cvSetImageCOI(complexImage, 1);
CvInvoke.cvCopy(image, complexImage, IntPtr.Zero);
CvInvoke.cvSetImageCOI(complexImage, 0);
Matrix<float> dft = new Matrix<float>(image.Rows, image.Cols, 2);
CvInvoke.cvDFT(complexImage, dft, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, 0);
//The Real part of the Fourier Transform
Matrix<float> outReal = new Matrix<float>(image.Size);
//The imaginary part of the Fourier Transform
Matrix<float> outIm = new Matrix<float>(image.Size);
CvInvoke.cvSplit(dft, outReal, outIm, IntPtr.Zero, IntPtr.Zero);
return dft;
}
// butterworth filter with Do frequency and order n.
// Filter is returned as matrix<float> with 2 channels.
private Matrix<float> make_butterworth(int Do, int n)
{
Matrix<float> ff = fourier();
Matrix<float> tmp = new Matrix<float>(ff.Rows, ff.Cols, 2);
Point center=new Point(tmp.Rows/2,tmp.Cols/2);
for (int i=0;i<orig.Rows;i++)
for (int j = 0; j < orig.Cols; j++)
{
int Duv= (int) (Math.Sqrt( Math.Pow(i-center.X,2) + Math.Pow(j-center.Y,2)));
tmp[i, j] = (float) (1 / (1 + Math.Pow((Duv / Do), 2 * n)));
}
return tmp;
}
// The click event which will trigger fourier() and
make_butterworth() takes Do and n order input from user
and applies filter on orig image.
private void lowPassToolStripMenuItem2_Click(object sender, EventArgs e)
{
dialog_input d1 = new dialog_input("Enter values of Do and order n seperated by space:\n");
d1.ShowDialog();
string[] s = d1.t.Split(new char[] { ' ', ',' });
int fc = Convert.ToInt32(s[0]);
int order = Convert.ToInt32(s[1]);
Matrix<float> filter= make_butterworth(fc, order); // 2 channels
Matrix<float> m = fourier(); // 2 channels
m._Mul(filter);
// filter * with fourier image.
CvInvoke.cvDFT(m,m,CV_DXT.CV_DXT_INVERSE, 0);
IntPtr cmplx = CvInvoke.cvCreateImage(m.Size, IPL_DEPTH.IPL_DEPTH_32F, 2);
CvInvoke.cvSetZero(cmplx);
CvInvoke.cvSetImageCOI(cmplx, 0);
CvInvoke.cvCopy(m, cmplx, IntPtr.Zero);
Bitmap bm = new Bitmap(m.Width, m.Height);
BitmapData bd = bm.LockBits(new Rectangle
(0, 0, bm.Width, bm.Height),
ImageLockMode.ReadWrite,
PixelFormat.Canonical);
bd.Scan0 = cmplx;
bm.UnlockBits(bd);
pictureBox2.Image = bm;
}
One thing i am taking fourier() as 2 channels instead of only taking real channel. i am not sure if i am wrong in this regard. Also thats why i had to take filter as 2 channels also where 2 channels are used to represent data of Gray and Alpha in both cases.
Problem occurs at bitmapdata object initialization due to pixelFormat.Canonical parameter. The result of multiply of fourier matrix and filter matrix is in matrix float. All i want to do is to take its IDFT and display the filtered image. Not sure about the PixelFormat. Any help would be great.
Read this chapter: opencv DFT tutorial , C code DFT and opencv DFT pythonit explains all you need to know about DFT in opencv.
About the types
1) Image is Real
2) DFT(Image) result in a complex image.
3) butterworth is a one channel matrix with the same size of image.
4) to filter, multiply each channel of DFT resulting image by butterworth filter. each channel must be multplied separated beacouse we have the real and complex parts of each pixel allocated in one channel as result of DFT.how filtering works
5) After filtering you will have a complex image
6) now you can apply the IDFT that have as result a real image. In opencv you maybe get as result a complex image, but the second channel are entirely zeros so you can discart.
Look here to:opencv C++ DFT