@lwx228 물었다 이미지를 만드는 공식을 만드는 방법. 끝까지 못갔어 (예쁜 애니메이션처럼 여기 )하지만 가까워졌습니다.
이미지를 로드하고 Canny 필터를 사용하여 밝음과 어두움 사이의 경계를 찾으십시오. 단일 폐쇄 루프에 가까울수록 다음 항목에 더 좋습니다. 아래의 불연속성은 나중에 아티팩트를 표시하지만 그렇게 나쁘지는 않습니다.
Original, left. Canny filtered, right.원본, 왼쪽. 캐니 필터링, 맞아.
//clear globals();// uncomment this if you change the picture 3 or 4 lines down to make it reload...
If( Try( Type( original ) != "Picture", 1 ), // set to 1 long enough to download once
original = Open(
//"https://i.stack.imgur.com/wtJoA.png", // this was an elephant...the loop closes nicely
// this fish does not close the loop...
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Malayan_fishes_%28PLATE_LXX%29_BHL46234933.jpg/800px-Malayan_fishes_%28PLATE_LXX%29_BHL46234933.jpg", // https://commons.wikimedia.org/wiki/File:Malayan_fishes_(PLATE_LXX)_BHL46234933.jpg
"png"
);
Beep();// comment out the clear globals to prevent re-loading the image over the internet each time
);
New Window( "Original Image", original );
bitmap = New Image( original );
bitmap << filter(
"canny", // TWEAK THESE for different images; you want a single loop for best results
largeThreshold( .5 )/*tracing begins when a large-value edge is found*/,
smallThreshold( .01 )/*tracing stops when the edge value gets too small*/,
radius( 2 )/*a blur-filter, use 0 for no blurring*/,
Repeat( 3 )/* number of repeats of a box-blur; 3 approximates a gaussian-blur filter */
);
New Window( "Original Edge Detection", bitmap );위의 코드에는 이미지를 포함하는 변수를 재설정하고 try()가 이미지가 없음을 감지할 때 다시 로드하게 하는 주석 처리된 clear globals()가 있습니다. 몇 줄 아래에서 코끼리 이미지로 전환할 수 있습니다. 경고음은 명확한 전역을 다시 설명하도록 상기시켜줍니다. 첫 번째 NewWindow()는 원본 이미지를 보여줍니다. 그런 다음 비트맵이 원본에서 복사되고 필터링됩니다. Canny 필터는 각 이미지에 대해 조정해야 하는 매개변수가 있는 가장자리 찾기 추적기입니다. 위의 두 이미지는 이러한 설정으로 작동합니다(코끼리가 너무 쉽기 때문입니다.) 마지막 newwindow()는 윤곽선을 보여줍니다.
불행히도 개요는 비트맵에 있습니다. 다음 코드에는 연속 루프를 형성하는 x,y 좌표 목록이 필요합니다.
bitmap = bitmap << getpixels;
nr = N Rows( bitmap );
nc = N Cols( bitmap );
points = Loc( bitmap );
// see https://community.jmp.com/t5/JSL-Cookbook/Using-Loc-with-a-2D-Matrix/ta-p/195207
rowlocs = Floor( (points) / nc ) + 1; // nc is the number of columns in the 2D pixmat
collocs = Mod( (points), nc ) + 1; // rowlocs and collocs are 2D indexes (1-based)
// center and scale
rowlocs = rowlocs - Min( rowlocs );
rowlocs = rowlocs / Max( rowlocs );
rowlocs = rowlocs * 2 - 1;
collocs = collocs - Min( collocs );
collocs = collocs / Max( collocs );
collocs = collocs * 2 - 1;
위의 픽셀을 bitmap이라는 매트릭스로 가져오고 nr 및 nc의 행 및 열 수를 가져옵니다. 0이 아닌 픽셀을 찾으려면 loc() 함수를 사용하십시오. 검은색은 0이고 흰색은 필터링된 그림에서 1입니다. Loc은 작동하는 인덱스를 반환하지만 비트맵이 nr*nc의 한 열이라고 가정합니다. 개요를 추적하려면 x,y 인덱스를 사용하는 것이 좋습니다. rowlocs 및 collocs는 다음 공식으로 계산됩니다. 여기 . 계속해서 인덱스를 -1에서 1의 공간으로 확장합니다. 인덱스는 비트맵의 인덱스만 가리키기 때문에 인덱싱에 사용되지 않습니다. 그러나 그들은 이웃 테스트에 사용될 것입니다.
// extract the line-loop. crude, probably slow... first thing that comes to mind...
kdt = KDTable( rowlocs || collocs );
idxSort = J( N Rows( points ), 1, . );
idx = (kdt << K nearest rows( 1, [0, 0] ))[1][1];
idxSort[1] = idx;
For( i = 2, i <= N Rows( points ), i += 1,
kdt << removerows( idx ); // remove this point from the table so
idx = (kdt << K nearest rows( 1, idx ))[1][1]; // can find the next one
idxSort[i] = idx; // save a sorted list of the indexes
);
이 코드가 수행하는 작업을 더 빠르게 수행하는 다른 방법을 생각할 수 있습니다. 2D 좌표의 가장 가까운 이웃 테이블을 만들고 시작 좌표(다른 곳에 있을 수 있는 0,0에 가장 가까운 좌표)를 찾은 다음 찾은 각 항목을 제거하고 제거된 좌표를 사용하여 가장 가까운 이웃 체인을 기록합니다. 다음을 찾기 위해 조정하십시오. 필요에 따라 간격을 건너뜁니다.모든 것이 작동하는지 확인하기 위해 그래프를 만드십시오.
Tracing the Canny outlineCanny 개요 추적
// put the row/col in connected order
rowlocs = rowlocs[idxSort];
collocs = collocs[idxsort];
dt = As Table( rowlocs || collocs );
dt << setdirty( 0 );
dt << Graph Builder(
Size( 514, 444 ),
Show Control Panel( 0 ),
Variables( X( :Col2 ), Y( :Col1 ) ),
Elements( Points( X, Y, Legend( 4 ) ) ),
SendToReport(
Dispatch( {}, "Col1", ScaleBox, {Min( 1.1 ), Max( -1.1 )} ),
Dispatch( {}, "graph title", TextEditBox, {Set Text( "Original Trace" )} ),
Dispatch( {}, "Graph Builder", FrameBox, {Marker Size( 0 )} )
)
);위의 처음 두 문은 이전에 저장된 인덱스를 사용하여 행 및 열 인덱스를 순서대로 정렬합니다. AsTable은 행 인덱스와 열 인덱스를 연결하여 만든 2열 행렬에서 데이터 테이블을 만듭니다. GraphBuilder가 그림을 만듭니다. 표를 사용하여 점을 순서대로 선택하고 그래프의 루프가 위에서 아래가 아니라 처음부터 끝까지 그려지는지 확인합니다.
나머지는 내가 잘 이해하지 못하는 공식과 많은 displaybox 작업으로 구성됩니다.
// run the fft
{colx, coly} = FFT( {collocs} );
{rowx, rowy} = FFT( {rowlocs} );
//dt=astable(colx||rowx);dt<<SETDIRTY></SETDIRTY>두 개의 fft() 호출이 있습니다. 하나는 X축에 대한 것이고 다른 하나는 Y축에 대한 것입니다. rebuild() 함수는 graphbox() line() 함수에서 사용하는 한 쌍의 행렬을 다시 빌드합니다. nInterp 슬라이더는 FFT 데이터에서 얼마나 많은 포인트를 다시 보간할지 제어하고 nKeep 슬라이더는 보간된 각 포인트에 대해 얼마나 많은 fft 데이터 빈을 유지할지 결정합니다.
Lots of interpolated points make it smooth, but there is not enough data보간점이 많아 원활하지만 데이터가 충분하지 않음
Plenty of data, but not enough interpolated points데이터는 많지만 보간 포인트가 충분하지 않음
Seems about right, enough data and enough interpolation옳고 충분한 데이터와 충분한 보간법에 대해 보입니다.
재미있었어요! 누군가 FFT에 더 많은 글을 게시하고 멋진 원을 얻고 싶다면 좋을 것입니다!