Project/mini-project

척추 세포 데이터의 정렬

fullfish 2022. 4. 4. 23:42

동기

생명과학쪽 공부하는 친구가 척추세포의 데이터를 보내주면 정렬해줄 수 있냐고 부탁받음

 

친구가 원하는것

척추 이미지 1
척추 이미지 2

척추 이미지 1처럼 척추를 찍은 사진이 있는데 빛나는 세포들의 좌표를 마우스로 찍어서 데이터를 저장함

그런데 항상 같은 위치, 같은 크기로 찍은게 아니라서 사진의 크기와 회전각도가 제각각이므로 맞춰주면 좋겠다.

 

해야할 것

1. csv확장자의 파일을 읽어서 해당 데이터를 배열화 함

2. V를 원점으로 삼기위해 모든 점을 평행이동 함

3. V-D의 거리를 구하고 이것을 이용해서 회전한 각도인 Θ를 구함

4. Θ를 이용해서 모든 데이터를 회전 시킴

5. 크기가 다른 여러 사진의 데이터를 중첩시키기위해서 L과 D의 위치를 고정시키고 그 비율에 맞춰서 데이터 이동

6. csv형식으로 내보내기

참고

완성 예시 (처음 3개의 값은 V, D, L 값/ X,Y 데이터만 결과로 필요)

원본 데이터
정렬 후 데이터

코드

const fileName = "S2 Br P5 epo";
const fs = require("fs");
const file_csv = fs.readFileSync(`./${fileName}.csv`); //readFile은 비동기 이건 동기
const string_csv = file_csv.toString();

const arr_json = csvToJSON(string_csv);
arr_json.pop(); // 마지막 빈값있길래 지움

// 처음 V값
let originV = [JSON.parse(arr_json[0].X), JSON.parse(arr_json[0].Y)]; // json.parse해서 string을 number로 바꿈

// V를 원점으로 삼기위한 값
let parallelTranslationForOrigin = originV.map((e) => -e);

// 모든 x,y에 대한 데이터
let allXY = [];
arr_json.forEach((e) => allXY.push([JSON.parse(e.X), JSON.parse(e.Y)]));

// 평행이동 시킨 x,y들의 데이터
let parallelTranslationAllXY = allXY.map((e) => [round(e[0] + parallelTranslationForOrigin[0], 1000), round(e[1] + parallelTranslationForOrigin[1], 1000)]);

//시계방향회전인지 여부 판별
let clockwise = true;
if (parallelTranslationAllXY[1][0] < 0) clockwise = false;

// 평행이동한 D값
let parallelTranslationD = [parallelTranslationAllXY[1][0], parallelTranslationAllXY[1][1]];

//V와 D사이거리 구하기
let lengthV_D = round(Math.sqrt(Math.pow(parallelTranslationD[0], 2) + Math.pow(parallelTranslationD[1], 2)), 1000);

//세타 구하기
let Θ = round(90 - (Math.asin(parallelTranslationD[1] / lengthV_D) * 180) / Math.PI, 1000);
if (!clockwise) Θ = -Θ;

//회전시킨 데이터 값들
let rotationTranslationAllXY = parallelTranslationAllXY.map((e) => rotationTranslation(e[0], e[1], Θ));

//기준으로 삼을 최대 Lx, Dy값
let ratioReferenceXY = [200, 530];

//각 데이터에 곱해줄 비율
let rateXY = [Math.abs(ratioReferenceXY[0] / rotationTranslationAllXY[2][0]), Math.abs(ratioReferenceXY[1] / rotationTranslationAllXY[1][1])];

//회전시킨 데이터에 비율따라 곱하기
let resultXY = rotationTranslationAllXY.map((e) => [Math.abs(round(e[0] * rateXY[0], 1)), round(e[1] * rateXY[1], 1)]);

//csv 형식으로 만들기
let result = `,X,Y\r`;
resultXY.forEach((e, index) => {
  result += `${index + 1},${e[0]},${e[1]}\r`;
});

//csv 파일 만들기
fs.writeFileSync(`chaged_${fileName}.csv`, result);

//csv를 JSON으로 바꿔주는 함수
function csvToJSON(csv_string) {
  const rows = csv_string.split("\r\n");
  const jsonArray = [];
  const header = rows[0].split(",");
  for (let i = 1; i < rows.length; i++) {
    let obj = {};
    let row = rows[i].split(",");
    for (let j = 0; j < header.length; j++) {
      obj[header[j]] = row[j];
    }
    jsonArray.push(obj);
  }
  return jsonArray;
}

//소수점 반올림 함수
function round(num, digit) {
  return Math.round(num * digit) / digit;
}

//데이터 회전 시키는 함수
function rotationTranslation(x, y, Θ) {
  let rotationTranslationX = x * Math.cos((Θ * Math.PI) / 180) - y * Math.sin((Θ * Math.PI) / 180);
  let rotationTranslationY = x * Math.sin((Θ * Math.PI) / 180) + y * Math.cos((Θ * Math.PI) / 180);
  return [round(rotationTranslationX, 1000), round(rotationTranslationY, 1000)];
}